diff --git a/include/parts.h b/include/parts.h index 71ad136..9562033 100644 --- a/include/parts.h +++ b/include/parts.h @@ -62,11 +62,6 @@ bool PE_SetNumeralSpace(int parts_no, int space, int state); bool PE_SetNumeralLength(int parts_no, int length, int state); bool PE_SetNumeralSurfaceArea(int parts_no, int x, int y, int w, int h, int state); bool PE_SetPartsRectangleDetectionSize(int PartsNumber, int Width, int Height, int State); -bool PE_SetPartsFlash(int PartsNumber, struct string *pIFlashFileName, int State); -bool PE_IsPartsFlashEnd(int PartsNumber, int State); -int PE_GetPartsFlashCurrentFrameNumber(int PartsNumber, int State); -bool PE_BackPartsFlashBeginFrame(int PartsNumber, int State); -bool PE_StepPartsFlashFinalFrame(int PartsNumber, int State); void PE_ReleaseParts(int parts_no); void PE_ReleaseAllParts(void); void PE_ReleaseAllPartsWithoutSystem(void); @@ -207,4 +202,16 @@ bool PE_SetPartsFontEdgeWeight(int parts_no, float edge_weight, int state); bool PE_SetTextCharSpace(int parts_no, int char_space, int state); bool PE_SetTextLineSpace(int parts_no, int line_space, int state); +// flash.c +bool PE_SetPartsFlash(int parts_no, struct string *flash_filename, int state); +bool PE_IsPartsFlashEnd(int parts_no, int state); +int PE_GetPartsFlashCurrentFrameNumber(int parts_no, int state); +bool PE_BackPartsFlashBeginFrame(int parts_no, int state); +bool PE_StepPartsFlashFinalFrame(int parts_no, int state); +bool PE_SetPartsFlashAndStop(int parts_no, struct string *flash_filename, int state); +bool PE_StopPartsFlash(int parts_no, int state); +bool PE_StartPartsFlash(int parts_no, int state); +bool PE_GoFramePartsFlash(int parts_no, int frame_no, int state); +int PE_GetPartsFlashEndFrame(int parts_no, int state); + #endif /* SYSTEM4_PARTS_H */ diff --git a/src/hll/GoatGUIEngine.c b/src/hll/GoatGUIEngine.c index 6cd6f90..e55f3e5 100644 --- a/src/hll/GoatGUIEngine.c +++ b/src/hll/GoatGUIEngine.c @@ -56,11 +56,11 @@ HLL_LIBRARY(GoatGUIEngine, HLL_EXPORT(SetNumeralShowComma, PE_SetNumeralShowComma), HLL_EXPORT(SetNumeralSpace, PE_SetNumeralSpace), HLL_TODO_EXPORT(SetPartsRectangleDetectionSize, PE_SetPartsRectangleDetectionSize), - HLL_TODO_EXPORT(SetPartsFlash, PE_SetPartsFlash), - HLL_TODO_EXPORT(IsPartsFlashEnd, PE_IsPartsFlashEnd), - HLL_TODO_EXPORT(GetPartsFlashCurrentFrameNumber, PE_GetPartsFlashCurrentFrameNumber), - HLL_TODO_EXPORT(BackPartsFlashBeginFrame, PE_BackPartsFlashBeginFrame), - HLL_TODO_EXPORT(StepPartsFlashFinalFrame, PE_StepPartsFlashFinalFrame), + HLL_EXPORT(SetPartsFlash, PE_SetPartsFlash), + HLL_EXPORT(IsPartsFlashEnd, PE_IsPartsFlashEnd), + HLL_EXPORT(GetPartsFlashCurrentFrameNumber, PE_GetPartsFlashCurrentFrameNumber), + HLL_EXPORT(BackPartsFlashBeginFrame, PE_BackPartsFlashBeginFrame), + HLL_EXPORT(StepPartsFlashFinalFrame, PE_StepPartsFlashFinalFrame), HLL_EXPORT(ReleaseParts, PE_ReleaseParts), HLL_EXPORT(ReleaseAllParts, PE_ReleaseAllParts), HLL_EXPORT(ReleaseAllPartsWithoutSystem, PE_ReleaseAllPartsWithoutSystem), @@ -126,11 +126,11 @@ HLL_LIBRARY(GoatGUIEngine, HLL_EXPORT(Save, PE_Save), HLL_EXPORT(Load, PE_Load), - HLL_TODO_EXPORT(SetPartsFlashAndStop, PE_SetPartsFlashAndStop), - HLL_TODO_EXPORT(StopPartsFlash, PE_StopPartsFlash), - HLL_TODO_EXPORT(StartPartsFlash, PE_StartPartsFlash), - HLL_TODO_EXPORT(GoFramePartsFlash, PE_GoFramePartsFlash), - HLL_TODO_EXPORT(GetPartsFlashEndFrame, PE_GetPartsFlashEndFrame), + HLL_EXPORT(SetPartsFlashAndStop, PE_SetPartsFlashAndStop), + HLL_EXPORT(StopPartsFlash, PE_StopPartsFlash), + HLL_EXPORT(StartPartsFlash, PE_StartPartsFlash), + HLL_EXPORT(GoFramePartsFlash, PE_GoFramePartsFlash), + HLL_EXPORT(GetPartsFlashEndFrame, PE_GetPartsFlashEndFrame), HLL_TODO_EXPORT(ClearPartsConstructionProcess, PE_ClearPartsConstructionProcess), HLL_TODO_EXPORT(AddCreateToPartsConstructionProcess, PE_AddCreateToPartsConstructionProcess), HLL_TODO_EXPORT(AddCreatePixelOnlyToPartsConstructionProcess, PE_AddCreatePixelOnlyToPartsConstructionProcess), diff --git a/src/hll/PartsEngine.c b/src/hll/PartsEngine.c index a5b16ec..ca14f8c 100644 --- a/src/hll/PartsEngine.c +++ b/src/hll/PartsEngine.c @@ -85,18 +85,18 @@ HLL_LIBRARY(PartsEngine, HLL_TODO_EXPORT(SetPartsRectangleDetectionSurfaceArea, PartsEngine_SetPartsRectangleDetectionSurfaceArea), HLL_TODO_EXPORT(SetPartsCGDetectionSize, PartsEngine_SetPartsCGDetectionSize), HLL_TODO_EXPORT(SetPartsCGDetectionSurfaceArea, PartsEngine_SetPartsCGDetectionSurfaceArea), - HLL_TODO_EXPORT(SetPartsFlash, PartsEngine_SetPartsFlash), - HLL_TODO_EXPORT(IsPartsFlashEnd, PartsEngine_IsPartsFlashEnd), - HLL_TODO_EXPORT(GetPartsFlashCurrentFrameNumber, PartsEngine_GetPartsFlashCurrentFrameNumber), - HLL_TODO_EXPORT(BackPartsFlashBeginFrame, PartsEngine_BackPartsFlashBeginFrame), - HLL_TODO_EXPORT(StepPartsFlashFinalFrame, PartsEngine_StepPartsFlashFinalFrame), - HLL_TODO_EXPORT(SetPartsFlashSurfaceArea, PartsEngine_SetPartsFlashSurfaceArea), - HLL_TODO_EXPORT(SetPartsFlashAndStop, PartsEngine_SetPartsFlashAndStop), - HLL_TODO_EXPORT(StopPartsFlash, PartsEngine_StopPartsFlash), - HLL_TODO_EXPORT(StartPartsFlash, PartsEngine_StartPartsFlash), - HLL_TODO_EXPORT(GoFramePartsFlash, PartsEngine_GoFramePartsFlash), - HLL_TODO_EXPORT(GetPartsFlashEndFrame, PartsEngine_GetPartsFlashEndFrame), - HLL_TODO_EXPORT(ExistsFlashFile, PartsEngine_ExistsFlashFile), + HLL_EXPORT(SetPartsFlash, PE_SetPartsFlash), + HLL_EXPORT(IsPartsFlashEnd, PE_IsPartsFlashEnd), + HLL_EXPORT(GetPartsFlashCurrentFrameNumber, PE_GetPartsFlashCurrentFrameNumber), + HLL_EXPORT(BackPartsFlashBeginFrame, PE_BackPartsFlashBeginFrame), + HLL_EXPORT(StepPartsFlashFinalFrame, PE_StepPartsFlashFinalFrame), + HLL_TODO_EXPORT(SetPartsFlashSurfaceArea, PE_SetPartsFlashSurfaceArea), + HLL_EXPORT(SetPartsFlashAndStop, PE_SetPartsFlashAndStop), + HLL_EXPORT(StopPartsFlash, PE_StopPartsFlash), + HLL_EXPORT(StartPartsFlash, PE_StartPartsFlash), + HLL_EXPORT(GoFramePartsFlash, PE_GoFramePartsFlash), + HLL_EXPORT(GetPartsFlashEndFrame, PE_GetPartsFlashEndFrame), + HLL_TODO_EXPORT(ExistsFlashFile, PE_ExistsFlashFile), HLL_TODO_EXPORT(ClearPartsConstructionProcess, PartsEngine_ClearPartsConstructionProcess), HLL_EXPORT(AddCreateToPartsConstructionProcess, PE_AddCreateToPartsConstructionProcess), HLL_EXPORT(AddCreatePixelOnlyToPartsConstructionProcess, PE_AddCreatePixelOnlyToPartsConstructionProcess), diff --git a/src/meson.build b/src/meson.build index 861781d..fbacf9b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -53,6 +53,7 @@ xsystem4 = [version_h, 'parts/construction.c', 'parts/debug.c', + 'parts/flash.c', 'parts/input.c', 'parts/motion.c', 'parts/parts.c', diff --git a/src/parts/debug.c b/src/parts/debug.c index 01de856..8d347e5 100644 --- a/src/parts/debug.c +++ b/src/parts/debug.c @@ -172,6 +172,13 @@ static void parts_construction_process_to_json(struct parts_construction_process } } +static void parts_flash_to_json(struct parts_flash *flash, cJSON *out, bool verbose) +{ + cJSON_AddSjisToObject(out, "filename", flash->name->text); + cJSON_AddNumberToObject(out, "frame_count", flash->swf->frame_count); + cJSON_AddNumberToObject(out, "current_frame", flash->current_frame); +} + static cJSON *parts_state_to_json(struct parts_state *state, bool verbose) { static const char *state_types[PARTS_NR_TYPES] = { @@ -182,7 +189,8 @@ static cJSON *parts_state_to_json(struct parts_state *state, bool verbose) [PARTS_NUMERAL] = "numeral", [PARTS_HGAUGE] = "hgauge", [PARTS_VGAUGE] = "vgauge", - [PARTS_CONSTRUCTION_PROCESS] = "construction_process" + [PARTS_CONSTRUCTION_PROCESS] = "construction_process", + [PARTS_FLASH] = "flash" }; const char *type = "invalid"; if (state->type >= 0 && state->type < PARTS_NR_TYPES) @@ -192,6 +200,8 @@ static cJSON *parts_state_to_json(struct parts_state *state, bool verbose) cJSON_AddStringToObject(obj, "type", type); switch (state->type) { + case PARTS_UNINITIALIZED: + break; case PARTS_CG: parts_cg_to_json(&state->cg, obj, verbose); break; @@ -211,7 +221,8 @@ static cJSON *parts_state_to_json(struct parts_state *state, bool verbose) case PARTS_CONSTRUCTION_PROCESS: parts_construction_process_to_json(&state->cproc, obj, verbose); break; - default: + case PARTS_FLASH: + parts_flash_to_json(&state->flash, obj, verbose); break; } @@ -463,6 +474,9 @@ static void parts_list_print(struct parts *parts, int indent) sys_message(")"); break; } + case PARTS_FLASH: + sys_message("(flash %s)", display_sjis0(state->flash.name->text)); + break; } sys_message(" @ z=%d (%d)\n", parts->global.z, parts->local.z); diff --git a/src/parts/flash.c b/src/parts/flash.c new file mode 100644 index 0000000..64ceb72 --- /dev/null +++ b/src/parts/flash.c @@ -0,0 +1,380 @@ +/* Copyright (C) 2023 kichikuou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include "system4.h" +#include "system4/archive.h" +#include "system4/hashtable.h" +#include "system4/string.h" + +#include "asset_manager.h" +#include "swf.h" +#include "xsystem4.h" +#include "parts.h" +#include "parts_internal.h" + +#define muldiv(x, y, denom) ((int64_t)(x) * (int64_t)(y) / (int64_t)(denom)) + +static void clear_display_list(struct parts_flash *f) +{ + while (!TAILQ_EMPTY(&f->display_list)) { + struct parts_flash_object *obj = TAILQ_FIRST(&f->display_list); + TAILQ_REMOVE(&f->display_list, obj, entry); + free(obj); + } +} + +static void free_bitmap(void *value) +{ + gfx_delete_texture(value); + free(value); +} + +void parts_flash_free(struct parts_flash *f) +{ + clear_display_list(f); + if (f->swf) + swf_free(f->swf); + if (f->name) + free_string(f->name); + if (f->dictionary) + ht_free_int(f->dictionary); + if (f->bitmaps) { + ht_foreach_value(f->bitmaps, free_bitmap); + ht_free_int(f->bitmaps); + } +} + +bool parts_flash_load(struct parts *parts, struct parts_flash *f, struct string *filename) +{ + if (f->swf) { + parts_flash_free(f); + memset(f, 0, sizeof(struct parts_flash)); + } + + TAILQ_INIT(&f->display_list); + if (!filename) + return false; + struct archive_data *dfile = asset_get_by_name(ASSET_FLASH, filename->text, NULL); + if (!dfile) + return false; + + f->swf = aff_load(dfile->data, dfile->size); + archive_free_data(dfile); + if (!f->swf) + return false; + + f->name = string_dup(filename); + f->tag = f->swf->tags; + f->stopped = false; + f->elapsed = 0; + f->current_frame = 0; + f->dictionary = ht_create(256); + f->bitmaps = ht_create(64); + + int width = f->swf->frame_size.x_max / 20; + int height = f->swf->frame_size.y_max / 20; + parts_set_dims(parts, &f->common, width, height); + return true; +} + +static void define_bits_lossless(struct parts_flash *f, struct swf_tag_define_bits_lossless *tag) +{ + ht_put_int(f->dictionary, tag->character_id, tag); + struct ht_slot *slot = ht_put_int(f->bitmaps, tag->character_id, NULL); + if (!slot->value) { + struct texture *t = xcalloc(1, sizeof(struct texture)); + gfx_init_texture_with_pixels(t, tag->width, tag->height, tag->data); + slot->value = t; + } +} + +static void define_shape(struct parts_flash *f, struct swf_tag_define_shape *tag) +{ + ht_put_int(f->dictionary, tag->shape_id, tag); +} + +static void do_action(struct parts_flash *f, struct swf_tag_do_action *tag) +{ + for (struct swf_action *a = tag->actions; a->code != ACTION_END; a = a->next) { + switch (a->code) { + case ACTION_STOP: + f->stopped = true; + break; + case ACTION_CONSTANT_POOL: + case ACTION_PUSH: + case ACTION_POP: + case ACTION_CALL_FUNCTION: + break; + default: + ERROR("unimplemented action %x", a->code); + break; + } + } +} + +static struct parts_flash_object *update_object(struct parts_flash_object *obj, struct swf_tag_place_object *tag) +{ + if (!obj) { + if (tag->flags & PLACE_FLAG_HAS_CHARACTER) { + obj = xcalloc(1, sizeof(struct parts_flash_object)); + obj->depth = tag->depth; + } else { + ERROR("no object to update (depth=%d)", tag->depth); + } + } + if (tag->flags & PLACE_FLAG_HAS_CHARACTER) { + obj->character_id = tag->character_id; + obj->matrix = (struct swf_matrix) { + .scale_x = 1 << 16, + .scale_y = 1 << 16, + }; + obj->color_transform = (struct swf_cxform_with_alpha) { + .mult_terms = { 256, 256, 256, 256 } + }; + } + + if (tag->flags & PLACE_FLAG_HAS_MATRIX) + obj->matrix = tag->matrix; + if (tag->flags & PLACE_FLAG_HAS_COLOR_TRANSFORM) + obj->color_transform = tag->color_transform; + if (tag->flags & PLACE_FLAG_HAS_BLEND_MODE) + WARNING("PLACE_FLAG_HAS_BLEND_MODE is not supported yet"); + + return obj; +} + +static void place_object(struct parts_flash *f, struct swf_tag_place_object *tag) +{ + struct parts_flash_object *p; + TAILQ_FOREACH(p, &f->display_list, entry) { + if (p->depth == tag->depth) { + update_object(p, tag); + return; + } else if (p->depth > tag->depth) { + struct parts_flash_object *obj = update_object(NULL, tag); + TAILQ_INSERT_BEFORE(p, obj, entry); + return; + } + } + p = update_object(NULL, tag); + TAILQ_INSERT_TAIL(&f->display_list, p, entry); +} + +static void remove_object(struct parts_flash *f, struct swf_tag_remove_object2 *tag) +{ + struct parts_flash_object *p; + TAILQ_FOREACH(p, &f->display_list, entry) { + if (p->depth == tag->depth) { + TAILQ_REMOVE(&f->display_list, p, entry); + free(p); + return; + } + } +} + +bool parts_flash_seek(struct parts_flash *f, int frame) +{ + if (!f->swf || f->current_frame == frame) + return false; + + if (f->current_frame > frame) { + f->current_frame = 0; + f->tag = f->swf->tags; + clear_display_list(f); + } + + struct swf_tag *t; + for (t = f->tag; t && f->current_frame < frame; t = t->next) { + switch (t->type) { + case TAG_FILE_ATTRIBUTES: + case TAG_SET_BACKGROUND_COLOR: + // Do nothing. + break; + case TAG_END: + f->stopped = true; + break; + case TAG_SHOW_FRAME: + f->current_frame++; + break; + case TAG_DEFINE_BITS_LOSSLESS: + case TAG_DEFINE_BITS_LOSSLESS2: + define_bits_lossless(f, (struct swf_tag_define_bits_lossless*)t); + break; + case TAG_DEFINE_SHAPE: + define_shape(f, (struct swf_tag_define_shape*)t); + break; + case TAG_DO_ACTION: + do_action(f, (struct swf_tag_do_action*)t); + break; + case TAG_PLACE_OBJECT2: + case TAG_PLACE_OBJECT3: + place_object(f, (struct swf_tag_place_object*)t); + break; + case TAG_REMOVE_OBJECT2: + remove_object(f, (struct swf_tag_remove_object2*)t); + break; + default: + ERROR("unknown tag 0x%x", t->type); + } + } + f->tag = t; + return true; +} + +bool parts_flash_update(struct parts_flash *f, int passed_time) +{ + if (!f->swf || f->stopped) + return false; + f->elapsed += passed_time; + int delta_frame = muldiv(f->elapsed, f->swf->frame_rate, 256000); + if (!delta_frame) + return false; + f->elapsed -= muldiv(delta_frame, 256000, f->swf->frame_rate); + return parts_flash_seek(f, f->current_frame + delta_frame); +} + +bool PE_SetPartsFlash(int parts_no, struct string *flash_filename, int state) +{ + if (!parts_state_valid(--state)) + return false; + + struct parts *parts = parts_get(parts_no); + struct parts_flash *f = parts_get_flash(parts, state); + if (parts_flash_load(parts, f, flash_filename)) { + parts_flash_seek(f, 1); + parts_dirty(parts); + return true; + } + return false; +} + +bool PE_IsPartsFlashEnd(int parts_no, int state) +{ + if (!parts_state_valid(--state)) + return false; + + struct parts *parts = parts_get(parts_no); + struct parts_flash *f = parts_get_flash(parts, state); + return f->swf && f->current_frame == f->swf->frame_count; +} + +int PE_GetPartsFlashCurrentFrameNumber(int parts_no, int state) +{ + if (!parts_state_valid(--state)) + return 0; + + struct parts *parts = parts_get(parts_no); + struct parts_flash *f = parts_get_flash(parts, state); + return f->current_frame; +} + +bool PE_BackPartsFlashBeginFrame(int parts_no, int state) +{ + if (!parts_state_valid(--state)) + return false; + + struct parts *parts = parts_get(parts_no); + struct parts_flash *f = parts_get_flash(parts, state); + if (!f->swf) + return false; + if (parts_flash_seek(f, 1)) + parts_dirty(parts); + f->elapsed = 0; + return true; +} + +bool PE_StepPartsFlashFinalFrame(int parts_no, int state) +{ + if (!parts_state_valid(--state)) + return false; + + struct parts *parts = parts_get(parts_no); + struct parts_flash *f = parts_get_flash(parts, state); + if (!f->swf) + return false; + if (parts_flash_seek(f, f->swf->frame_count)) + parts_dirty(parts); + f->elapsed = 0; + return true; +} + +bool PE_SetPartsFlashAndStop(int parts_no, struct string *flash_filename, int state) +{ + if (!parts_state_valid(--state)) + return false; + + struct parts *parts = parts_get(parts_no); + struct parts_flash *f = parts_get_flash(parts, state); + bool result = parts_flash_load(parts, f, flash_filename); + f->stopped = true; + return result; +} + +bool PE_StopPartsFlash(int parts_no, int state) +{ + if (!parts_state_valid(--state)) + return false; + + struct parts *parts = parts_get(parts_no); + struct parts_flash *f = parts_get_flash(parts, state); + if (!f->swf) + return false; + f->stopped = true; + return true; +} + +bool PE_StartPartsFlash(int parts_no, int state) +{ + if (!parts_state_valid(--state)) + return false; + + struct parts *parts = parts_get(parts_no); + struct parts_flash *f = parts_get_flash(parts, state); + if (!f->swf) + return false; + f->stopped = false; + return true; +} + +bool PE_GoFramePartsFlash(int parts_no, int frame_no, int state) +{ + if (!parts_state_valid(--state)) + return false; + + struct parts *parts = parts_get(parts_no); + struct parts_flash *f = parts_get_flash(parts, state); + if (!f->swf) + return false; + if (parts_flash_seek(f, frame_no)) + parts_dirty(parts); + f->elapsed = 0; + return true; +} + +int PE_GetPartsFlashEndFrame(int parts_no, int state) +{ + if (!parts_state_valid(--state)) + return 0; + + struct parts *parts = parts_get(parts_no); + struct parts_flash *f = parts_get_flash(parts, state); + if (!f->swf) + return 0; + return f->swf->frame_count; +} diff --git a/src/parts/parts.c b/src/parts/parts.c index 96ec265..6c4a0a4 100644 --- a/src/parts/parts.c +++ b/src/parts/parts.c @@ -170,6 +170,9 @@ static void parts_state_free(struct parts_state *state) parts_cp_op_free(op); } break; + case PARTS_FLASH: + parts_flash_free(&state->flash); + break; } memset(state, 0, sizeof(struct parts_state)); } @@ -213,6 +216,7 @@ void parts_state_reset(struct parts_state *state, enum parts_type type) case PARTS_UNINITIALIZED: case PARTS_CG: case PARTS_ANIMATION: + case PARTS_FLASH: break; } } @@ -273,6 +277,14 @@ struct parts_construction_process *parts_get_construction_process(struct parts * return &parts->states[state].cproc; } +struct parts_flash *parts_get_flash(struct parts *parts, int state) +{ + if (parts->states[state].type != PARTS_FLASH) { + parts_state_reset(&parts->states[state], PARTS_FLASH); + } + return &parts->states[state].flash; +} + static Point calculate_offset(int mode, int w, int h) { switch (mode) { @@ -652,6 +664,12 @@ void parts_set_surface_area(struct parts *parts, struct parts_common *common, in static void parts_update_loop(struct parts *parts, int passed_time) { + if (parts->states[parts->state].type == PARTS_FLASH) { + if (parts_flash_update(&parts->states[parts->state].flash, passed_time)) + parts_dirty(parts); + return; + } + if (parts->states[parts->state].type != PARTS_ANIMATION) return; @@ -1325,11 +1343,6 @@ bool PE_SetNumeralSurfaceArea(int parts_no, int x, int y, int w, int h, int stat } bool PE_SetPartsRectangleDetectionSize(int PartsNumber, int Width, int Height, int State); -bool PE_SetPartsFlash(int PartsNumber, struct string *pIFlashFileName, int State); -bool PE_IsPartsFlashEnd(int PartsNumber, int State); -int PE_GetPartsFlashCurrentFrameNumber(int PartsNumber, int State); -bool PE_BackPartsFlashBeginFrame(int PartsNumber, int State); -bool PE_StepPartsFlashFinalFrame(int PartsNumber, int State); void PE_ReleaseParts(int parts_no) { @@ -1546,9 +1559,3 @@ bool PE_IsExist(int parts_no) { return !!ht_get_int(parts_table, parts_no, NULL); } - -bool PE_SetPartsFlashAndStop(int PartsNumber, struct string *pIFlashFileName, int State); -bool PE_StopPartsFlash(int PartsNumber, int State); -bool PE_StartPartsFlash(int PartsNumber, int State); -bool PE_GoFramePartsFlash(int PartsNumber, int FrameNumber, int State); -int PE_GetPartsFlashEndFrame(int PartsNumber, int State); diff --git a/src/parts/parts_internal.h b/src/parts/parts_internal.h index a8dfba5..b57cfba 100644 --- a/src/parts/parts_internal.h +++ b/src/parts/parts_internal.h @@ -20,9 +20,11 @@ #include "gfx/gfx.h" #include "gfx/font.h" #include "queue.h" +#include "swf.h" typedef struct cJSON cJSON; struct string; +struct hash_table; // NOTE: actual value is +1 enum parts_state_type { @@ -96,7 +98,8 @@ enum parts_type { PARTS_HGAUGE, PARTS_VGAUGE, PARTS_CONSTRUCTION_PROCESS, -#define PARTS_NR_TYPES (PARTS_CONSTRUCTION_PROCESS+1) + PARTS_FLASH, +#define PARTS_NR_TYPES (PARTS_FLASH+1) }; struct parts_common { @@ -210,6 +213,28 @@ struct parts_construction_process { TAILQ_HEAD(, parts_cp_op) ops; }; +struct parts_flash_object { + TAILQ_ENTRY(parts_flash_object) entry; + uint16_t depth; + uint16_t character_id; + struct swf_matrix matrix; + struct swf_cxform_with_alpha color_transform; +}; + +struct parts_flash { + struct parts_common common; + struct string *name; + struct swf *swf; + + struct swf_tag *tag; + bool stopped; + unsigned elapsed; + int current_frame; + struct hash_table *dictionary; + struct hash_table *bitmaps; // bitmap character id -> struct texture * + TAILQ_HEAD(, parts_flash_object) display_list; +}; + struct parts_state { enum parts_type type; union { @@ -220,6 +245,7 @@ struct parts_state { struct parts_numeral num; struct parts_gauge gauge; struct parts_construction_process cproc; + struct parts_flash flash; }; }; @@ -276,6 +302,7 @@ struct parts_numeral *parts_get_numeral(struct parts *parts, int state); struct parts_gauge *parts_get_hgauge(struct parts *parts, int state); 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); void parts_set_pos(struct parts *parts, Point pos); void parts_set_dims(struct parts *parts, struct parts_common *common, int w, int h); void parts_set_scale_x(struct parts *parts, float mag); @@ -328,6 +355,12 @@ void parts_add_cp_op(struct parts_construction_process *cproc, struct parts_cp_o bool parts_build_construction_process(struct parts *parts, struct parts_construction_process *cproc); +// flash.c +void parts_flash_free(struct parts_flash *f); +bool parts_flash_load(struct parts *parts, struct parts_flash *f, struct string *filename); +bool parts_flash_update(struct parts_flash *f, int passed_time); +bool parts_flash_seek(struct parts_flash *f, int frame); + // debug.c void parts_debug_init(void); cJSON *parts_engine_to_json(bool verbose); diff --git a/src/parts/render.c b/src/parts/render.c index ccebf49..1f4cbf1 100644 --- a/src/parts/render.c +++ b/src/parts/render.c @@ -19,6 +19,7 @@ #include #include "system4.h" +#include "system4/hashtable.h" #include "gfx/gfx.h" #include "scene.h" @@ -49,6 +50,27 @@ static void parts_render_text(struct parts *parts, struct parts_common *common) gfx_render_texture(&parts->states[parts->state].common.texture, &rect); } +static void parts_render_texture(struct texture *texture, mat4 mw_transform, Rectangle *rect, float blend_rate, vec3 add_color, vec3 multiply_color) +{ + mat4 wv_transform = WV_TRANSFORM(config.view_width, config.view_height); + + struct gfx_render_job job = { + .shader = &parts_shader.shader, + .texture = texture->handle, + .world_transform = mw_transform[0], + .view_transform = wv_transform[0], + .data = texture, + }; + + gfx_prepare_job(&job); + glUniform1f(parts_shader.blend_rate, blend_rate); + glUniform2f(parts_shader.bot_left, rect->x, rect->y); + glUniform2f(parts_shader.top_right, rect->x + rect->w, rect->y + rect->h); + glUniform3fv(parts_shader.add_color, 1, add_color); + glUniform3fv(parts_shader.multiply_color, 1, multiply_color); + gfx_run_job(&job); +} + static void parts_render_cg(struct parts *parts, struct parts_common *common) { switch (parts->draw_filter) { @@ -68,34 +90,80 @@ static void parts_render_cg(struct parts *parts, struct parts_common *common) glm_scale(mw_transform, (vec3){ parts->global.scale.x, parts->global.scale.y, 1.0 }); glm_translate(mw_transform, (vec3){ common->origin_offset.x, common->origin_offset.y, 0 }); glm_scale(mw_transform, (vec3){ common->w, common->h, 1.0 }); - mat4 wv_transform = WV_TRANSFORM(config.view_width, config.view_height); - - struct gfx_render_job job = { - .shader = &parts_shader.shader, - .texture = common->texture.handle, - .world_transform = mw_transform[0], - .view_transform = wv_transform[0], - .data = &common->texture, - }; Rectangle r = common->surface_area; if (!r.w && !r.h) { r = (Rectangle) { 0, 0, common->texture.w, common->texture.h }; } - gfx_prepare_job(&job); - glUniform1f(parts_shader.blend_rate, parts->global.alpha / 255.0); - glUniform2f(parts_shader.bot_left, r.x, r.y); - glUniform2f(parts_shader.top_right, r.x + r.w, r.y + r.h); - glUniform3f(parts_shader.add_color, parts->global.add_color.r / 255.0f, - parts->global.add_color.g / 255.0f, parts->global.add_color.b / 255.0f); - glUniform3f(parts_shader.multiply_color, parts->global.multiply_color.r / 255.0f, - parts->global.multiply_color.g / 255.0f, parts->global.multiply_color.b / 255.0f); - gfx_run_job(&job); + vec3 add_color = { + parts->global.add_color.r / 255.0f, + parts->global.add_color.g / 255.0f, + parts->global.add_color.b / 255.0f + }; + vec3 multiply_color = { + parts->global.multiply_color.r / 255.0f, + parts->global.multiply_color.g / 255.0f, + parts->global.multiply_color.b / 255.0f, + }; + parts_render_texture(&common->texture, mw_transform, &r, parts->global.alpha / 255.0, add_color, multiply_color); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); } +static void parts_render_flash_shape(struct parts *parts, struct parts_flash *f, struct parts_flash_object *obj, struct swf_tag_define_shape *tag) +{ + struct texture *src = ht_get_int(f->bitmaps, tag->fill_style.bitmap_id, NULL); + if (!src) + ERROR("undefined bitmap id %d", tag->fill_style.bitmap_id); + + mat4 mw_transform = GLM_MAT4_IDENTITY_INIT; + glm_translate(mw_transform, (vec3) { parts->global.pos.x, parts->global.pos.y, 0 }); + glm_rotate_z(mw_transform, parts->local.rotation.z, mw_transform); + glm_scale(mw_transform, (vec3){ parts->global.scale.x, parts->global.scale.y, 1.0 }); + int x = f->common.origin_offset.x + (obj->matrix.translate_x + tag->bounds.x_min) / 20; + int y = f->common.origin_offset.y + (obj->matrix.translate_y + tag->bounds.y_min) / 20; + int width = (tag->bounds.x_max - tag->bounds.x_min) / 20 * (obj->matrix.scale_x / 65536.0f); + int height = (tag->bounds.y_max - tag->bounds.y_min) / 20 * (obj->matrix.scale_y / 65536.0f); + // TODO: consider matrix.roate_skew + glm_translate(mw_transform, (vec3){ x, y, 0 }); + glm_scale(mw_transform, (vec3){ width, height, 1.0 }); + + Rectangle r = { 0, 0, width, height }; + + float blend_rate = (parts->global.alpha / 255.0f) * (obj->color_transform.mult_terms[3] / 256.0f); + vec3 add_color = { + (parts->global.add_color.r + obj->color_transform.add_terms[0]) / 255.0f, + (parts->global.add_color.g + obj->color_transform.add_terms[1]) / 255.0f, + (parts->global.add_color.b + obj->color_transform.add_terms[2]) / 255.0f + }; + vec3 multiply_color = { + (parts->global.multiply_color.r / 255.0f) * (obj->color_transform.mult_terms[0] / 256.0f), + (parts->global.multiply_color.g / 255.0f) * (obj->color_transform.mult_terms[1] / 256.0f), + (parts->global.multiply_color.b / 255.0f) * (obj->color_transform.mult_terms[2] / 256.0f) + }; + parts_render_texture(src, mw_transform, &r, blend_rate, add_color, multiply_color); +} + +static void parts_render_flash(struct parts *parts, struct parts_flash *f) +{ + struct parts_flash_object *obj; + TAILQ_FOREACH(obj, &f->display_list, entry) { + struct swf_tag *tag = ht_get_int(f->dictionary, obj->character_id, NULL); + if (!tag) { + WARNING("character %d is not defined", obj->character_id); + continue; + } + switch (tag->type) { + case TAG_DEFINE_SHAPE: + parts_render_flash_shape(parts, f, obj, (struct swf_tag_define_shape *)tag); + break; + default: + ERROR("unknown tag 0x%x", tag->type); + } + } +} + void parts_render(struct parts *parts) { if (!parts->global.show) @@ -109,22 +177,25 @@ void parts_render(struct parts *parts) // render struct parts_state *state = &parts->states[parts->state]; - if (state->common.texture.handle) { - switch (state->type) { - case PARTS_UNINITIALIZED: - break; - case PARTS_CG: - case PARTS_ANIMATION: - case PARTS_NUMERAL: - case PARTS_HGAUGE: - case PARTS_VGAUGE: - case PARTS_CONSTRUCTION_PROCESS: + switch (state->type) { + case PARTS_UNINITIALIZED: + break; + case PARTS_CG: + case PARTS_ANIMATION: + case PARTS_NUMERAL: + case PARTS_HGAUGE: + case PARTS_VGAUGE: + case PARTS_CONSTRUCTION_PROCESS: + if (state->common.texture.handle) parts_render_cg(parts, &state->common); - break; - case PARTS_TEXT: + break; + case PARTS_TEXT: + if (state->common.texture.handle) parts_render_text(parts, &state->common); - break; - } + break; + case PARTS_FLASH: + parts_render_flash(parts, &state->flash); + break; } } diff --git a/src/parts/save.c b/src/parts/save.c index 0d17fa5..02f47e3 100644 --- a/src/parts/save.c +++ b/src/parts/save.c @@ -298,6 +298,23 @@ static void load_parts_construction_process(struct iarray_reader *r, struct part parts_build_construction_process(parts, cproc); } +static void save_parts_flash(struct iarray_writer *w, struct parts_flash *flash) +{ + iarray_write_string_or_null(w, flash->name); + iarray_write(w, flash->stopped); + iarray_write(w, flash->current_frame); +} + +static void load_parts_flash(struct iarray_reader *r, struct parts *parts, + struct parts_flash *flash) +{ + struct string *name = iarray_read_string_or_null(r); + parts_flash_load(parts, flash, name); + free_string(name); + flash->stopped = !!iarray_read(r); + parts_flash_seek(flash, iarray_read(r)); +} + static void save_parts_state(struct iarray_writer *w, struct parts_state *state) { iarray_write(w, state->type); @@ -328,6 +345,9 @@ static void save_parts_state(struct iarray_writer *w, struct parts_state *state) case PARTS_CONSTRUCTION_PROCESS: save_parts_construction_process(w, &state->cproc); break; + case PARTS_FLASH: + save_parts_flash(w, &state->flash); + break; } } @@ -364,6 +384,9 @@ static void load_parts_state(struct iarray_reader *r, struct parts *parts, case PARTS_CONSTRUCTION_PROCESS: load_parts_construction_process(r, parts, &state->cproc); break; + case PARTS_FLASH: + load_parts_flash(r, parts, &state->flash); + break; } }