[Decompilation] [th03/th04/th05] Cutscenes: Main script parsing loop

Part of P0225, funded by Enderwolf, Blue Bolt, 32th System, and Yanga.
This commit is contained in:
nmlgc 2022-11-17 04:40:43 +01:00
parent 8118e61c77
commit 6d977a9df7
9 changed files with 232 additions and 489 deletions

View File

@ -26,22 +26,25 @@ extern "C" {
#if (GAME == 5)
#include "th01/hardware/egc.h"
#include "th04/hardware/bgimage.hpp"
#include "th04/hardware/grppsafx.h"
#include "th04/snd/snd.h"
#include "th04/gaiji/gaiji.h"
#include "th05/hardware/input.h"
#include "th05/formats/pi.hpp"
#elif (GAME == 4)
#include "th03/formats/pi.hpp"
#include "th04/hardware/input.h"
#else
#include "th03/hardware/input.h"
#include "th03/formats/pi.hpp"
#endif
#if (GAME >= 4)
#include "th04/hardware/grppsafx.h"
#include "th04/snd/snd.h"
#else
#include "th01/hardware/grppsafx.h"
#include "th03/hardware/input.h"
#include "th03/formats/pi.hpp"
#include "th03/snd/snd.h"
// Let's rather not have this one global, since it might be wrong in an
// in-game context?
#define key_det input_sp
#endif
}
#include "th03/math/str_val.hpp"
@ -117,6 +120,10 @@ extern int text_interval;
extern uint8_t text_col;
extern uint8_t text_fx; // TH04 and TH05 directly set [graph_putsa_fx_func].
extern int script_number_param_default;
#if (GAME >= 4)
#define text_fx graph_putsa_fx_func
#endif
// -----
#if (GAME == 5)
@ -1000,3 +1007,184 @@ script_ret_t pascal near script_op(unsigned char c)
}
return CONTINUE;
}
void near cutscene_animate(void)
{
extern ShiftJISKanji near CUTSCENE_KANJI[];
#if (GAME == 5)
int gaiji;
#endif
unsigned char c;
uint8_t speedup_cycle;
ShiftJISKanji& kanji = *CUTSCENE_KANJI;
cursor.x = BOX_LEFT;
cursor.y = BOX_TOP;
text_interval = TEXT_INTERVAL_DEFAULT;
text_col = V_WHITE;
text_fx = FX_WEIGHT_BOLD;
#if (GAME == 3)
speedup_cycle = 0;
#endif
// Necessary because scripts can (and do) show multiple text boxes on the
// initially black background.
// ZUN bug: TH05 assumes that they don't. While this is true for all
// scripts in the original game, it's still technically a bug.
#if (GAME != 5)
box_bg_allocate_and_snap();
#endif
fast_forward = false;
while(1) {
cutscene_input_sense();
if(key_det & INPUT_CANCEL) {
fast_forward = true;
} else {
fast_forward = false;
}
#if (GAME == 5) // ZUN bloat: Should be part of the colmap loop.
int i = 0;
#endif
c = *(script_p++);
if(str_sep_control_or_space(c)) {
continue;
}
// Opcode?
if(c == '\\') {
c = *(script_p++);
if(script_op(c) == STOP) {
break;
}
continue;
}
#if (GAME == 5)
if(c == '@') {
c = tolower(*script_p);
script_p++;
switch(c) {
case 't':
gaiji = gs_SWEAT;
break;
case 'h':
gaiji = gs_HEART_2;
break;
case '?':
gaiji = gs_QUESTION;
break;
case '!':
c = *(script_p++);
switch(c) {
case '!':
gaiji = gs_DOUBLE_EXCLAMATION;
break;
case '?':
gaiji = gs_EXCLAMATION_QUESTION;
break;
default:
script_p--;
gaiji = gs_EXCLAMATION;
break;
}
break;
default:
script_p--;
script_number_param_read_first(gaiji, gs_NOTES);
break;
}
graph_showpage(0);
graph_accesspage(1);
// Still ignoring [text_fx].
graph_gaiji_putc(cursor.x, cursor.y, gaiji, text_col);
cursor_advance_and_animate();
i = 1; // ZUN bloat
continue;
}
#endif
// Regular kanji
kanji.byte[0] = c;
c = *script_p;
kanji.byte[1] = c;
script_p++;
#if (GAME == 5)
if(cursor.x == BOX_LEFT) {
for(i = 0; i < colmap_count; i++) {
if(colmap.keys[i][0].t == kanji.t) {
text_col = colmap.values[i];
break;
}
}
}
#endif
#if (GAME >= 4)
graph_showpage(0);
graph_accesspage(1);
graph_putsa_fx(cursor.x, cursor.y, text_col, kanji.byte);
// ZUN bloat: All blitting operations in this module access the
// intended page before they blit. That's why preliminary state
// changes like this one are completely redundant, thankfully.
#if (GAME == 5)
graph_accesspage(0);
#endif
#else
graph_accesspage(1);
graph_putsa_fx(
cursor.x, cursor.y, (text_col | text_fx), kanji.byte
);
graph_accesspage(0);
graph_putsa_fx(
cursor.x, cursor.y, (text_col | text_fx), kanji.byte
);
#endif
cursor_advance_and_animate();
#if (GAME == 5)
i = 1; // ZUN bloat
#endif
// High-level overview, point 3)
#if (GAME == 3)
if(fast_forward) {
continue;
}
if(key_det == INPUT_NONE) {
frame_delay(text_interval);
} else {
int speedup_interval = (text_interval / 3);
if((speedup_cycle & 1) || speedup_interval) {
if(speedup_interval == 0) {
speedup_interval++;
}
frame_delay(speedup_interval);
}
speedup_cycle++;
}
#endif
}
#if (GAME == 5)
bgimage_free();
pi_free(PIC_SLOT);
#else
box_bg_put();
box_bg_free();
#endif
}

View File

@ -11,3 +11,6 @@ bool16 pascal near cutscene_script_load(const char *fn);
#else
void near cutscene_script_free();
#endif
// Runs the loaded cutscene script in a blocking way.
void near cutscene_animate(void);

View File

@ -1,15 +1,5 @@
NAME_LEN = 6
NAME_KANJI_LEN = (NAME_LEN / 2)
NAME_W = ((NAME_LEN * GLYPH_HALF_W) + GLYPH_FULL_W)
BOX_LEFT = 80
BOX_TOP = 320
BOX_W = 480
BOX_H = (GLYPH_H * 4)
BOX_VRAM_W = (BOX_W / BYTE_DOTS)
BOX_RIGHT = (BOX_LEFT + BOX_W)
BOX_BOTTOM = (BOX_TOP + BOX_H)
public _script, _fast_forward, _box_bg
if (GAME ge 4)

View File

@ -4,10 +4,16 @@ if (GAME eq 5)
evendata
endif
public _BOX_MASKS
_BOX_MASKS label word
dw 8888h, 0h, 2222h, 0h
dw 8888h, 4444h, 2222h, 1111h
dw 0AAAAh, 4444h, 0AAAAh, 1111h
dw 0AAAAh, 4444h, 0AAAAh, 5555h
dw 0FFFFh, 0FFFFh, 0FFFFh, 0FFFFh
if (GAME ge 4)
public _BOX_MASKS
_BOX_MASKS label word
dw 8888h, 0h, 2222h, 0h
dw 8888h, 4444h, 2222h, 1111h
dw 0AAAAh, 4444h, 0AAAAh, 1111h
dw 0AAAAh, 4444h, 0AAAAh, 5555h
dw 0FFFFh, 0FFFFh, 0FFFFh, 0FFFFh
endif
public _CUTSCENE_KANJI
_CUTSCENE_KANJI db ' ', 0
even

View File

@ -45,6 +45,9 @@ void pascal input_mode_cpu_vs_1p();
void pascal input_mode_cpu_vs_cpu();
// Just allows quitting via pressing any button.
void pascal input_mode_attract();
// Input sense function used in UIs
#define input_reset_sense_interface input_mode_interface
/// -----
// Basic keyboard input function in this game. Resets and updates all three

View File

@ -26,7 +26,7 @@ include th03/formats/scoredat.inc
extern __ctype:byte
extern _execl:proc
group_01 group CFG_LRES_TEXT, CUTSCENE_TEXT, mainl_01_TEXT, SCOREDAT_TEXT, REGIST_TEXT, mainl_03_TEXT
group_01 group CFG_LRES_TEXT, CUTSCENE_TEXT, SCOREDAT_TEXT, REGIST_TEXT, mainl_03_TEXT
; ===========================================================================
@ -998,7 +998,7 @@ loc_9EF1:
call graph_clear
call graph_show
call @cutscene_script_load$qnxc pascal, [off_E4B6]
call sub_AC6E
call @cutscene_animate$qv
call @cutscene_script_free$qv
call sub_990C
call sub_9A2C
@ -1198,146 +1198,9 @@ sub_9F8D endp
@CUTSCENE_SCRIPT_LOAD$QNXC procdesc pascal near \
fn:dword
@cutscene_script_free$qv procdesc near
@box_bg_allocate_and_snap$qv procdesc pascal near
@box_bg_free$qv procdesc pascal near
@box_bg_put$qv procdesc pascal near
@cursor_advance_and_animate$qv procdesc pascal near
@SCRIPT_OP$QUC procdesc pascal near \
c:word
@cutscene_animate$qv procdesc pascal near
CUTSCENE_TEXT ends
mainl_01_TEXT segment byte public 'CODE' use16
; =============== S U B R O U T I N E =======================================
; Attributes: bp-based frame
sub_AC6E proc near
var_6 = dword ptr -6
var_2 = byte ptr -2
var_1 = byte ptr -1
enter 6, 0
push si
mov word ptr [bp+var_6+2], ds
mov word ptr [bp+var_6], offset asc_EFC2
mov _cursor.x, BOX_LEFT
mov _cursor.y, BOX_TOP
mov _text_interval, 1
mov _text_col, V_WHITE
mov _text_fx, FX_WEIGHT_BOLD
mov [bp+var_2], 0
call @box_bg_allocate_and_snap$qv
mov _fast_forward, 0
loc_ACA3:
call input_mode_interface
test _input_sp.hi, high INPUT_CANCEL
jz short loc_ACB6
mov _fast_forward, 1
jmp short loc_ACBB
; ---------------------------------------------------------------------------
loc_ACB6:
mov _fast_forward, 0
loc_ACBB:
les bx, _script
mov al, es:[bx]
mov [bp+var_1], al
inc word ptr _script
mov ah, 0
mov bx, ax
test (__ctype + 1)[bx], _IS_CTL
jnz short loc_ACA3
cmp [bp+var_1], ' '
jz short loc_ACA3
cmp [bp+var_1], '\'
jnz short loc_ACFB
les bx, _script
mov al, es:[bx]
mov [bp+var_1], al
inc word ptr _script
call @script_op$quc pascal, word ptr [bp+var_1]
cmp al, -1
jnz short loc_ACA3
jmp loc_ADA0
; ---------------------------------------------------------------------------
loc_ACFB:
les bx, [bp+var_6]
mov al, [bp+var_1]
mov es:[bx], al
les bx, _script
mov al, es:[bx]
mov [bp+var_1], al
les bx, [bp+var_6]
mov es:[bx+1], al
inc word ptr _script
graph_accesspage 1
push _cursor.x
push _cursor.y
mov al, _text_col
or al, _text_fx
mov ah, 0
push ax
push word ptr [bp+var_6+2]
push bx
call graph_putsa_fx
graph_accesspage 0
push _cursor.x
push _cursor.y
mov al, _text_col
or al, _text_fx
mov ah, 0
push ax
pushd [bp+var_6]
call graph_putsa_fx
call @cursor_advance_and_animate$qv
cmp _fast_forward, 0
jnz loc_ACA3
cmp _input_sp, INPUT_NONE
jnz short loc_AD7A
push _text_interval
call frame_delay
jmp loc_ACA3
; ---------------------------------------------------------------------------
loc_AD7A:
mov ax, _text_interval
mov bx, 3
cwd
idiv bx
mov si, ax
test [bp+var_2], 1
jnz short loc_AD8F
or si, si
jz short loc_AD9A
loc_AD8F:
or si, si
jnz short loc_AD94
inc si
loc_AD94:
push si
call frame_delay
loc_AD9A:
inc [bp+var_2]
jmp loc_ACA3
; ---------------------------------------------------------------------------
loc_ADA0:
call @box_bg_put$qv
call @box_bg_free$qv
pop si
leave
retn
sub_AC6E endp
mainl_01_TEXT ends
SCOREDAT_TEXT segment byte public 'CODE' use16
@SCOREDAT_LOAD_AND_DECODE$Q6RANK_T procdesc pascal near \
rank:word
@ -1619,7 +1482,7 @@ loc_B9DD:
call graph_clear
call graph_show
call @cutscene_script_load$qnxc pascal, [off_EE4E]
call sub_AC6E
call @cutscene_animate$qv
call @cutscene_script_free$qv
call sub_C40D
les bx, _resident
@ -1638,7 +1501,7 @@ loc_B9DD:
push ds
push offset a@99ed_txt ; "@99ED.TXT"
call @cutscene_script_load$qnxc
call sub_AC6E
call @cutscene_animate$qv
call @cutscene_script_free$qv
loc_BA66:
@ -3101,7 +2964,8 @@ include th03/snd/se_priority[data].asm
a0 db '0',0
aOver_pi db 'over.pi',0
include th03/formats/pi_put_masked[data].asm
asc_EFC2 db ' ', 0
public _CUTSCENE_KANJI
_CUTSCENE_KANJI db ' ', 0
even
public _REGIST_PLAYCHARS
_REGIST_PLAYCHARS label dword

View File

@ -11,6 +11,14 @@
typedef enum {
g_NULL = '\0',
g_EMPTY = 0x02,
gs_NOTES, // ♫
gs_HEART_2 = 0x06, // 🎔 (duplicated)
gs_EXCLAMATION, // !
gs_QUESTION, // ?
gs_SWEAT, // 💦
gs_DOUBLE_EXCLAMATION, // ‼
gs_EXCLAMATION_QUESTION, // ⁉
#if (GAME == 5)
ga_RETURN_KEY = 0x1C,

View File

@ -206,7 +206,7 @@ sub_A0BD proc near
push word ptr off_E5C0+2
push bx
call @cutscene_script_load$qnxc
call sub_ADFC
call @cutscene_animate$qv
call @cutscene_script_free$qv
pop bp
retn
@ -336,102 +336,11 @@ _main endp
@CUTSCENE_SCRIPT_LOAD$QNXC procdesc pascal near \
fn:dword
@cutscene_script_free$qv procdesc near
@box_bg_allocate_and_snap$qv procdesc pascal near
@box_bg_free$qv procdesc pascal near
@box_bg_put$qv procdesc pascal near
@cursor_advance_and_animate$qv procdesc pascal near
@SCRIPT_OP$QUC procdesc pascal near \
c:word
@cutscene_animate$qv procdesc pascal near
CUTSCENE_TEXT ends
maine_01_TEXT segment byte public 'CODE' use16
; =============== S U B R O U T I N E =======================================
; Attributes: bp-based frame
sub_ADFC proc near
var_6 = dword ptr -6
var_1 = byte ptr -1
enter 6, 0
mov word ptr [bp+var_6+2], ds
mov word ptr [bp+var_6], offset asc_EB84
mov _cursor.x, BOX_LEFT
mov _cursor.y, BOX_TOP
mov _text_interval, 1
mov _text_col, V_WHITE
mov _graph_putsa_fx_func, FX_WEIGHT_BOLD
call @box_bg_allocate_and_snap$qv
mov _fast_forward, 0
loc_AE2D:
call far ptr _input_reset_sense
test _key_det.hi, high INPUT_CANCEL
jz short loc_AE40
mov _fast_forward, 1
jmp short loc_AE45
; ---------------------------------------------------------------------------
loc_AE40:
mov _fast_forward, 0
loc_AE45:
mov bx, _script_p
mov al, [bx]
mov [bp+var_1], al
inc _script_p
mov ah, 0
mov bx, ax
test (__ctype + 1)[bx], _IS_CTL
jnz short loc_AE2D
cmp [bp+var_1], ' '
jz short loc_AE2D
cmp [bp+var_1], '\'
jnz short loc_AE82
mov bx, _script_p
mov al, [bx]
mov [bp+var_1], al
inc _script_p
call @script_op$quc pascal, word ptr [bp+var_1]
cmp al, -1
jnz short loc_AE2D
jmp short loc_AEC8
; ---------------------------------------------------------------------------
loc_AE82:
les bx, [bp+var_6]
mov al, [bp+var_1]
mov es:[bx], al
mov bx, _script_p
mov al, [bx]
mov [bp+var_1], al
mov bx, word ptr [bp+var_6]
mov es:[bx+1], al
inc _script_p
graph_showpage 0
graph_accesspage 1
push _cursor.x
push _cursor.y
mov al, _text_col
mov ah, 0
push ax
push word ptr [bp+var_6+2]
push bx
call graph_putsa_fx
call @cursor_advance_and_animate$qv
jmp loc_AE2D
; ---------------------------------------------------------------------------
loc_AEC8:
call @box_bg_put$qv
call @box_bg_free$qv
leave
retn
sub_ADFC endp
; =============== S U B R O U T I N E =======================================
; Attributes: bp-based frame
@ -3418,8 +3327,6 @@ include th04/hardware/bgimage[data].asm
include th03/formats/cdg[data].asm
include th03/formats/pi_put_masked[data].asm
include th03/cutscene/cutscene[data].asm
asc_EB84 db ' ', 0
even
aSff1_pi db 'sff1.pi',0
aStaff db 'staff',0
aSff1_cdg db 'sff1.cdg',0

View File

@ -226,7 +226,7 @@ loc_A5DC:
push word ptr off_10190+2
push bx
call @cutscene_script_load$qnxc
call sub_AFD6
call @cutscene_animate$qv
pop bp
retn
sub_A5A4 endp
@ -303,11 +303,7 @@ _main endp
@CUTSCENE_SCRIPT_LOAD$QNXC procdesc pascal near \
fn:dword
@SCRIPT_NUMBER_PARAM_READ_FIRST$QMI procdesc pascal near \
ret:dword
@cursor_advance_and_animate$qv procdesc pascal near
@SCRIPT_OP$QUC procdesc pascal near \
c:word
@cutscene_animate$qv procdesc pascal near
CUTSCENE_TEXT ends
maine_01_TEXT segment byte public 'CODE' use16
@ -316,227 +312,6 @@ maine_01_TEXT segment byte public 'CODE' use16
; Attributes: bp-based frame
sub_AFD6 proc near
var_A = word ptr -0Ah
@@str = dword ptr -8
var_3 = word ptr -3
@@ch = word ptr -2
enter 0Ah, 0
push si
mov word ptr [bp+@@str+2], ds
mov word ptr [bp+@@str], offset asc_1085A
mov _cursor.x, BOX_LEFT
mov _cursor.y, BOX_TOP
mov _text_interval, 2
mov _text_col, V_WHITE
mov _graph_putsa_fx_func, FX_WEIGHT_BOLD
mov _fast_forward, 0
loc_B005:
call _input_reset_sense_held
test _key_det.hi, high INPUT_CANCEL
jz short loc_B018
mov _fast_forward, 1
jmp short loc_B01D
; ---------------------------------------------------------------------------
loc_B018:
mov _fast_forward, 0
loc_B01D:
xor si, si
mov bx, _script_p
mov al, [bx]
mov byte ptr [bp+var_3], al
inc _script_p
mov ah, 0
mov bx, ax
test (__ctype + 1)[bx], _IS_CTL
jnz short loc_B005
cmp byte ptr [bp+var_3], ' '
jz short loc_B005
cmp byte ptr [bp+var_3], '\'
jnz short loc_B05D
mov bx, _script_p
mov al, [bx]
mov byte ptr [bp+var_3], al
inc _script_p
call @script_op$quc pascal, [bp+var_3]
cmp al, -1
jnz short loc_B005
jmp loc_B196
; ---------------------------------------------------------------------------
loc_B05D:
cmp byte ptr [bp+var_3], '@'
jnz loc_B118
mov bx, _script_p
mov al, [bx]
mov ah, 0
push ax ; ch
call _tolower
pop cx
mov byte ptr [bp+var_3], al
inc _script_p
mov ah, 0
mov [bp+var_A], ax
mov cx, 4 ; switch 4 cases
mov bx, offset word_B1A5
loc_B086:
mov ax, cs:[bx]
cmp ax, [bp+var_A]
jz short loc_B095
add bx, 2
loop loc_B086
jmp short loc_B0E2 ; default
; ---------------------------------------------------------------------------
loc_B095:
jmp word ptr cs:[bx+8] ; switch jump
loc_B099:
mov [bp+@@ch], 9 ; jumptable 0000B095 case 116
jmp short loc_B0F4
; ---------------------------------------------------------------------------
loc_B0A0:
mov [bp+@@ch], 6 ; jumptable 0000B095 case 104
jmp short loc_B0F4
; ---------------------------------------------------------------------------
loc_B0A7:
mov [bp+@@ch], 8 ; jumptable 0000B095 case 63
jmp short loc_B0F4
; ---------------------------------------------------------------------------
loc_B0AE:
mov bx, _script_p ; jumptable 0000B095 case 33
mov al, [bx]
mov byte ptr [bp+var_3], al
inc _script_p
mov ah, 0
cmp ax, '!'
jz short loc_B0C9
cmp ax, '?'
jz short loc_B0D0
jmp short loc_B0D7
; ---------------------------------------------------------------------------
loc_B0C9:
mov [bp+@@ch], 0Ah
jmp short loc_B0F4
; ---------------------------------------------------------------------------
loc_B0D0:
mov [bp+@@ch], 0Bh
jmp short loc_B0F4
; ---------------------------------------------------------------------------
loc_B0D7:
dec _script_p
mov [bp+@@ch], 7
jmp short loc_B0F4
; ---------------------------------------------------------------------------
loc_B0E2:
dec _script_p ; default
mov _script_number_param_default, 3
push ss
lea ax, [bp+@@ch]
push ax
call @script_number_param_read_first$qmi
loc_B0F4:
graph_showpage 0
graph_accesspage 1
push _cursor.x
push _cursor.y
push [bp+@@ch]
mov al, _text_col
mov ah, 0
push ax
call graph_gaiji_putc
jmp short loc_B18D
; ---------------------------------------------------------------------------
loc_B118:
les bx, [bp+@@str]
mov al, byte ptr [bp+var_3]
mov es:[bx], al
mov bx, _script_p
mov al, [bx]
mov byte ptr [bp+var_3], al
mov bx, word ptr [bp+@@str]
mov es:[bx+1], al
inc _script_p
cmp _cursor.x, 80
jnz short loc_B164
xor si, si
jmp short @@colmap_more?
; ---------------------------------------------------------------------------
@@colmap_loop:
mov bx, si
imul bx, NAME_LEN
mov ax, _colmap.CM_keys[bx]
les bx, [bp+@@str]
cmp ax, es:[bx]
jnz short @@colmap_next
mov al, _colmap.CM_values[si]
mov _text_col, al
jmp short loc_B164
; ---------------------------------------------------------------------------
@@colmap_next:
inc si
@@colmap_more?:
mov al, _colmap_count
mov ah, 0
cmp ax, si
jg short @@colmap_loop
loc_B164:
graph_showpage 0
graph_accesspage 1
push _cursor.x
push _cursor.y
mov al, _text_col
mov ah, 0
push ax
pushd [bp+@@str]
call graph_putsa_fx
graph_accesspage 0
loc_B18D:
call @cursor_advance_and_animate$qv
mov si, 1
jmp loc_B005
; ---------------------------------------------------------------------------
loc_B196:
call _bgimage_free
call pi_free pascal, 0
pop si
leave
retn
sub_AFD6 endp
; ---------------------------------------------------------------------------
word_B1A5 dw 21h, 3Fh, 68h, 74h
; value table for switch statement
dw offset loc_B0AE ; jump table for switch statement
dw offset loc_B0A7
dw offset loc_B0A0
dw offset loc_B099
; =============== S U B R O U T I N E =======================================
; Attributes: bp-based frame
sub_B1B5 proc near
@@str = dword ptr -4
@ -6453,6 +6228,7 @@ loc_E70D:
; No idea why TASM can't assemble this properly after script_op() was
; decompiled. Seems to be related to the size of this segment; it works
; when removing some instructions further above.
; It still doesn't after decompiling cutscene_animate() though?
db 0Fh, 8Ch, 93h, 00h
test _key_det.hi, high INPUT_CANCEL
jnz short loc_E757
@ -6629,8 +6405,6 @@ include th05/formats/pi_buffers[bss].asm
include th05/hardware/vram_planes[data].asm
include th03/formats/cdg[data].asm
include th03/cutscene/cutscene[data].asm
asc_1085A db ' ', 0
even
byte_1085E db 0
db 0
public _ALLCAST_BG_FN