From 8d01dc8cc6c698cb925cc0ff2a488763f31a1977 Mon Sep 17 00:00:00 2001 From: myst6re Date: Sat, 27 Jan 2024 15:47:17 +0100 Subject: [PATCH] FF8: Allow more external texture replacement for battle effects --- src/ff8.h | 16 +- src/ff8/battle/effects.h | 38 +++++ src/ff8/vram.cpp | 325 +++++++++++++++++++++++++++++++++++++-- src/ff8_data.cpp | 21 ++- src/image/tim.cpp | 46 ++++-- 5 files changed, 421 insertions(+), 25 deletions(-) create mode 100644 src/ff8/battle/effects.h diff --git a/src/ff8.h b/src/ff8.h index 3b6ac7b2..7060b64f 100644 --- a/src/ff8.h +++ b/src/ff8.h @@ -1455,8 +1455,9 @@ struct ff8_externals uint32_t sub_50A9A0; uint32_t battle_read_effect_sub_50AF20; DWORD* func_off_battle_effects_C81774; + int* battle_magic_id; + uint32_t sub_571870; DWORD* func_off_battle_effect_textures_50AF93; - uint32_t battle_effect_quezacotl_sub_6C3550; uint32_t sub_6C3640; uint32_t sub_6C3760; uint8_t **vibrate_data_summon_quezacotl; @@ -1464,6 +1465,7 @@ struct ff8_externals uint32_t load_magic_data_sub_5718E0; uint32_t load_magic_data_sub_571900; uint32_t sub_47D890; + uint32_t sub_505DF0; uint32_t sub_4A94D0; uint32_t sub_4BCBE0; uint32_t sub_4C8B10; @@ -1473,6 +1475,18 @@ struct ff8_externals void *battle_menu_state; uint32_t battle_pause_window_sub_4CD350; uint32_t sub_4A7210; + uint32_t sub_B586F0; + uint32_t sub_B64B80; + DWORD* leviathan_funcs_B64C3C; + uint32_t mag_data_palette_sub_B66560; + DWORD** effect_struct_27973EC; + uint8_t** mag_data_dword_2798A68; + DWORD** effect_struct_2797624; + uint32_t sub_B63230; + uint32_t mag_data_texture_sub_B66560; + BYTE** dword_27973E8; + uint32_t battle_set_action_upload_raw_palette_sub_B666F0; + uint32_t battle_set_action_upload_raw_palette_sub_B66400; }; void ff8gl_field_78(struct ff8_polygon_set *polygon_set, struct ff8_game_obj *game_object); diff --git a/src/ff8/battle/effects.h b/src/ff8/battle/effects.h new file mode 100644 index 00000000..b635402d --- /dev/null +++ b/src/ff8/battle/effects.h @@ -0,0 +1,38 @@ +/****************************************************************************/ +// 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) 2024 myst6re // +// Copyright (C) 2024 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 + +namespace FF8BattleEffect { + enum Effect { + Leviathan = 5, + Quezacotl = 115 + }; +} + +namespace FF8BattleEffectOpcode { + enum Opcode { + UploadTexture39 = 39, + UploadPalette75 = 75 + }; +} diff --git a/src/ff8/vram.cpp b/src/ff8/vram.cpp index 5f62a375..b38797bd 100644 --- a/src/ff8/vram.cpp +++ b/src/ff8/vram.cpp @@ -71,6 +71,24 @@ std::unordered_map wm_wmset_palette_animations_ char battle_texture_name[MAX_PATH] = ""; int battle_texture_id = 0; Stage stage; +struct BattleTextureHeader { + uint8_t effect_id; + uint8_t file_id; + uint8_t is_palette; + uint8_t file_relative_id; + int16_t x, y; + int16_t w, h; + uint8_t *data; +}; +constexpr int battle_texture_data_list_size = 32; +std::vector battle_texture_headers = std::vector(battle_texture_data_list_size); +int battle_texture_headers_cursor = 0; +struct BattleTextureFileName { + char file_name[32]; + void *data; +}; +std::vector battle_texture_data_list = std::vector(battle_texture_data_list_size); +int battle_texture_data_list_cursor = 0; uint8_t *ff8_vram_seek(int xBpp2, int y) { @@ -256,6 +274,23 @@ int read_vram_to_buffer_with_palette1_parent_call2(texture_page *tex_page, int r return ret; } +int read_vram_to_buffer_with_palette2_parent_call(int a1, uint16_t rel_pos, int16_t palette_pos_related, uint8_t x, uint8_t y, int16_t w, int16_t h) +{ + uint8_t color_key = (rel_pos >> 7) & 3; + + if (trace_all || trace_vram) ffnx_trace("%s: x=%d y=%d w=%d h=%d color_key=%d rel_pos=(%d, %d)\n", __func__, x, y, w, h, color_key, rel_pos & 0xF, (rel_pos >> 4) & 1); + + next_psxvram_x = (x >> (2 - color_key)) + ((rel_pos & 0xF) << 6); + next_psxvram_y = y + (((rel_pos >> 4) & 1) << 8); + + int ret = ((int(*)(int, uint16_t, int16_t, uint8_t, uint8_t, int16_t, int16_t))ff8_externals.sub_4649A0)(a1, rel_pos, palette_pos_related, x, y, w, h); + + next_psxvram_x = -1; + next_psxvram_y = -1; + + return ret; +} + void read_vram_to_buffer(uint8_t *vram, int vram_w_2048, uint8_t *target, int target_w, signed int w, int h, int bpp) { if (trace_all || trace_vram) ffnx_trace("%s: vram_pos=(%d, %d) target=0x%X target_w=%d w=%d h=%d bpp=%d\n", __func__, next_psxvram_x, next_psxvram_y, int(target), target_w, w, h, bpp); @@ -1042,11 +1077,51 @@ void ff8_field_effects_upload_vram1(int16_t *pos_and_size, uint8_t *texture_buff texturePacker.setTexture(texture_name, TexturePacker::TextureInfos(pos_and_size[0], pos_and_size[1], pos_and_size[2], pos_and_size[3] * 2, bpp)); } +int battle_get_texture_file_name_index(void *texture_buffer) +{ + for (int i = 0; i < battle_texture_data_list_size; ++i) { + if (battle_texture_data_list.at(i).data == texture_buffer) { + return i; + } + } + + return -1; +} + int16_t ff8_battle_open_and_read_file(int fileId, void *data, int a3, int callback) { - if (trace_all || trace_vram) ffnx_trace("%s: %d\n", __func__, fileId); + if (trace_all || trace_vram) ffnx_trace("%s: %d => %s\n", __func__, fileId, ff8_externals.battle_filenames[fileId]); + + battle_texture_id = 0; + if (stricmp(ff8_externals.battle_filenames[fileId], "B0WAVE.DAT") == 0) { + // Fix redundant texture + strncpy(battle_texture_name, "battle/A8DEF.TIM", sizeof(battle_texture_name)); + + if (trace_all || trace_vram) ffnx_trace("%s: rename texture to %s\n", __func__, battle_texture_name); + } else { + snprintf(battle_texture_name, sizeof(battle_texture_name), "battle/%s", ff8_externals.battle_filenames[fileId]); + } + + // We already know that we have only one texture in those files + if (StrStrIA(battle_texture_name, ".TIM") != nullptr) { + battle_texture_id = -1; + } + + // Remember texture name for later + BattleTextureFileName tex = BattleTextureFileName(); + strncpy(tex.file_name, battle_texture_name, sizeof(tex.file_name)); + tex.data = data; - snprintf(battle_texture_name, sizeof(battle_texture_name), "battle/%s", ff8_externals.battle_filenames[fileId]); + int index = battle_get_texture_file_name_index(data); + if (index < 0) { + battle_texture_data_list[battle_texture_data_list_cursor] = tex; + + battle_texture_data_list_cursor = (battle_texture_data_list_cursor + 1) % battle_texture_data_list_size; + } else { + if (trace_all || trace_vram) ffnx_trace("%s: texture found in the list at index %d\n", __func__, index); + + battle_texture_data_list[index] = tex; + } return ((int16_t(*)(int,void*,int,int))ff8_externals.battle_open_file)(fileId, data, a3, callback); } @@ -1055,6 +1130,7 @@ void *ff8_battle_open_effect(const char *fileName, void *data, int dataSize, DWO { if (trace_all || trace_vram) ffnx_trace("%s: %s\n", __func__, fileName); + battle_texture_id = -1; snprintf(battle_texture_name, sizeof(battle_texture_name), "magic/%s", fileName); return ((void *(*)(const char*,void*,int,DWORD*))ff8_externals.load_magic_data_sub_571900)(fileName, data, dataSize, outSize); @@ -1064,8 +1140,6 @@ size_t ff8_battle_read_file(char *fileName, void *data) { if (trace_all || trace_vram) ffnx_trace("%s: %s\n", __func__, fileName); - battle_texture_id = 0; - size_t file_size = ff8_externals.sm_pc_read(fileName, data); if (save_textures && StrStrIA(fileName, ".X") != nullptr) { @@ -1075,19 +1149,242 @@ size_t ff8_battle_read_file(char *fileName, void *data) return file_size; } +int battle_get_texture_header_index(uint8_t *texture_buffer) +{ + for (int i = 0; i < battle_texture_data_list_size; ++i) { + if (battle_texture_headers.at(i).data == texture_buffer) { + return i; + } + } + + return -1; +} + +void battle_read_effect_alloc() +{ + if (trace_all || trace_vram) ffnx_trace("%s: magic_id=%d\n", __func__, *ff8_externals.battle_magic_id); + + // Reset list + for (int i = 0; i < battle_texture_headers.size(); ++i) { + battle_texture_headers[i] = BattleTextureHeader(); + } + battle_texture_headers_cursor = 0; + + ((void(*)())ff8_externals.sub_571870)(); +} + +void battle_set_texture_header(uint8_t file_id, bool is_palette, uint8_t file_relative_id, int16_t *pos_and_size, uint8_t *texture_buffer) +{ + if (trace_all || trace_vram) ffnx_trace("%s: effect_id=%d file_id=%d is_palette=%d file_relative_id=%d pos_and_size=(%d, %d, %d, %d) texture_buffer=0x%X battle_texture_headers_cursor=%d\n", __func__, + *ff8_externals.battle_magic_id, file_id, is_palette, file_relative_id, + pos_and_size[0], pos_and_size[1], pos_and_size[2], pos_and_size[3], texture_buffer, battle_texture_headers_cursor); + + BattleTextureHeader header = BattleTextureHeader(); + header.effect_id = *ff8_externals.battle_magic_id; + header.file_id = file_id; + header.is_palette = is_palette; + header.file_relative_id = file_relative_id; + header.x = pos_and_size[0]; + header.y = pos_and_size[1]; + header.w = pos_and_size[2]; + header.h = pos_and_size[3]; + header.data = texture_buffer; + + int index = battle_get_texture_header_index(texture_buffer); + + if (index < 0) { + battle_texture_headers[battle_texture_headers_cursor] = header; + + battle_texture_headers_cursor = (battle_texture_headers_cursor + 1) % battle_texture_data_list_size; + } else { + battle_texture_headers[index] = header; + } +} + +void ff8_battle_set_palette_data_from_mag(int file_relative_id) +{ + if (trace_all || trace_vram) ffnx_trace("%s: file_relative_id=%d\n", __func__, file_relative_id); + + int file_ext_number = *(uint8_t *)(*(*ff8_externals.effect_struct_27973EC + 42) + file_relative_id); + + uint8_t *data = ff8_externals.mag_data_dword_2798A68[file_ext_number]; + uint8_t *texture_data = data + *(DWORD *)(data + 8) + *(DWORD *)(data + *(DWORD *)(data + 8) + 4 * file_relative_id); + *(*ff8_externals.effect_struct_2797624 + 63) = DWORD(texture_data); + *(*ff8_externals.effect_struct_2797624 + 60) = *(*ff8_externals.effect_struct_27973EC + 39) + 8 * file_relative_id; + + battle_set_texture_header(file_ext_number, true, file_relative_id, *(int16_t **)(*ff8_externals.effect_struct_2797624 + 60), texture_data); +} + +void ff8_battle_set_texture_data_from_mag(int file_relative_id) +{ + if (trace_all || trace_vram) ffnx_trace("%s: file_relative_id=%d\n", __func__, file_relative_id); + + BYTE file_ext_infos = *(BYTE *)(*(*ff8_externals.effect_struct_27973EC + 41) + file_relative_id); + int file_ext_number = file_ext_infos & 0x7F; + + *(*ff8_externals.effect_struct_2797624 + 62) = file_ext_infos & 0x80; + uint8_t *texture_data1 = ff8_externals.mag_data_dword_2798A68[file_ext_number] + *(DWORD *)(ff8_externals.mag_data_dword_2798A68[file_ext_number] + 20); + uint8_t *texture_data = texture_data1 + ((unsigned int)0xFFFFFF & *(DWORD *)(texture_data1 + 4 * file_relative_id)); + *(*ff8_externals.effect_struct_2797624 + 63) = DWORD(texture_data); + int pos_and_size = *(*ff8_externals.effect_struct_27973EC + 38) + 8 * file_relative_id; + *(*ff8_externals.effect_struct_2797624 + 60) = pos_and_size; + WORD *v4 = *(WORD **)(*ff8_externals.effect_struct_2797624 + 60); + *(WORD *)&pos_and_size = *v4; + *(*ff8_externals.effect_struct_2797624 + 61) = *(*ff8_externals.effect_struct_2797624 + 62) | *(*ff8_externals.dword_27973E8 + 146) & 0x60 | ((v4[1] & 0x100 | (pos_and_size >> 2) & 0x3F0) >> 4); + + battle_set_texture_header(file_ext_number, false, file_relative_id, *(int16_t **)(*ff8_externals.effect_struct_2797624 + 60), texture_data); +} + +void *battle_set_texture_action_upload_raw_pal_call(int16_t *pos_and_size, char *texture_data) +{ + if (trace_all || trace_vram) ffnx_trace("%s: pos=(%d, %d) size=(%d, %d) texture_data=0x%X\n", __func__, pos_and_size[0], pos_and_size[1], pos_and_size[2], pos_and_size[3], texture_data); + + battle_set_texture_header(0, true, 0, pos_and_size, (uint8_t *)texture_data); + + return ((void*(*)(int16_t*,char*))ff8_externals.sub_505DF0)(pos_and_size, texture_data); +} + +void ff8_battle_upload_texture(int16_t *pos_and_size, uint8_t *texture_buffer) +{ + int index = battle_get_texture_header_index(texture_buffer); + + if (index >= 0) { + const BattleTextureHeader &header = battle_texture_headers.at(index); + + if (trace_all || trace_vram) ffnx_trace("%s: texture identified is_pal=%d rect=(%d, %d, %d, %d) effect_id=%d file_id=%d file_relative_id=%d\n", __func__, + header.is_palette, header.x, header.y, header.w, header.h, header.effect_id, header.file_id, header.file_relative_id); + + if (header.is_palette) { + next_pal_data = (uint16_t *)texture_buffer; + } else { + uint16_t minX = 0xFFFF, maxX = 0, minY = 0xFFFF, maxY = 0; + int index2 = index; + + for (int i = 1; i < battle_texture_data_list_size; ++i) { + const BattleTextureHeader &header2 = battle_texture_headers.at((index + i) % battle_texture_data_list_size); + if (!header2.is_palette) { + break; + } + + if (minX > header2.x) { + minX = header2.x; + } + if (minY > header2.y) { + minY = header2.y; + } + if (maxX < header2.x + header2.w) { + maxX = header2.x + header2.w; + } + if (maxY < header2.y + header2.h) { + maxY = header2.y + header2.h; + } + } + + snprintf(next_texture_name, sizeof(next_texture_name), "magic/MAG%03d_B.%02d-%d", header.effect_id, header.file_id, header.file_relative_id); + + if (minY != 0xFFFF) { + next_bpp = maxX - minX == 16 ? Tim::Bpp4 : Tim::Bpp8; + texture_infos = TexturePacker::TextureInfos(pos_and_size[0], pos_and_size[1], pos_and_size[2], pos_and_size[3], next_bpp); + palette_infos = TexturePacker::TextureInfos(minX, minY, maxX - minX, maxY - minY, next_bpp); + int last_pal_x = -1, last_pal_y = -1; + + for (int i = 1; i < battle_texture_data_list_size; ++i) { + const BattleTextureHeader &header2 = battle_texture_headers.at((index + i) % battle_texture_data_list_size); + if (!header2.is_palette || (last_pal_y >= 0 && std::abs(header2.y - last_pal_y) > 10) || (last_pal_x >= 0 && last_pal_x != header.x)) { + break; + } + + ff8_tim tim_infos = ff8_tim(); + tim_infos.pal_data = (uint16_t *)header2.data; + tim_infos.pal_w = header2.w; + tim_infos.pal_h = header2.h; + tim_infos.img_data = texture_buffer; + tim_infos.img_w = pos_and_size[2]; + tim_infos.img_h = pos_and_size[3]; + Tim(next_bpp, tim_infos).save(next_texture_name, header2.y - minY, true); + + last_pal_x = header2.x; + last_pal_y = header2.y; + + // We assume that the first palette is correct, so we remove it from the array. The others maybe not, so keep them + if (i == 1) { + battle_texture_headers[(index + i) % battle_texture_data_list_size] = BattleTextureHeader(); + } + } + } else { + next_bpp = Tim::Bpp16; + } + + battle_texture_headers[index] = BattleTextureHeader(); + } + } else { + int index = battle_get_texture_file_name_index(texture_buffer); + + if (index >= 0) { + if (trace_all || trace_vram) ffnx_trace("%s: texture data identified 0x%X\n", __func__, texture_buffer); + + strncpy(next_texture_name, battle_texture_data_list[index].file_name, sizeof(battle_texture_data_list[index].file_name)); + + battle_texture_data_list[index] = BattleTextureFileName(); + + next_bpp = Tim::Bpp8; + + // Splitted version of A8DEF.TIM + if (stricmp(next_texture_name, "battle/MA8DEF_P.0") == 0 || stricmp(next_texture_name, "battle/MA8DEF_P.1") == 0 || stricmp(next_texture_name, "battle/MA8DEF_P.2") == 0) { + next_bpp = Tim::Bpp4; + } + + ff8_tim tim_infos = ff8_tim(); + tim_infos.img_data = texture_buffer; + tim_infos.img_x = pos_and_size[0]; + tim_infos.img_y = pos_and_size[1]; + tim_infos.img_w = pos_and_size[2]; + tim_infos.img_h = pos_and_size[3]; + // Save without palette + Tim(next_bpp, tim_infos).save(next_texture_name); + + // Save with every palettes we know + for (int i = 1; i < battle_texture_data_list_size; ++i) { + const BattleTextureHeader &header = battle_texture_headers.at(i % battle_texture_data_list_size); + if (!header.is_palette) { + continue; + } + + char fileName[MAX_PATH] = {}; + + snprintf(fileName, sizeof(fileName), "%s-pal-guess-%dx%d", next_texture_name, header.x, header.y); + + tim_infos.pal_data = (uint16_t *)header.data; + tim_infos.pal_w = header.w; + tim_infos.pal_h = header.h; + Tim(next_bpp, tim_infos).save(fileName, true); + } + } else if (trace_all || trace_vram) { + ffnx_warning("%s: unknown texture uploaded\n", __func__); + } + } + + ff8_upload_vram(pos_and_size, texture_buffer); +} + void ff8_battle_upload_texture_palette(int16_t *pos_and_size, uint8_t *texture_buffer) { - if (trace_all || trace_vram) ffnx_trace("%s: %s\n", __func__, battle_texture_name); + if (trace_all || trace_vram) ffnx_trace("%s: %s battle_texture_id=%d\n", __func__, battle_texture_name, battle_texture_id); Tim tim = Tim::fromTimData(texture_buffer - 20); next_pal_data = (uint16_t *)texture_buffer; ff8_upload_vram(pos_and_size, texture_buffer); + texture_infos = TexturePacker::TextureInfos(tim.imageX(), tim.imageY(), tim.imageWidth(), tim.imageHeight(), tim.bpp()); + palette_infos = TexturePacker::TextureInfos(tim.paletteX(), tim.paletteY(), tim.paletteWidth(), tim.paletteHeight(), Tim::Bpp16); next_bpp = tim.bpp(); - snprintf(next_texture_name, sizeof(next_texture_name), "%s-%d", battle_texture_name, battle_texture_id); - - ++battle_texture_id; + if (battle_texture_id < 0) { + strncpy(next_texture_name, battle_texture_name, sizeof(next_texture_name)); + } else { + snprintf(next_texture_name, sizeof(next_texture_name), "%s-%d", battle_texture_name, battle_texture_id); + ++battle_texture_id; + } if (save_textures) { if (StrStrIA(battle_texture_name, ".X") != nullptr) { @@ -1120,8 +1417,6 @@ void clean_psxvram_pages() void vram_init() { - //---- Texture uploads identification - replace_function(ff8_externals.upload_psx_vram, ff8_upload_vram); replace_function(ff8_externals.copy_psx_vram_part, ff8_copy_vram_part); @@ -1164,6 +1459,11 @@ void vram_init() replace_call(ff8_externals.battle_open_file_wrapper + 0x14, ff8_battle_open_and_read_file); replace_call(ff8_externals.battle_open_file + 0x8E, ff8_battle_read_file); replace_call(ff8_externals.battle_open_file + 0x1A2, ff8_battle_read_file); + replace_call(ff8_externals.battle_read_effect_sub_50AF20 + 0x63, battle_read_effect_alloc); + replace_call(ff8_externals.battle_upload_texture_to_vram + 0x2E, ff8_battle_upload_texture); + replace_function(ff8_externals.mag_data_palette_sub_B66560, ff8_battle_set_palette_data_from_mag); + replace_function(ff8_externals.mag_data_texture_sub_B66560, ff8_battle_set_texture_data_from_mag); + replace_call(ff8_externals.battle_set_action_upload_raw_palette_sub_B66400 + 0x68, battle_set_texture_action_upload_raw_pal_call); replace_call(ff8_externals.battle_upload_texture_to_vram + 0x45, ff8_battle_upload_texture_palette); replace_call(ff8_externals.load_magic_data_sub_5718E0 + 0xB, ff8_battle_open_effect); replace_call(ff8_externals.load_magic_data_sub_571B80 + 0x1E, ff8_battle_open_effect); @@ -1185,6 +1485,11 @@ void vram_init() replace_call(ff8_externals.sub_464BD0 + 0xAF, read_vram_to_buffer_with_palette1_parent_call2); + // read_vram_to_buffer_with_palette2_parent_calls + replace_call(ff8_externals.ssigpu_callbacks_2[100] + 0x33, read_vram_to_buffer_with_palette2_parent_call); + replace_call(ff8_externals.ssigpu_callbacks_2[116] + 0x2E, read_vram_to_buffer_with_palette2_parent_call); + replace_call(ff8_externals.ssigpu_callbacks_2[124] + 0x2E, read_vram_to_buffer_with_palette2_parent_call); + replace_call(uint32_t(ff8_externals.sub_464F70) + 0x2C5, read_vram_to_buffer); replace_call(ff8_externals.sub_4653B0 + 0x9D, read_vram_to_buffer); diff --git a/src/ff8_data.cpp b/src/ff8_data.cpp index 1fd879a2..a6de062a 100644 --- a/src/ff8_data.cpp +++ b/src/ff8_data.cpp @@ -23,6 +23,7 @@ #include "ff8_data.h" #include "patch.h" +#include "ff8/battle/effects.h" void ff8_set_main_loop(uint32_t driver_mode, uint32_t main_loop) { @@ -186,6 +187,7 @@ void ff8_find_externals() ff8_externals.battle_filenames = (char **)get_absolute_value(ff8_externals.battle_open_file, 0x11); ff8_externals.sub_47D890 = get_relative_call(ff8_externals.sub_506CF0, 0x59); + ff8_externals.sub_505DF0 = get_relative_call(ff8_externals.sub_506CF0, 0xAA); ff8_externals.sub_4A94D0 = get_relative_call(ff8_externals.sub_47D890, 0x9); ff8_externals.sub_4BCBE0 = get_relative_call(ff8_externals.sub_4A94D0, 0x1E0); ff8_externals.sub_4C8B10 = get_relative_call(ff8_externals.sub_4BCBE0, 0x8E); @@ -769,13 +771,28 @@ void ff8_find_externals() ff8_externals.sub_50A9A0 = get_absolute_value(ff8_externals.sub_50A790, 0x7C); ff8_externals.battle_read_effect_sub_50AF20 = get_relative_call(ff8_externals.sub_50A9A0, 0xF4); ff8_externals.func_off_battle_effects_C81774 = (DWORD*)get_absolute_value(ff8_externals.battle_read_effect_sub_50AF20, 0x2C); + ff8_externals.battle_magic_id = (int*)get_absolute_value(ff8_externals.battle_read_effect_sub_50AF20, 0x3E); + ff8_externals.sub_571870 = get_relative_call(ff8_externals.battle_read_effect_sub_50AF20, 0x63); ff8_externals.func_off_battle_effect_textures_50AF93 = (DWORD*)get_absolute_value(ff8_externals.battle_read_effect_sub_50AF20, 0x6B); - ff8_externals.battle_effect_quezacotl_sub_6C3550 = ff8_externals.func_off_battle_effects_C81774[115]; - ff8_externals.sub_6C3640 = get_relative_call(ff8_externals.battle_effect_quezacotl_sub_6C3550, 0x5); + ff8_externals.sub_6C3640 = get_relative_call(ff8_externals.func_off_battle_effects_C81774[FF8BattleEffect::Quezacotl], 0x5); ff8_externals.sub_6C3760 = get_absolute_value(ff8_externals.sub_6C3640, 0x8B); ff8_externals.vibrate_data_summon_quezacotl = (uint8_t **)get_absolute_value(ff8_externals.sub_6C3760, 0xB0); + ff8_externals.sub_B586F0 = get_absolute_value(ff8_externals.func_off_battle_effects_C81774[FF8BattleEffect::Leviathan], 0x45); + ff8_externals.sub_B64B80 = get_relative_call(ff8_externals.sub_B586F0, 0x1B5); + ff8_externals.leviathan_funcs_B64C3C = (DWORD *)get_absolute_value(ff8_externals.sub_B64B80, 0xBF); + ff8_externals.mag_data_palette_sub_B66560 = get_relative_call(ff8_externals.leviathan_funcs_B64C3C[FF8BattleEffectOpcode::UploadPalette75], 0x13); + ff8_externals.effect_struct_27973EC = (DWORD **)get_absolute_value(ff8_externals.mag_data_palette_sub_B66560, 0x4); + ff8_externals.mag_data_dword_2798A68 = (uint8_t **)get_absolute_value(ff8_externals.mag_data_palette_sub_B66560, 0x19); + ff8_externals.effect_struct_2797624 = (DWORD **)get_absolute_value(ff8_externals.mag_data_palette_sub_B66560, 0x28); + ff8_externals.battle_set_action_upload_raw_palette_sub_B666F0 = get_relative_call(ff8_externals.leviathan_funcs_B64C3C[FF8BattleEffectOpcode::UploadPalette75], 0xD4); + ff8_externals.battle_set_action_upload_raw_palette_sub_B66400 = get_relative_call(ff8_externals.battle_set_action_upload_raw_palette_sub_B666F0, 0x141); + + ff8_externals.sub_B63230 = get_relative_call(ff8_externals.leviathan_funcs_B64C3C[FF8BattleEffectOpcode::UploadTexture39], 0x9); + ff8_externals.mag_data_texture_sub_B66560 = get_relative_call(ff8_externals.sub_B63230, 0xA); + ff8_externals.dword_27973E8 = (BYTE**)get_absolute_value(ff8_externals.mag_data_texture_sub_B66560, 0x8F); + ff8_externals.load_magic_data_sub_571B80 = get_relative_call(ff8_externals.func_off_battle_effect_textures_50AF93[0], 0x5); ff8_externals.load_magic_data_sub_571900 = get_relative_call(ff8_externals.load_magic_data_sub_571B80, 0x1E); ff8_externals.load_magic_data_sub_5718E0 = get_relative_call(ff8_externals.func_off_battle_effect_textures_50AF93[198], 0x5); diff --git a/src/image/tim.cpp b/src/image/tim.cpp index 85997dcb..ee8d3c85 100644 --- a/src/image/tim.cpp +++ b/src/image/tim.cpp @@ -222,25 +222,47 @@ bool Tim::toRGBA32(uint32_t *target, PaletteDetectionStrategy *paletteDetectionS { if (_tim.pal_data == nullptr || paletteDetectionStrategy == nullptr) { - ffnx_error("%s bpp 0 without palette\n", __func__); + uint8_t *img_data8 = _tim.img_data; - return false; - } + for (int y = 0; y < _tim.img_h; ++y) + { + for (int x = 0; x < _tim.img_w / 2; ++x) + { + // Grey color + uint8_t color = (*img_data8 & 0xF) * 16; - uint8_t *img_data = _tim.img_data; + *target = ((color == 0 && withAlpha ? 0x00 : 0xffu) << 24) | + (color << 16) | (color << 8) | color; - for (int y = 0; y < _tim.img_h; ++y) + ++target; + + color = (*img_data8 >> 4) * 16; + + *target = ((color == 0 && withAlpha ? 0x00 : 0xffu) << 24) | + (color << 16) | (color << 8) | color; + + ++target; + ++img_data8; + } + } + } + else { - for (int x = 0; x < _tim.img_w / 2; ++x) + uint8_t *img_data = _tim.img_data; + + for (int y = 0; y < _tim.img_h; ++y) { - *target = fromR5G5B5Color((_tim.pal_data + paletteDetectionStrategy->palOffset(x * 2, y))[*img_data & 0xF], withAlpha); + for (int x = 0; x < _tim.img_w / 2; ++x) + { + *target = fromR5G5B5Color((_tim.pal_data + paletteDetectionStrategy->palOffset(x * 2, y))[*img_data & 0xF], withAlpha); - ++target; + ++target; - *target = fromR5G5B5Color((_tim.pal_data + paletteDetectionStrategy->palOffset(x * 2 + 1, y))[*img_data >> 4], withAlpha); + *target = fromR5G5B5Color((_tim.pal_data + paletteDetectionStrategy->palOffset(x * 2 + 1, y))[*img_data >> 4], withAlpha); - ++target; - ++img_data; + ++target; + ++img_data; + } } } } @@ -248,7 +270,7 @@ bool Tim::toRGBA32(uint32_t *target, PaletteDetectionStrategy *paletteDetectionS { if (_tim.pal_data == nullptr || paletteDetectionStrategy == nullptr) { - uint8_t *img_data8 = (uint8_t *)_tim.img_data; + uint8_t *img_data8 = _tim.img_data; for (int y = 0; y < _tim.img_h; ++y) {