Skip to content

Commit

Permalink
FF8: World chara.one textures replacement (#615)
Browse files Browse the repository at this point in the history
  • Loading branch information
myst6re committed Oct 3, 2023
1 parent 1687b07 commit 57669ed
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 4 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- Graphics: Fix crash when using external texture replacement ( https://github.com/julianxhokaxhiu/FFNx/pull/588 )
- Graphics: Fix texture glitches using external texture replacement ( https://github.com/julianxhokaxhiu/FFNx/pull/591 )
- Graphics: Fix external texture blending ( https://github.com/julianxhokaxhiu/FFNx/pull/598 https://github.com/julianxhokaxhiu/FFNx/pull/601 )
- Graphics: Add chara.one worldmap texture replacement ( https://github.com/julianxhokaxhiu/FFNx/pull/615 )
- Graphics: Increase max texture size to 16384 for external textures ( https://github.com/julianxhokaxhiu/FFNx/pull/601 )
- Music: Add `ff8_external_music_force_original_filenames` option to use original music names (eg 018s-julia.ogg) instead of just the main identifier in external music ( https://github.com/julianxhokaxhiu/FFNx/pull/594 )
- Voice: Enable battle dialogs voice acting
Expand Down
4 changes: 4 additions & 0 deletions src/ff8.h
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,8 @@ struct ff8_externals
uint32_t load_field_models;
uint32_t chara_one_read_file;
uint32_t chara_one_seek_file;
uint32_t chara_one_set_data_start;
uint8_t **chara_one_data_start;
uint32_t chara_one_upload_texture;
uint32_t worldmap_main_loop;
uint32_t worldmap_enter_main;
Expand All @@ -1071,6 +1073,8 @@ struct ff8_externals
uint32_t worldmap_sub_541970_upload_tim;
uint32_t worldmap_sub_548020;
uint32_t worldmap_sub_5531F0;
uint32_t worldmap_sub_545E20;
uint32_t worldmap_chara_one;
int32_t (*open_file_world)(const char*, int32_t, uint32_t, void *);
uint32_t open_file_world_sub_52D670_texl_call1;
uint32_t open_file_world_sub_52D670_texl_call2;
Expand Down
8 changes: 4 additions & 4 deletions src/ff8/field/chara_one.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ std::unordered_map<uint32_t, CharaOneModel> ff8_chara_one_parse_models(const uin
CharaOneModel model = CharaOneModel();

if (flag >> 24 != 0xd0) { // NPCs (not main characters)
uint32_t timOffset;
uint32_t tim_offset;

if (trace_all) ffnx_info("%s: %d %d %d\n", __func__, i, int(cur - chara_one_data), size);

Expand All @@ -70,14 +70,14 @@ std::unordered_map<uint32_t, CharaOneModel> ff8_chara_one_parse_models(const uin
}

while (cur - chara_one_data < size) {
memcpy(&timOffset, cur, 4);
memcpy(&tim_offset, cur, 4);
cur += 4;

if (timOffset == 0xFFFFFFFF) {
if (tim_offset == 0xFFFFFFFF) {
break;
}

model.texturesData.push_back(timOffset & 0xFFFFFF);
model.texturesData.push_back(tim_offset & 0xFFFFFF);
}
} else {
model.isMch = true;
Expand Down
48 changes: 48 additions & 0 deletions src/ff8/vram.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "../utils.h"
#include "field/background.h"
#include "field/chara_one.h"
#include "world/chara_one.h"
#include "battle/stage.h"
#include "file.h"

Expand Down Expand Up @@ -57,6 +58,9 @@ int chara_one_current_pos = 0;
uint32_t chara_one_current_model = 0;
uint32_t chara_one_current_mch = 0;
uint32_t chara_one_current_texture = 0;
// World
std::vector<CharaOneModelTextures> chara_one_world_texture_offsets;
uint8_t *chara_one_world_data;
// Battle
char battle_texture_name[MAX_PATH] = "";
int battle_texture_id = 0;
Expand Down Expand Up @@ -386,6 +390,48 @@ void ff8_wm_section_42_upload(uint8_t *tim_file_data)
((void(*)(uint8_t*))ff8_externals.worldmap_sub_541970_upload_tim)(tim_file_data);
}

int ff8_wm_chara_one_read_file(int fd, uint8_t *data, size_t size)
{
if (trace_all || trace_vram) ffnx_trace("%s\n", __func__);

int read = ((int(*)(int, uint8_t *, size_t))ff8_externals.chara_one_read_file)(fd, data, size);

chara_one_world_texture_offsets = ff8_world_chara_one_parse_models(data, read);
chara_one_world_data = data;

if (save_textures) {
char filename[MAX_PATH];
snprintf(filename, sizeof(filename), "world/esk/chara_one/");
ff8_world_chara_one_model_save_textures(chara_one_world_texture_offsets, data, filename);
}

return read;
}

int ff8_wm_chara_one_upload_texture_2(char *image_buffer, char bpp, char a3, int x, int16_t y, int w, int16_t h)
{
if (trace_all || trace_vram) ffnx_trace("%s\n", __func__);

int chara_one_offset = int(*(ff8_externals.chara_one_data_start) - chara_one_world_data);
int model_id = 0;
for (const CharaOneModelTextures &textures: chara_one_world_texture_offsets) {
int texture_id = 0;
for (const uint32_t texture_offset: textures) {
if (texture_offset == chara_one_offset) {
next_bpp = Tim::Bpp(bpp);
snprintf(next_texture_name, MAX_PATH, "world/esk/chara_one/model%d-%d", model_id, texture_id);

return ((int(*)(char *, char, char, int, __int16, int, __int16))ff8_externals.chara_one_upload_texture)(image_buffer, bpp, a3, x, y, w, h);
}
++texture_id;
}

++model_id;
}

return ((int(*)(char *, char, char, int, __int16, int, __int16))ff8_externals.chara_one_upload_texture)(image_buffer, bpp, a3, x, y, w, h);
}

int ff8_wm_open_data(const char *path, int32_t pos, uint32_t size, void *data)
{
if (strstr(path, "texl.obj") != nullptr)
Expand Down Expand Up @@ -777,6 +823,8 @@ void vram_init()
replace_call(ff8_externals.worldmap_sub_53F310_call_330, ff8_wm_section_39_upload); // Rails/Roads
replace_call(ff8_externals.worldmap_sub_53F310_call_366, ff8_wm_section_40_upload);
replace_call(ff8_externals.worldmap_sub_548020 + 0x47, ff8_wm_section_42_upload); // Train/vehicles
replace_call(ff8_externals.worldmap_chara_one + 0xCC, ff8_wm_chara_one_read_file);
replace_call(ff8_externals.worldmap_chara_one + 0x4D1, ff8_wm_chara_one_upload_texture_2); // Characters/Chocobos/Ragnarok
// wm texl project
replace_call(ff8_externals.upload_psxvram_texl_pal_call1, ff8_wm_texl_palette_upload_vram);
replace_call(ff8_externals.upload_psxvram_texl_pal_call2, ff8_wm_texl_palette_upload_vram);
Expand Down
85 changes: 85 additions & 0 deletions src/ff8/world/chara_one.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/****************************************************************************/
// Copyright (C) 2009 Aali132 //
// Copyright (C) 2018 quantumpencil //
// Copyright (C) 2018 Maxime Bacoux //
// Copyright (C) 2020 Chris Rizzitello //
// Copyright (C) 2020 John Pritchard //
// Copyright (C) 2023 myst6re //
// Copyright (C) 2023 Julian Xhokaxhiu //
// Copyright (C) 2023 Cosmos //
// Copyright (C) 2023 Tang-Tang Zhou //
// //
// This file is part of FFNx //
// //
// FFNx 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 3 of the License //
// //
// FFNx 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. //
/****************************************************************************/

#include "chara_one.h"
#include "../../image/tim.h"
#include "../../saveload.h"
#include "../../log.h"

std::vector<CharaOneModelTextures> ff8_world_chara_one_parse_models(const uint8_t *chara_one_data, size_t size)
{
std::vector<CharaOneModelTextures> models;

const uint8_t *cur = chara_one_data + size, *min_cur = chara_one_data + 4;

// The header is at the end of the file
uint32_t count;
cur -= 4;
memcpy(&count, cur, 4);

for (uint32_t i = 0; i < count && cur >= min_cur; ++i) {
CharaOneModelTextures textures;

uint32_t tim_offset;

while (cur >= min_cur) {
cur -= 4;
memcpy(&tim_offset, cur, 4);

if (tim_offset == 0xFFFFFFFF) {
break;
}

textures.push_back(tim_offset & 0xFFFFFFF);
}

models.push_back(textures);

cur -= 4; // Skipping offset to the model data
}

return models;
}

bool ff8_world_chara_one_model_save_textures(const std::vector<CharaOneModelTextures> &models, const uint8_t *chara_one_model_data, const char *dirname)
{
if (trace_all || trace_vram) ffnx_trace("%s: %s\n", __func__, dirname);

int model_id = 0;
for (const CharaOneModelTextures &textures: models) {
int texture_id = 0;
for (uint32_t texture_pointer: textures) {
char name[MAX_PATH] = {};
snprintf(name, sizeof(name), "%s/model%d-%d", dirname, model_id, texture_id);
Tim tim = Tim::fromTimData(chara_one_model_data + texture_pointer);

if (!tim.save(name, 0, 0, true)) {
return false;
}
++texture_id;
}
++model_id;
}

return true;
}
33 changes: 33 additions & 0 deletions src/ff8/world/chara_one.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/****************************************************************************/
// Copyright (C) 2009 Aali132 //
// Copyright (C) 2018 quantumpencil //
// Copyright (C) 2018 Maxime Bacoux //
// Copyright (C) 2020 Chris Rizzitello //
// Copyright (C) 2020 John Pritchard //
// Copyright (C) 2023 myst6re //
// Copyright (C) 2023 Julian Xhokaxhiu //
// Copyright (C) 2023 Cosmos //
// Copyright (C) 2023 Tang-Tang Zhou //
// //
// This file is part of FFNx //
// //
// FFNx 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 3 of the License //
// //
// FFNx 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. //
/****************************************************************************/

#pragma once

#include "../../common.h"

#include <vector>

typedef std::vector<uint32_t> CharaOneModelTextures;

std::vector<CharaOneModelTextures> ff8_world_chara_one_parse_models(const uint8_t *chara_one_data, size_t size);
bool ff8_world_chara_one_model_save_textures(const std::vector<CharaOneModelTextures> &models, const uint8_t *chara_one_model_data, const char *dirname);
6 changes: 6 additions & 0 deletions src/ff8_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,8 @@ void ff8_find_externals()
ff8_externals.load_field_models = get_relative_call(ff8_externals.read_field_data, JP_VERSION ? 0xFA2 : 0xF0F);
ff8_externals.chara_one_read_file = get_relative_call(ff8_externals.load_field_models, 0x15F);
ff8_externals.chara_one_seek_file = get_relative_call(ff8_externals.load_field_models, 0x582);
ff8_externals.chara_one_set_data_start = get_relative_call(ff8_externals.load_field_models, 0xAFF);
ff8_externals.chara_one_data_start = (uint8_t **)get_absolute_value(ff8_externals.chara_one_set_data_start, 0x5);
ff8_externals.chara_one_upload_texture = get_relative_call(ff8_externals.load_field_models, 0xB72);

ff8_externals.worldmap_sub_53F310 = get_relative_call(ff8_externals.worldmap_enter_main, 0xA7);
Expand Down Expand Up @@ -516,6 +518,8 @@ void ff8_find_externals()
ff8_externals.worldmap_sub_53F310_call_366 = ff8_externals.worldmap_sub_53F310 + 0x366;
ff8_externals.worldmap_sub_53F310_loc_53F7EE = ff8_externals.worldmap_sub_53F310 + 0x4DE;
ff8_externals.worldmap_sub_541970_upload_tim = get_relative_call(ff8_externals.worldmap_sub_53F310, 0x330);
ff8_externals.worldmap_sub_545E20 = get_relative_call(ff8_externals.worldmap_sub_53F310, 0x60C);
ff8_externals.worldmap_chara_one = get_relative_call(ff8_externals.worldmap_sub_545E20, 0x68);
ff8_externals.worldmap_sub_5531F0 = get_relative_call(ff8_externals.worldmap_sub_53F310, 0x614);
ff8_externals.open_file_world = (int32_t(*)(const char*, int32_t, uint32_t, void *))get_relative_call(ff8_externals.worldmap_sub_5531F0, 0x395);
ff8_externals.open_file_world_sub_52D670_texl_call1 = ff8_externals.worldmap_sub_5531F0 + 0x395;
Expand Down Expand Up @@ -572,6 +576,8 @@ void ff8_find_externals()
ff8_externals.worldmap_sub_53F310_call_366 = ff8_externals.worldmap_sub_53F310 + 0x382;
ff8_externals.worldmap_sub_53F310_loc_53F7EE = ff8_externals.worldmap_sub_53F310 + 0x507;
ff8_externals.worldmap_sub_541970_upload_tim = get_relative_call(ff8_externals.worldmap_sub_53F310, 0x34C);
ff8_externals.worldmap_sub_545E20 = get_relative_call(ff8_externals.worldmap_sub_53F310, 0x635);
ff8_externals.worldmap_chara_one = get_relative_call(ff8_externals.worldmap_sub_545E20, 0x6B);
ff8_externals.worldmap_sub_5531F0 = get_relative_call(ff8_externals.worldmap_sub_53F310, 0x63D);
ff8_externals.open_file_world = (int32_t(*)(const char*, int32_t, uint32_t, void *))get_relative_call(ff8_externals.worldmap_sub_5531F0, 0x38F);
ff8_externals.open_file_world_sub_52D670_texl_call1 = ff8_externals.worldmap_sub_5531F0 + 0x38F;
Expand Down

0 comments on commit 57669ed

Please sign in to comment.