Skip to content

Commit

Permalink
tr2/screenshots: improve screenshots support
Browse files Browse the repository at this point in the history
Resolves #1766.
Resolves #1773.
  • Loading branch information
rr- committed Oct 29, 2024
1 parent 7ebb2a0 commit f8efbc9
Show file tree
Hide file tree
Showing 36 changed files with 376 additions and 402 deletions.
3 changes: 3 additions & 0 deletions docs/tr2/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
## [Unreleased](https://github.com/LostArtefacts/TRX/compare/tr2-0.5...develop) - ××××-××-××
- improved FMV mode appearance - removed black scanlines (#1729)
- improved FMV mode behavior - stopped switching screen resolutions (#1729)
- improved screenshots: now saved in the screenshots/ directory with level titles and timestamps as JPG or PNG, similar to TR1X (#1773)
- improved switch object names
- Switch Type 1 renamed to "Airlock Switch"
- Switch Type 2 renamed to "Small Switch"
- Switch Type 3 renamed to "Switch Button"
- Switch Type 4 renamed to "Lever/Switch"
- Switch Type 5 renamed to "Underwater Lever/Switch"
- fixed screenshots not working in windowed mode (#1766)
- fixed screenshots key not getting debounced (#1773)
- fixed `/give` not working with weapons (regression from 0.5)
- fixed the camera being cut off after using the gong hammer in Ice Palace (#1580)
- fixed the audio not being in sync when Lara strikes the gong in Ice Palace (#1725)
Expand Down
15 changes: 15 additions & 0 deletions src/libtrx/game/clock.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "game/clock.h"

#include <stdio.h>
#include <time.h>

size_t Clock_GetDateTime(char *const buffer, const size_t size)
{
time_t lt = time(0);
struct tm *tptr = localtime(&lt);

return snprintf(
buffer, size, "%04d%02d%02d_%02d%02d%02d", tptr->tm_year + 1900,
tptr->tm_mon + 1, tptr->tm_mday, tptr->tm_hour, tptr->tm_min,
tptr->tm_sec);
}
3 changes: 3 additions & 0 deletions src/libtrx/include/libtrx/game/clock.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#pragma once

#include <stddef.h>

size_t Clock_GetDateTime(char *buffer, size_t size);
extern double Clock_GetHighPrecisionCounter(void);
1 change: 1 addition & 0 deletions src/libtrx/include/libtrx/game/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@

extern bool Game_IsPlayable(void);
extern GAMEFLOW_LEVEL_TYPE Game_GetCurrentLevelType(void);
extern int32_t Game_GetCurrentLevelNum(void);
3 changes: 3 additions & 0 deletions src/libtrx/include/libtrx/game/output.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

extern bool Output_MakeScreenshot(const char *path);
3 changes: 3 additions & 0 deletions src/libtrx/include/libtrx/output.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

extern Output_MakeScreenshot(const char const path);
10 changes: 10 additions & 0 deletions src/libtrx/include/libtrx/screenshot.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once

#include <stdbool.h>

typedef enum {
SCREENSHOT_FORMAT_JPEG,
SCREENSHOT_FORMAT_PNG,
} SCREENSHOT_FORMAT;

bool Screenshot_Make(SCREENSHOT_FORMAT format);
2 changes: 2 additions & 0 deletions src/libtrx/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ sources = [
'event_manager.c',
'filesystem.c',
'game/backpack.c',
'game/clock.c',
'game/console/cmd/config.c',
'game/console/cmd/die.c',
'game/console/cmd/end_level.c',
Expand Down Expand Up @@ -122,6 +123,7 @@ sources = [
'json/json_write.c',
'log.c',
'memory.c',
'screenshot.c',
'strings/common.c',
'strings/fuzzy_match.c',
'vector.c',
Expand Down
141 changes: 141 additions & 0 deletions src/libtrx/screenshot.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#include "screenshot.h"

#include "filesystem.h"
#include "game/clock.h"
#include "game/game.h"
#include "game/gameflow/common.h"
#include "game/output.h"
#include "memory.h"

#include <stdio.h>
#include <string.h>

#define SCREENSHOTS_DIR "screenshots"

static char *M_GetScreenshotTitle(void);
static char *M_CleanScreenshotTitle(const char *source);
static char *M_GetScreenshotBaseName(void);
static const char *M_GetScreenshotFileExt(SCREENSHOT_FORMAT format);
static char *M_GetScreenshotPath(SCREENSHOT_FORMAT format);

static char *M_CleanScreenshotTitle(const char *const source)
{
// Sanitize screenshot title.
// - Replace spaces with underscores
// - Remove all non-alphanumeric characters
// - Merge consecutive underscores together
// - Remove leading underscores
// - Remove trailing underscores
char *result = Memory_Alloc(strlen(source) + 1);

bool last_was_underscore = false;
char *out = result;
for (size_t i = 0; i < strlen(source); i++) {
if (source[i] == ' ' || source[i] == '_') {
if (!last_was_underscore && out > result) {
*out++ = '_';
last_was_underscore = true;
}
} else if (((source[i] >= 'A' && source[i] <= 'Z')
|| (source[i] >= 'a' && source[i] <= 'z')
|| (source[i] >= '0' && source[i] <= '9'))) {
*out++ = source[i];
last_was_underscore = false;
}
}
*out++ = '\0';

// Strip trailing underscores
while (out[-1] == '_' && out >= result) {
out--;
}
*out = '\0';

return result;
}

static char *M_GetScreenshotTitle(void)
{
const int32_t level_num = Game_GetCurrentLevelNum();
if (level_num < 0) {
return Memory_DupStr("Intro");
}

const char *const level_title = Gameflow_GetLevelTitle(level_num);
if (level_title != NULL && strlen(level_title) > 0) {
char *clean_level_title = M_CleanScreenshotTitle(level_title);
if (clean_level_title != NULL && strlen(clean_level_title) > 0) {
return clean_level_title;
}
Memory_FreePointer(clean_level_title);
}

// If title totally invalid, name it based on level number
const char *const fmt = "Level_%d";
const size_t result_size = snprintf(NULL, 0, fmt, level_num) + 1;
char *result = Memory_Alloc(result_size);
snprintf(result, result_size, fmt, level_num);
return result;
}

static char *M_GetScreenshotBaseName(void)
{
char *screenshot_title = M_GetScreenshotTitle();

// Get timestamp
char date_time[30];
Clock_GetDateTime(date_time, 30);

// Full screenshot name
const char *const fmt = "%s_%s";
const size_t out_size =
snprintf(NULL, 0, fmt, date_time, screenshot_title) + 1;
char *out = Memory_Alloc(out_size);
snprintf(out, out_size, "%s_%s", date_time, screenshot_title);
return out;
}

static const char *M_GetScreenshotFileExt(const SCREENSHOT_FORMAT format)
{
switch (format) {
case SCREENSHOT_FORMAT_JPEG:
return "jpg";
case SCREENSHOT_FORMAT_PNG:
return "png";
default:
return "jpg";
}
}

static char *M_GetScreenshotPath(const SCREENSHOT_FORMAT format)
{
char *base_name = M_GetScreenshotBaseName();
const char *const ext = M_GetScreenshotFileExt(format);

char *full_path = Memory_Alloc(
strlen(SCREENSHOTS_DIR) + strlen(base_name) + strlen(ext) + 6);
sprintf(full_path, "%s/%s.%s", SCREENSHOTS_DIR, base_name, ext);
if (File_Exists(full_path)) {
for (int i = 2; i < 100; i++) {
sprintf(
full_path, "%s/%s_%d.%s", SCREENSHOTS_DIR, base_name, i, ext);
if (!File_Exists(full_path)) {
break;
}
}
}

Memory_FreePointer(&base_name);
return full_path;
}

bool Screenshot_Make(const SCREENSHOT_FORMAT format)
{
File_CreateDirectory(SCREENSHOTS_DIR);

char *full_path = M_GetScreenshotPath(format);
const bool result = Output_MakeScreenshot(full_path);
Memory_FreePointer(&full_path);

return result;
}
1 change: 1 addition & 0 deletions src/tr1/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <libtrx/config.h>
#include <libtrx/gfx/common.h>
#include <libtrx/screenshot.h>

#include <stdbool.h>
#include <stdint.h>
Expand Down
12 changes: 0 additions & 12 deletions src/tr1/game/clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <time.h>

static double M_GetElapsedUnit(CLOCK_TIMER *const timer, const double unit);
static bool M_CheckElapsedUnit(
Expand Down Expand Up @@ -76,17 +75,6 @@ double Clock_GetSpeedMultiplier(void)
}
}

void Clock_GetDateTime(char *date_time)
{
time_t lt = time(0);
struct tm *tptr = localtime(&lt);

sprintf(
date_time, "%04d%02d%02d_%02d%02d%02d", tptr->tm_year + 1900,
tptr->tm_mon + 1, tptr->tm_mday, tptr->tm_hour, tptr->tm_min,
tptr->tm_sec);
}

int32_t Clock_GetFrameAdvance(void)
{
return g_Config.rendering.fps == 30 ? 2 : 1;
Expand Down
4 changes: 2 additions & 2 deletions src/tr1/game/clock.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include <libtrx/game/clock.h>

#include <stdbool.h>
#include <stdint.h>

Expand Down Expand Up @@ -44,6 +46,4 @@ bool Clock_CheckElapsedMilliseconds(CLOCK_TIMER *timer, int32_t wait);
// by the turbo cheat multiplier.
bool Clock_CheckElapsedRawMilliseconds(CLOCK_TIMER *timer, int32_t how_often);

void Clock_GetDateTime(char *date_time);

int32_t Clock_GetFrameAdvance(void);
5 changes: 5 additions & 0 deletions src/tr1/game/game/game.c
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@ GAMEFLOW_LEVEL_TYPE Game_GetCurrentLevelType(void)
return g_GameInfo.current_level_type;
}

extern int32_t Game_GetCurrentLevelNum(void)
{
return g_CurrentLevel;
}

bool Game_IsPlayable(void)
{
if (g_GameInfo.current_level_type == GFL_TITLE
Expand Down
6 changes: 4 additions & 2 deletions src/tr1/game/output.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <libtrx/engine/image.h>
#include <libtrx/filesystem.h>
#include <libtrx/game/console/common.h>
#include <libtrx/gfx/context.h>
#include <libtrx/memory.h>
#include <libtrx/utils.h>

Expand Down Expand Up @@ -1364,9 +1365,10 @@ void Output_ApplyTint(float *r, float *g, float *b)
}
}

bool Output_MakeScreenshot(const char *path)
bool Output_MakeScreenshot(const char *const path)
{
return S_Output_MakeScreenshot(path);
GFX_Context_ScheduleScreenshot(path);
return true;
}

int Output_GetObjectBounds(const BOUNDS_16 *const bounds)
Expand Down
2 changes: 1 addition & 1 deletion src/tr1/game/phase/phase_photo_mode.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ static void M_End(void)
static PHASE_CONTROL M_Control(int32_t nframes)
{
if (m_Status == PS_ACTIVE) {
Shell_MakeScreenshot();
Screenshot_Make(g_Config.screenshot_format);
Sound_Effect(SFX_MENU_CHOOSE, NULL, SPM_ALWAYS);
m_Status = PS_COOLDOWN;
} else if (m_Status == PS_COOLDOWN) {
Expand Down
2 changes: 1 addition & 1 deletion src/tr1/game/savegame/savegame_bson.c
Original file line number Diff line number Diff line change
Expand Up @@ -1248,7 +1248,7 @@ static JSON_OBJECT *M_DumpCurrentMusic(void)
{
JSON_OBJECT *current_music_obj = JSON_ObjectNew();
JSON_ObjectAppendInt(
current_music_obj, "current_track", Music_GetCurrentTrack());
current_music_obj, "current_track", Music_GetCurrentPlayingTrack());
JSON_ObjectAppendDouble(
current_music_obj, "timestamp", Music_GetTimestamp());

Expand Down
Loading

0 comments on commit f8efbc9

Please sign in to comment.