From d57c8cc9f42a3b2ba56ef911cc8c2595921ec4d8 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sat, 12 Jul 2025 15:18:52 +0900 Subject: [PATCH] Dungeons & Dolls: Call global variable destructors on exit Early versions of System4 (Sys42VM.dll < 3.0) execute destructors for global variables on exit. Dungeons & Dolls saves its game state in the destructors of global variables (~CDataDoll(), ~CDataItem(), ~CDataMain()), so the game was not saved in xsystem4 before this. This behavior has some quirks: * Destructors for variables on the call stack are not called on exit. * Destructors are called in the reverse order of the global variable declarations. * Inside a destructor, it is possible to access another global variable whose destructor has already been called (!). Since this is a problematic behavior that was removed in later versions of Sys42VM, let's add minimal support for it as a game-specific hack. --- include/xsystem4.h | 1 + src/hacks.c | 3 +++ src/vm.c | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/include/xsystem4.h b/include/xsystem4.h index 3bdf14f..f81f71f 100644 --- a/include/xsystem4.h +++ b/include/xsystem4.h @@ -91,5 +91,6 @@ extern bool game_rance02_mg; extern bool game_rance6_mg; extern bool game_rance7_mg; extern bool game_rance8; +extern bool game_dungeons_and_dolls; #endif /* XSYSTEM4_H */ diff --git a/src/hacks.c b/src/hacks.c index 69c6bff..ca541f0 100644 --- a/src/hacks.c +++ b/src/hacks.c @@ -37,6 +37,7 @@ bool game_rance02_mg = false; bool game_rance6_mg = false; bool game_rance7_mg = false; bool game_rance8 = false; +bool game_dungeons_and_dolls = false; static void write_instruction0(struct buffer *out, enum opcode op) { @@ -205,6 +206,8 @@ void apply_game_specific_hacks(struct ain *ain) apply_rance7_hacks(ain); } else if (!strcmp(game_name, "ランス・クエスト") || !strcmp(game_name, "Rance Quest")) { game_rance8 = true; + } else if (!strcmp(game_name, "DUNGEONS&DOLLS")) { + game_dungeons_and_dolls = true; } free(game_name); } diff --git a/src/vm.c b/src/vm.c index 51d0b99..4b4a6f8 100644 --- a/src/vm.c +++ b/src/vm.c @@ -2319,8 +2319,28 @@ static void vm_execute(void) } } +static void call_global_destructors(void) +{ + if (heap_size <= 0 || heap[0].ref <= 0) + return; + struct page *global_page = heap_get_page(0); + // Call global variable destructors, but do not unref them because the + // destructors may reference other global variables. + for (int i = global_page->nr_vars - 1; i >= 0; i--) { + if (variable_type(global_page, i, NULL, NULL) != AIN_STRUCT) + continue; + int slot = global_page->values[i].i; + delete_struct(heap_get_page(slot)->index, slot); + } +} + static void vm_free(void) { + if (game_dungeons_and_dolls) { + // Dungeons & Dolls saves the game state in destructors of global variables + call_global_destructors(); + } + // call library exit routines exit_libraries(); // flush call stack