Skip to content

Commit

Permalink
FF8: Add Triple Triad and draw spell texts to exe data feature (#774)
Browse files Browse the repository at this point in the history
  • Loading branch information
myst6re authored Jan 20, 2025
1 parent f9cb1dd commit 8948bba
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

## FF8

- Modding: Allow modding card texts hardcoded in exe ( https://github.com/julianxhokaxhiu/FFNx/pull/774 )
- Modding: Allow modding draw texts hardcoded in exe ( https://github.com/julianxhokaxhiu/FFNx/pull/774 )
- SFX: Fix incorrect volume assignment when playing sfx effects using the external layer

# 1.21.3
Expand Down
4 changes: 4 additions & 0 deletions docs/mods/exe_data.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ And then FFNx will look for those files directly instead of data from the EXE.
- `card_names.msd`: Card names. Note: this is not exactly the same
format in the EXE, the msd format is used because it is a well documented format
of FF8.
- `card_texts.msd`: Card module texts. Note: this is not exactly the same
format in the EXE, the msd format is used because it is a well documented format
of FF8.
- `draw_point.msd`: Draw point and Disc error messages
174 changes: 173 additions & 1 deletion src/exe_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

uint8_t *ff8_exe_scan_texts = nullptr;
uint8_t *ff8_exe_card_names = nullptr;
uint8_t *ff8_exe_draw_point = nullptr;
uint8_t *ff8_exe_card_texts = nullptr;

bool ff8_get_exe_path(const char *name, char *target_filename)
{
Expand All @@ -46,6 +48,46 @@ bool ff8_get_card_names_filename(char *filename)
return ff8_get_exe_path("card_names", filename);
}

bool ff8_get_draw_point_filename(char *filename)
{
return ff8_get_exe_path("draw_point", filename);
}

bool ff8_get_card_texts_filename(char *filename)
{
return ff8_get_exe_path("card_texts", filename);
}

void ff8_dump_msd(const char *filename, uint8_t *data)
{
uint32_t *offsets = (uint32_t *)data;
uint32_t first_offset = *offsets;
int text_count = first_offset / 4;
int higher_offset = 0;

for (int i = 0; i < text_count; ++i) {
if (offsets[i] > higher_offset) {
higher_offset = offsets[i];
}
}

for (int i = higher_offset; i < higher_offset + 1024; ++i) {
if (data[i] == '\0') {
higher_offset = i + 1;
break;
}
}

FILE *f = fopen(filename, "wb");

if (f == nullptr) {
return;
}

fwrite(data, first_offset + higher_offset, 1, f);
fclose(f);
}

void ff8_dump_battle_scan_texts()
{
uint8_t *data = (uint8_t *)ff8_externals.scan_text_data;
Expand Down Expand Up @@ -128,10 +170,55 @@ void ff8_dump_card_names()
fclose(f);
}

uint8_t *ff8_open_msd(char *filename)
void ff8_dump_card_texts()
{
uint8_t *data = *ff8_externals.card_texts_off_B96968;
uint16_t *offsets = (uint16_t *)(data + 2);
uint32_t first_offset = *offsets;
int text_count = first_offset / 4;
int higher_offset = 0;
uint32_t offsets_rel_to_start[0x200] = {};

for (int i = 0; i < text_count; ++i) {
offsets_rel_to_start[i] = offsets[i * 2];
if (offsets[i * 2] > higher_offset) {
higher_offset = offsets[i * 2];
}
}

for (int i = higher_offset; i < higher_offset + 1024; ++i) {
if (data[i] == '\0') {
higher_offset = i + 1;
break;
}
}

char filename[MAX_PATH] = {};
if (ff8_get_card_texts_filename(filename)) {
ffnx_warning("Save exe file skipped because the file [ %s ] already exists.\n", filename);

return;
}

FILE *f = fopen(filename, "wb");

if (f == nullptr) {
return;
}

fwrite(offsets_rel_to_start, text_count * 4, 1, f);
fwrite(data + first_offset, higher_offset - first_offset, 1, f);
fclose(f);
}

uint8_t *ff8_open_msd(char *filename, int *data_size = nullptr)
{
if (trace_all || trace_direct) ffnx_info("Direct file using %s\n", filename);

if (data_size != nullptr) {
*data_size = 0;
}

FILE *f = fopen(filename, "rb");

if (f == nullptr) {
Expand All @@ -151,6 +238,10 @@ uint8_t *ff8_open_msd(char *filename)
fread(target_data, file_size, 1, f);
fclose(f);

if (data_size != nullptr) {
*data_size = file_size;
}

return target_data;
}

Expand Down Expand Up @@ -190,6 +281,72 @@ uint8_t *ff8_override_card_names()
return ff8_exe_card_names;
}

uint8_t *ff8_override_draw_point()
{
if (ff8_exe_draw_point != nullptr) {
return ff8_exe_draw_point;
}

char filename[MAX_PATH] = {};
if (! ff8_get_draw_point_filename(filename)) {
if (trace_all || trace_direct) ffnx_warning("Direct file not found %s\n", filename);

return nullptr;
}

ff8_exe_draw_point = ff8_open_msd(filename);

return ff8_exe_draw_point;
}

uint8_t *ff8_override_card_texts()
{
if (ff8_exe_card_texts != nullptr) {
return ff8_exe_card_texts;
}

char filename[MAX_PATH] = {};
if (! ff8_get_card_texts_filename(filename)) {
if (trace_all || trace_direct) ffnx_warning("Direct file not found %s\n", filename);

return nullptr;
}

int data_size = 0;
uint8_t *ff8_exe_card_texts_msd = ff8_open_msd(filename, &data_size);

if (ff8_exe_card_texts_msd == nullptr) {
return nullptr;
}

ff8_exe_card_texts = (uint8_t *)driver_malloc(data_size + 16); // Allocated once, never freed

if (ff8_exe_card_texts == nullptr) {
driver_free(ff8_exe_card_texts_msd);

return nullptr;
}

uint32_t *msd_offsets = (uint32_t *)ff8_exe_card_texts_msd;
*(uint16_t *)ff8_exe_card_texts = *(uint16_t *)*ff8_externals.card_texts_off_B96968;
int text_count = 256;

for (int i = 0; i < text_count; ++i) {
if (msd_offsets[i] / 4 < text_count) {
text_count = msd_offsets[i] / 4;
}
}

// We shift the positions (+2)
memcpy(ff8_exe_card_texts + 2, ff8_exe_card_texts_msd, text_count * 4);
// But not the texts!
memcpy(ff8_exe_card_texts + text_count * 4, ff8_exe_card_texts_msd + text_count * 4, data_size - text_count * 4);

driver_free(ff8_exe_card_texts_msd);

return ff8_exe_card_texts;
}

uint8_t *ff8_battle_get_scan_text(uint8_t target_id)
{
uint8_t *direct_data_msd = ff8_override_battle_scans();
Expand Down Expand Up @@ -238,6 +395,12 @@ void dump_exe_data()
{
ff8_dump_battle_scan_texts();
ff8_dump_card_names();

if (!ff8_get_draw_point_filename(dirname)) {
ff8_dump_msd(dirname, *(uint8_t **)ff8_externals.drawpoint_messages);
}

ff8_dump_card_texts();
}
}

Expand All @@ -252,5 +415,14 @@ void exe_data_init()
{
replace_call(ff8_externals.sub_84F8D0 + 0x88, ff8_battle_get_scan_text);
replace_function(ff8_externals.get_card_name, ff8_get_card_name);
uint8_t *msd = ff8_override_draw_point();
if (msd != nullptr) {
patch_code_uint(ff8_externals.drawpoint_messages, uint32_t(msd));
}
msd = ff8_override_card_texts();
if (msd != nullptr) {
patch_code_uint(uint32_t(ff8_externals.card_texts_off_B96504), uint32_t(msd));
patch_code_uint(uint32_t(ff8_externals.card_texts_off_B96968), uint32_t(msd));
}
}
}
5 changes: 5 additions & 0 deletions src/ff8.h
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,8 @@ struct ff8_externals
uint32_t sub_4BDB30;
ff8_menu_callback *menu_callbacks;
uint32_t menu_cards_render;
uint32_t sub_534AD0;
uint8_t **card_texts_off_B96504;
uint32_t sub_4EFC00;
uint32_t sub_4EFCD0;
uint32_t menu_config_controller;
Expand Down Expand Up @@ -1412,6 +1414,8 @@ struct ff8_externals
uint32_t sub_534560;
uint32_t sub_536C30;
uint32_t sub_535640;
uint32_t sub_536CB0;
uint8_t **card_texts_off_B96968;
uint32_t sub_536C80;
uint32_t sub_5366D0;
uint32_t sub_539500;
Expand Down Expand Up @@ -1532,6 +1536,7 @@ struct ff8_externals
uint32_t field_vars_stack_1CFE9B8;
uint32_t get_card_name;
uint32_t card_name_positions;
uint32_t drawpoint_messages;
};

void ff8gl_field_78(struct ff8_polygon_set *polygon_set, struct ff8_game_obj *game_object);
Expand Down
6 changes: 6 additions & 0 deletions src/ff8_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ void ff8_find_externals()
ff8_externals.cardgame_funcs = (uint32_t *)get_absolute_value(ff8_externals.sub_534560, 0x5D);
ff8_externals.sub_536C30 = ff8_externals.cardgame_funcs[1];
ff8_externals.sub_535640 = ff8_externals.cardgame_funcs[3];
ff8_externals.sub_536CB0 = get_absolute_value(ff8_externals.sub_536C30, 0x14);
ff8_externals.card_texts_off_B96968 = (uint8_t **)get_absolute_value(ff8_externals.sub_536CB0, 0x59);
ff8_externals.sub_536C80 = get_absolute_value(ff8_externals.sub_536C30, 0x25);
ff8_externals.sub_5366D0 = get_absolute_value(ff8_externals.sub_535640, 0x42);
ff8_externals.cardgame_tim_texture_intro = (uint8_t *)get_absolute_value(ff8_externals.sub_536C80, 0x3);
Expand Down Expand Up @@ -317,6 +319,8 @@ void ff8_find_externals()
common_externals.stop_movie = get_relative_call(common_externals.update_movie_sample, 0x3E2);
ff8_externals.movie_object = (ff8_movie_obj *)get_absolute_value(common_externals.prepare_movie, 0xDB);

ff8_externals.drawpoint_messages = get_absolute_value(ff8_externals.opcode_drawpoint, 0xD6);

common_externals.debug_print = get_relative_call(common_externals.update_movie_sample, 0x141);

ff8_externals._load_texture = get_relative_call(ff8_externals.load_fonts, JP_VERSION ? 0x31B : 0x197);
Expand Down Expand Up @@ -412,6 +416,8 @@ void ff8_find_externals()
ff8_externals.sub_4BDB30 = get_relative_call(ff8_externals.sub_4B3140, 0x4);
ff8_externals.menu_callbacks = (ff8_menu_callback *)get_absolute_value(ff8_externals.sub_4BDB30, 0x11);
ff8_externals.menu_cards_render = get_absolute_value(uint32_t(ff8_externals.menu_callbacks[7].func), 0x5);
ff8_externals.sub_534AD0 = get_relative_call(ff8_externals.menu_cards_render, 0x76);
ff8_externals.card_texts_off_B96504 = (uint8_t **)get_absolute_value(ff8_externals.sub_534AD0, 0xB1);
ff8_externals.sub_4EFC00 = get_relative_call(ff8_externals.menu_cards_render, 0x2B6);
ff8_externals.sub_4EFCD0 = get_absolute_value(ff8_externals.sub_4EFC00, 0xB0);
ff8_externals.menu_config_render = get_absolute_value(uint32_t(ff8_externals.menu_callbacks[8].func), 0x3);
Expand Down

0 comments on commit 8948bba

Please sign in to comment.