From 96d46069ceaeeda83c6dbd1d4814969f69a0da0b Mon Sep 17 00:00:00 2001 From: kichikuou Date: Thu, 2 Apr 2026 13:29:21 +0900 Subject: [PATCH] PartsEngine: Implement movie parts This adds PARTS_MOVIE type which creates a SACT sprite as a render target for DrawMovie. The GL texture is shared between the SACT sprite and parts_common, so movie frames rendered to the sprite are directly visible through the parts engine's rendering path. --- include/parts.h | 3 +++ src/hll/PartsEngine.c | 35 +++++++++++++++++++++++++- src/parts/debug.c | 11 ++++++++ src/parts/parts.c | 51 ++++++++++++++++++++++++++++++++++++++ src/parts/parts_internal.h | 10 +++++++- src/parts/render.c | 1 + src/parts/save.c | 4 +++ 7 files changed, 113 insertions(+), 2 deletions(-) diff --git a/include/parts.h b/include/parts.h index 2043092..d83999f 100644 --- a/include/parts.h +++ b/include/parts.h @@ -116,6 +116,9 @@ bool PE_Load(struct page **buffer); // GUIEngine int PE_GetFreeNumber(void); bool PE_IsExist(int parts_no); +// PartsFunc interface +bool PE_init_parts_movie(int parts_no, int width, int height, int bg_r, int bg_g, int bg_b, int state); +int PE_get_movie_sprite(int parts_no, int state); // construction.c bool PE_AddCreateToPartsConstructionProcess(int parts_no, int w, int h, int state); diff --git a/src/hll/PartsEngine.c b/src/hll/PartsEngine.c index cf8af87..239dcec 100644 --- a/src/hll/PartsEngine.c +++ b/src/hll/PartsEngine.c @@ -18,6 +18,7 @@ #include "system4/ain.h" +#include "vm/page.h" #include "parts.h" #include "hll.h" @@ -96,7 +97,39 @@ static void PartsEngine_AddComponentMotionPos(int parts_no, float begin_x, float begin_t, end_t, curve_name); } -HLL_WARN_UNIMPLEMENTED(0, int, PartsEngine, PartsFunc, int func_id, struct page **array_int, struct page **array_float, struct page **array_string); +// Generic dispatch function for PartsEngine operations. +// func_id selects the operation; arguments and return values are passed +// through three typed arrays (int/bool, float, string). +// +// Calling convention (from game script): +// 1. Push input values into the appropriate arrays. +// 2. Push a placeholder (0 or "") for each output slot. +// 3. Call PartsFunc; outputs are written back into the placeholder slots. +static int PartsEngine_PartsFunc(int func_id, struct page **array_int, + struct page **array_float, struct page **array_string) +{ + int nr_ints = (array_int && *array_int) ? (*array_int)->nr_vars : 0; + union vm_value *ints = nr_ints ? (*array_int)->values : NULL; + +#define REQUIRE_INTS(n) \ + if (nr_ints != (n)) VM_ERROR("Invalid arguments for PartsFunc %d: expected %d ints, got %d", func_id, (n), nr_ints) + + switch (func_id) { + case 162: // bool InitPartsMovie(int parts_no, int width, int height, int bg_r, int bg_g, int bg_b, int state) + REQUIRE_INTS(8); + ints[7].i = PE_init_parts_movie(ints[0].i, ints[1].i, ints[2].i, ints[3].i, ints[4].i, ints[5].i, ints[6].i); + return 1; + case 163: // int GetMovieSprite(int parts_no, int state) + REQUIRE_INTS(3); + ints[2].i = PE_get_movie_sprite(ints[0].i, ints[1].i); + return 1; + default: + WARNING("Unknown func_id: %d", func_id); + return 0; + } +#undef REQUIRE_INTS +} + HLL_WARN_UNIMPLEMENTED(, void, PartsEngine, StopSoundWithoutSystemSound); HLL_WARN_UNIMPLEMENTED(, void, PartsEngine, ReleaseMessage, void); HLL_WARN_UNIMPLEMENTED(0, int, PartsEngine, GetMessageType, void); diff --git a/src/parts/debug.c b/src/parts/debug.c index b4c9ae9..7dac020 100644 --- a/src/parts/debug.c +++ b/src/parts/debug.c @@ -209,6 +209,11 @@ static void parts_flat_to_json(struct parts_flat *flat, cJSON *out, bool verbose cJSON_AddNumberToObject(out, "end_frame", flat->end_frame); } +static void parts_movie_to_json(struct parts_movie *movie, cJSON *out, bool verbose) +{ + cJSON_AddNumberToObject(out, "sprite_no", movie->sprite_no); +} + static cJSON *parts_state_to_json(struct parts_state *state, bool verbose) { static const char *state_types[PARTS_NR_TYPES] = { @@ -262,6 +267,9 @@ static cJSON *parts_state_to_json(struct parts_state *state, bool verbose) case PARTS_FLAT: parts_flat_to_json(&state->flat, obj, verbose); break; + case PARTS_MOVIE: + parts_movie_to_json(&state->movie, obj, verbose); + break; } return obj; @@ -545,6 +553,9 @@ static void parts_list_print(struct parts *parts, int indent) case PARTS_FLAT: sys_message("(flat %s)", state->flat.name ? display_sjis0(state->flat.name->text) : "(null)"); break; + case PARTS_MOVIE: + sys_message("(movie sp=%d)", state->movie.sprite_no); + break; } sys_message(" @ z=%d (%d)\n", parts->global.z, parts->local.z); diff --git a/src/parts/parts.c b/src/parts/parts.c index 9575acc..e5e5902 100644 --- a/src/parts/parts.c +++ b/src/parts/parts.c @@ -26,6 +26,7 @@ #include "vm/page.h" #include "xsystem4.h" #include "sact.h" +#include "sprite.h" #include "parts.h" #include "parts_internal.h" @@ -178,6 +179,12 @@ static void parts_state_free(struct parts_state *state) case PARTS_FLAT: parts_flat_free(&state->flat); break; + case PARTS_MOVIE: + // The texture is owned by the SACT sprite. + state->common.texture.handle = 0; + if (state->movie.sprite_no >= 0) + sact_SP_Delete(state->movie.sprite_no); + break; } memset(state, 0, sizeof(struct parts_state)); } @@ -225,6 +232,9 @@ void parts_state_reset(struct parts_state *state, enum parts_type type) case PARTS_FLASH: case PARTS_FLAT: break; + case PARTS_MOVIE: + state->movie.sprite_no = -1; + break; } } @@ -300,6 +310,14 @@ struct parts_flat *parts_get_flat(struct parts *parts, int state) return &parts->states[state].flat; } +struct parts_movie *parts_get_movie(struct parts *parts, int state) +{ + if (parts->states[state].type != PARTS_MOVIE) { + parts_state_reset(&parts->states[state], PARTS_MOVIE); + } + return &parts->states[state].movie; +} + static Point calculate_offset(int mode, int w, int h) { switch (mode) { @@ -1861,3 +1879,36 @@ void PE_SetSpeedupRateByMessageSkip(int parts_no, int rate) if (rate != 1) UNIMPLEMENTED("(%d, %d)"); } + +bool PE_init_parts_movie(int parts_no, int width, int height, int bg_r, int bg_g, int bg_b, int state) +{ + if (!parts_state_valid(--state)) + return false; + + struct parts *parts = parts_get(parts_no); + struct parts_movie *movie = parts_get_movie(parts, state); + + int sp_no = sact_SP_GetUnuseNum(0); + struct sact_sprite *sp = sact_create_sprite(sp_no, width, height, bg_r, bg_g, bg_b, 255); + if (!sp) + return false; + + struct texture *tex = sprite_get_texture(sp); + movie->sprite_no = sp_no; + movie->common.texture = *tex; // XXX: textures normally shouldn't be copied like this... + parts_set_dims(parts, &movie->common, width, height); + parts_dirty(parts); + return true; +} + +int PE_get_movie_sprite(int parts_no, int state) +{ + if (!parts_state_valid(--state)) + return -1; + + struct parts *parts = parts_try_get(parts_no); + if (!parts || parts->states[state].type != PARTS_MOVIE) + return -1; + + return parts->states[state].movie.sprite_no; +} diff --git a/src/parts/parts_internal.h b/src/parts/parts_internal.h index 7078389..3054e1f 100644 --- a/src/parts/parts_internal.h +++ b/src/parts/parts_internal.h @@ -104,7 +104,8 @@ enum parts_type { PARTS_CONSTRUCTION_PROCESS, PARTS_FLASH, PARTS_FLAT, -#define PARTS_NR_TYPES (PARTS_FLAT+1) + PARTS_MOVIE, +#define PARTS_NR_TYPES (PARTS_MOVIE+1) }; struct parts_common { @@ -313,6 +314,11 @@ struct parts_flat { size_t nr_textures; }; +struct parts_movie { + struct parts_common common; + int sprite_no; // SACT sprite number used as movie render target +}; + struct parts_state { enum parts_type type; union { @@ -325,6 +331,7 @@ struct parts_state { struct parts_construction_process cproc; struct parts_flash flash; struct parts_flat flat; + struct parts_movie movie; }; }; @@ -385,6 +392,7 @@ struct parts_gauge *parts_get_vgauge(struct parts *parts, int state); struct parts_construction_process *parts_get_construction_process(struct parts *parts, int state); struct parts_flash *parts_get_flash(struct parts *parts, int state); struct parts_flat *parts_get_flat(struct parts *parts, int state); +struct parts_movie *parts_get_movie(struct parts *parts, int state); void parts_set_pos(struct parts *parts, Point pos); void parts_set_global_pos(Point pos); void parts_set_dims(struct parts *parts, struct parts_common *common, int w, int h); diff --git a/src/parts/render.c b/src/parts/render.c index 078a3bf..d56a5db 100644 --- a/src/parts/render.c +++ b/src/parts/render.c @@ -419,6 +419,7 @@ void parts_render(struct parts *parts) case PARTS_HGAUGE: case PARTS_VGAUGE: case PARTS_CONSTRUCTION_PROCESS: + case PARTS_MOVIE: if (state->common.texture.handle) parts_render_cg(parts, &state->common); break; diff --git a/src/parts/save.c b/src/parts/save.c index f99c00b..172a262 100644 --- a/src/parts/save.c +++ b/src/parts/save.c @@ -386,6 +386,8 @@ static void save_parts_state(struct iarray_writer *w, struct parts_state *state) case PARTS_FLAT: save_parts_flat(w, &state->flat); break; + case PARTS_MOVIE: + break; } } @@ -428,6 +430,8 @@ static void load_parts_state(struct iarray_reader *r, struct parts *parts, case PARTS_FLAT: load_parts_flat(r, parts, &state->flat); break; + case PARTS_MOVIE: + break; } }