Add texthook option

`-texthook print` prints text to console, and `-texthook copy`
automatically copies text to clipboard (for use with text hooker tools).

On Windows, copy mode can be toggled from the menu bar.
This commit is contained in:
kichikuou
2024-12-04 16:33:50 +09:00
parent 8da92f6856
commit 1e7a59f9cd
10 changed files with 191 additions and 46 deletions
+7
View File
@@ -118,6 +118,13 @@ tone generator emulation.
Specifies the MIDI device number to use. If not specified, the first available
device is used.
#### `-texthook` _mode_
Specifies the text hook mode. This option is used for capturing in-game text
for translation or other purposes. The available modes are:
- `none`: Disable text hooking.
- `print`: Print the captured text to the console.
- `copy`: Copy the captured text to the clipboard.
#### `-game` _game_id_
As System1-3 have slight variations depending on the game, `system3` uses the
fingerprint of the scenario file (ADISK.DAT) to identify the game being played.
+19
View File
@@ -77,6 +77,21 @@ std::pair<char*, char*> parse_keyval(char *line)
return { trim(line), trim(eq + 1) };
}
TexthookMode parse_texthook_mode(const char *s, int lineno = -1)
{
if (!strcasecmp(s, "none"))
return TEXTHOOK_NONE;
if (!strcasecmp(s, "print"))
return TEXTHOOK_PRINT;
if (!strcasecmp(s, "copy"))
return TEXTHOOK_COPY;
if (lineno == -1)
ERROR("Command line: Invalid texthook mode '%s'", s);
else
ERROR(INIFILENAME ":%d Invalid texthook mode '%s'", lineno, s);
return TEXTHOOK_NONE;
}
void init_string(std::string& s, Encoding* encoding, const char *dflt)
{
char *encoded = encoding->fromUtf8(s.empty() ? dflt : s.c_str());
@@ -120,6 +135,8 @@ Config::Config(int argc, char *argv[])
title = argv[++i];
else if (strcmp(argv[i], "-scanline") == 0)
scanline = true;
else if (strcmp(argv[i], "-texthook") == 0)
texthook_mode = parse_texthook_mode(argv[++i]);
}
}
@@ -178,6 +195,8 @@ void Config::load_ini()
title = val;
else if (!strcasecmp(key, "scanline"))
scanline = to_bool(val, lineno);
else if (!strcasecmp(key, "texthook"))
texthook_mode = parse_texthook_mode(val, lineno);
else
WARNING(INIFILENAME ":%d unknown key '%s'", lineno, key);
} else if (current_section == STRING) {
+2
View File
@@ -2,6 +2,7 @@
#define CONFIG_H_
#include <string>
#include "texthook.h"
class Encoding;
@@ -32,6 +33,7 @@ struct Config {
bool use_fm = false;
bool no_antialias = false;
bool scanline = false;
TexthookMode texthook_mode = TEXTHOOK_NONE;
private:
void load_ini();
+2
View File
@@ -97,6 +97,8 @@ int main(int argc, char *argv[])
EM_ASM( xsystem35.shell.windowSizeChanged(); );
#endif
texthook_set_mode(config.texthook_mode);
int exit_code = NACT_RESTART;
while (exit_code == NACT_RESTART) {
exit_code = g_nact->mainloop();
+129 -43
View File
@@ -1,9 +1,15 @@
#include <string>
#include <stdio.h>
#include <SDL.h>
#include "texthook.h"
#ifdef __EMSCRIPTEN__ // ----------------------------------------------
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
void texthook_set_mode(TexthookMode m) {
// Do nothing
}
void texthook_character(int page, int character) {
EM_ASM_({ xsystem35.texthook.message(String.fromCharCode($0), $1); },
character, page);
@@ -21,59 +27,139 @@ EM_JS(void, texthook_keywait, (void), {
xsystem35.texthook.keywait();
});
#elif defined(TEXTHOOK_PRINT) // --------------------------------------
#else
static int newlines = 0;
namespace {
class TextHookHandler {
public:
virtual void character(int page, int c) = 0;
virtual void newline(void) = 0;
virtual void nextpage(void) = 0;
virtual void keywait(void) = 0;
};
class None : public TextHookHandler {
public:
void character(int page, int c) override {}
void newline(void) override {}
void nextpage(void) override {}
void keywait(void) override {}
} none;
class Print : public TextHookHandler {
public:
void character(int page, int c) override {
if (newlines)
printf("%d:", page);
newlines = 0;
if (c <= 0x7f) {
putchar(c);
} else if (c <= 0x7ff) {
putchar(0xc0 | c >> 6);
putchar(0x80 | (c & 0x3f));
} else {
putchar(0xe0 | c >> 12);
putchar(0x80 | (c >> 6 & 0x3f));
putchar(0x80 | (c & 0x3f));
}
}
void newline(void) override {
if (newlines < 2) {
putchar('\n');
fflush(stdout);
newlines++;
}
}
void nextpage(void) override {
while (newlines < 2) {
putchar('\n');
fflush(stdout);
newlines++;
}
}
void keywait(void) override {
newline();
}
private:
int newlines = 0;
} print;
class Copy : public TextHookHandler {
public:
void character(int page, int c) override {
if (c <= 0x7f) {
buf += c;
} else if (c <= 0x7ff) {
buf += static_cast<char>(0xc0 | c >> 6);
buf += static_cast<char>(0x80 | (c & 0x3f));
} else {
buf += static_cast<char>(0xe0 | c >> 12);
buf += static_cast<char>(0x80 | (c >> 6 & 0x3f));
buf += static_cast<char>(0x80 | (c & 0x3f));
}
}
void newline(void) override {
buf += "\n";
}
void nextpage(void) override {
if (!buf.empty())
copy_to_clipboard();
}
void keywait(void) override {
if (!buf.empty())
copy_to_clipboard();
}
private:
std::string buf;
void copy_to_clipboard(void) {
SDL_SetClipboardText(buf.c_str());
buf.clear();
}
} copy;
TextHookHandler *handler = &none;
} // namespace
void texthook_set_mode(TexthookMode mode) {
switch (mode) {
case TEXTHOOK_NONE:
handler = &none;
break;
case TEXTHOOK_PRINT:
handler = &print;
break;
case TEXTHOOK_COPY:
handler = &copy;
break;
}
}
void texthook_character(int page, int c) {
if (newlines)
printf("%d:", page);
newlines = 0;
if (c <= 0x7f) {
putchar(c);
} else if (c <= 0x7ff) {
putchar(0xc0 | c >> 6);
putchar(0x80 | (c & 0x3f));
} else {
putchar(0xe0 | c >> 12);
putchar(0x80 | (c >> 6 & 0x3f));
putchar(0x80 | (c & 0x3f));
}
handler->character(page, c);
}
void texthook_newline(void) {
if (newlines < 2) {
putchar('\n');
fflush(stdout);
newlines++;
}
handler->newline();
}
void texthook_nextpage(void) {
while (newlines < 2) {
putchar('\n');
fflush(stdout);
newlines++;
}
handler->nextpage();
}
void texthook_keywait(void) {
texthook_newline();
handler->keywait();
}
#else // --------------------------------------------------------------
void texthook_character(int page, int c) {
}
void texthook_newline(void) {
}
void texthook_nextpage(void) {
}
void texthook_keywait(void) {
}
#endif // -------------------------------------------------------------
#endif
+7
View File
@@ -3,6 +3,13 @@
extern "C" {
enum TexthookMode {
TEXTHOOK_NONE,
TEXTHOOK_PRINT,
TEXTHOOK_COPY,
};
void texthook_set_mode(TexthookMode mode);
void texthook_character(int page, int character);
void texthook_newline(void);
void texthook_nextpage(void);
+15 -3
View File
@@ -8,12 +8,15 @@
#include "ags.h"
#include "mako.h"
#include "msgskip.h"
#include "texthook.h"
#include "resource.h"
extern SDL_Window* g_window;
namespace {
bool auto_copy_enabled = false;
HWND get_hwnd(SDL_Window* window) {
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
@@ -21,15 +24,18 @@ HWND get_hwnd(SDL_Window* window) {
return info.info.win.window;
}
void init_menu(bool mouse_move_enabled, bool scanline_enabled)
void init_menu(bool mouse_move_enabled, const Config& config)
{
HINSTANCE hinst = (HINSTANCE)GetModuleHandle(NULL);
HMENU hmenu = LoadMenu(hinst, MAKEINTRESOURCE(IDR_MENU1));
SetMenu(get_hwnd(g_window), hmenu);
if (mouse_move_enabled)
CheckMenuItem(hmenu, ID_OPTION_MOUSE_MOVE, MF_BYCOMMAND | MFS_CHECKED);
if (scanline_enabled)
if (config.scanline)
CheckMenuItem(hmenu, ID_SCANLINE, MF_BYCOMMAND | MFS_CHECKED);
auto_copy_enabled = config.texthook_mode == TEXTHOOK_COPY;
if (auto_copy_enabled)
CheckMenuItem(hmenu, ID_TEXT_AUTO_COPY, MF_BYCOMMAND | MFS_CHECKED);
}
void init_console(int sys_ver)
@@ -131,7 +137,7 @@ void NACT::text_dialog()
void NACT::platform_initialize()
{
init_menu(mouse_move_enabled, config.scanline);
init_menu(mouse_move_enabled, config);
init_console(sys_ver);
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
}
@@ -217,6 +223,12 @@ bool NACT::handle_platform_event(const SDL_Event& e)
CheckMenuItem(GetMenu(get_hwnd(g_window)), ID_TEXT_WAIT, MF_BYCOMMAND |
text_wait_enb ? MFS_CHECKED : MFS_UNCHECKED);
break;
case ID_TEXT_AUTO_COPY:
auto_copy_enabled = !auto_copy_enabled;
CheckMenuItem(GetMenu(get_hwnd(g_window)), ID_TEXT_AUTO_COPY, MF_BYCOMMAND |
auto_copy_enabled ? MFS_CHECKED : MFS_UNCHECKED);
texthook_set_mode(auto_copy_enabled ? TEXTHOOK_COPY : TEXTHOOK_NONE);
break;
}
break;
case MM_MCINOTIFY:
+1
View File
@@ -31,5 +31,6 @@
#define ID_OPTION_CD 40023
#define ID_TEXT_SKIP 40030
#define ID_TEXT_WAIT 40031
#define ID_TEXT_AUTO_COPY 40032
#endif // _RESOURCE_H_
+3
View File
@@ -112,6 +112,7 @@ BEGIN
BEGIN
MENUITEM "Skip", ID_TEXT_SKIP
MENUITEM "Wait", ID_TEXT_WAIT
MENUITEM "Auto Copy to Clipboard", ID_TEXT_AUTO_COPY
END
END
@@ -170,6 +171,7 @@ BEGIN
BEGIN
MENUITEM "スキップ", ID_TEXT_SKIP
MENUITEM "ウェイトあり", ID_TEXT_WAIT
MENUITEM "クリップボードに自動コピー", ID_TEXT_AUTO_COPY
END
END
@@ -228,6 +230,7 @@ BEGIN
BEGIN
MENUITEM "跳过", ID_TEXT_SKIP
MENUITEM "等待", ID_TEXT_WAIT
MENUITEM "Auto Copy to Clipboard", ID_TEXT_AUTO_COPY
END
END
+6
View File
@@ -35,6 +35,12 @@ encoding = shift_jis
; original Japanese game title will be used.
title = Rance 4.1 ~Save the Medicine Plant!~
; Text hook mode.
; none : No text hook (default)
; print : Print text to console
; copy : Automatically copy text to clipboard (for use with text hooker tools)
texthook = none
; The [string] section allows you to override Japanese text embedded in the
; game engine. The values below are the original Japanese text.
[string]