diff --git a/Changelog.md b/Changelog.md index a7c3760a..5a95d824 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,10 @@ - Full commit list since last stable release: https://github.com/julianxhokaxhiu/FFNx/compare/1.15.0...master +## Common + + - Improve texture upload time by reducing allocations for color conversion + ## FF7 - Input: Allow Cloud to walk/run based on the left analogue stick position ( https://github.com/julianxhokaxhiu/FFNx/issues/523 ) @@ -9,6 +13,7 @@ ## FF8 - Common: Fix startup hang on launch +- Graphics: Add Field texture replacement ( https://github.com/julianxhokaxhiu/FFNx/pull/542 ) # 1.15.0 diff --git a/src/common.cpp b/src/common.cpp index 10e85581..9532e55e 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -170,6 +170,12 @@ bool simulate_OK_button = false; GamepadAnalogueIntent gamepad_analogue_intent = INTENT_NONE; +uint32_t *image_data_cache = nullptr; +uint32_t image_data_size_cache = 0; + +uint8_t *image_data_scaled_cache = nullptr; +uint32_t image_data_scaled_size_cache = 0; + uint32_t noop() { return 0; } uint32_t noop_a1(uint32_t a1) { return 0; } uint32_t noop_a2(uint32_t a1, uint32_t a2) { return 0; } @@ -1506,7 +1512,16 @@ uint32_t load_external_texture(void* image_data, uint32_t dataSize, struct textu if (scale > 1) { uint32_t image_data_size = originalWidth * scale * originalHeight * scale * 4; - image_data_scaled = (uint8_t*)driver_malloc(image_data_size); + // Allocate with cache + if (image_data_scaled_size_cache == 0 || image_data_size > image_data_scaled_size_cache) { + if (image_data_scaled_cache != nullptr) { + driver_free(image_data_scaled_cache); + } + image_data_scaled_cache = (uint8_t*)driver_malloc(image_data_size); + image_data_scaled_size_cache = image_data_size; + } + + image_data_scaled = image_data_scaled_cache; // convert source data if (image_data_scaled != nullptr) @@ -1535,11 +1550,6 @@ uint32_t load_external_texture(void* image_data, uint32_t dataSize, struct textu VRASS(texture_set, ogl.external, false); } - if (image_data_scaled != nullptr && image_data_scaled != image_data) - { - driver_free(image_data_scaled); - } - if (textureType == TexturePacker::InternalTexture) { gl_replace_texture(texture_set, VREF(tex_header, palette_index), texture); @@ -1822,7 +1832,17 @@ struct texture_set *common_load_texture(struct texture_set *_texture_set, struct // allocate PBO uint32_t image_data_size = w * h * 4; - image_data = (uint32_t*)driver_malloc(image_data_size); + + // Allocate with cache + if (image_data_size_cache == 0 || image_data_size > image_data_size_cache) { + if (image_data_cache != nullptr) { + driver_free(image_data_cache); + } + image_data_cache = (uint32_t*)driver_malloc(image_data_size); + image_data_size_cache = image_data_size; + } + + image_data = image_data_cache; // convert source data if (image_data != NULL) convert_image_data(VREF(tex_header, image_data), image_data, w, h, tex_format, invert_alpha, color_key, palette_offset, reference_alpha); @@ -1839,9 +1859,6 @@ struct texture_set *common_load_texture(struct texture_set *_texture_set, struct // commit PBO and populate texture set gl_upload_texture(_texture_set, VREF(tex_header, palette_index), image_data, RendererTextureType::BGRA); } - - // free the memory buffer - driver_free(image_data); } } else ffnx_unexpected("no texture format specified or no source data\n"); diff --git a/src/ff8.h b/src/ff8.h index b971f4d6..dc1b8c8d 100644 --- a/src/ff8.h +++ b/src/ff8.h @@ -900,6 +900,66 @@ struct ff8_gfx_driver gfx_field_EC *field_EC; }; +struct ff8_field_state_common { + uint8_t stack_data[0x140]; + uint32_t field_140; + uint32_t field_144; + uint32_t field_148; + uint32_t field_14c; + uint32_t field_150; + uint32_t field_154; + uint32_t field_158; + uint32_t field_15c; + uint32_t execution_flags; // bgdraw: 0x10, bganime/rbganime: 0x980 (0x800: animation ongoing) + uint32_t field_164; + uint32_t field_168; + uint32_t field_16c; + uint32_t field_170; + uint8_t field_174; // has anim? + uint8_t field_175; // has anim mask? + uint16_t current_instruction_position; // field_176 + uint32_t field_178; + uint32_t field_17c; + uint32_t field_180; + uint8_t stack_current_position; // field_184 + uint8_t field_185; + uint8_t field_186; + uint8_t field_187; +}; + +struct ff8_field_state_background { + ff8_field_state_common common; + uint16_t bgstate; // field_188, set to -1 if off + uint16_t field_18a; + uint16_t bgparam_anim_start; // field_18c + uint16_t bgparam_anim_end; // field_18e + uint16_t bgparam_anim_speed1; // field_190 + uint16_t bgparam_anim_speed2; // field_192 + uint16_t bgparam_anim_flags; // field_194 + uint16_t field_196; + uint32_t field_198; + uint16_t bgshadeloop_remember_stack_pointer; // field_19c + uint16_t bgshade_add_value; // field_19e + uint16_t field_1a0; // bgshadeloop + uint16_t field_1a2; // bgshadeloop + uint8_t field_1a4; // bgshadeloop + uint8_t field_1a5; // bgshadeloop + uint8_t field_1a6; // bgshadeloop + uint8_t bgshade_color1r; // field_1a7 + uint8_t bgshade_color1g; // field_1a8 + uint8_t bgshade_color1b; // field_1a9 + uint8_t bgshade_color2r; // field_1aa + uint8_t bgshade_color2g; // field_1ab + uint8_t bgshade_color2b; // field_1ac + uint8_t bgshade_color1r_2; // field_1ad + uint8_t bgshade_color1g_2; // field_1ae + uint8_t bgshade_color1b_2; // field_1af + uint8_t bgshade_color2r_2; // field_1b0 + uint8_t bgshade_color2g_2; // field_1b1 + uint8_t bgshade_color2b_2; // field_1b2 + uint8_t field_1b3; +}; + // --------------- end of FF8 imports --------------- // memory addresses and function pointers from FF8.exe @@ -986,7 +1046,13 @@ struct ff8_externals uint32_t read_field_data; uint32_t upload_mim_file; char *field_filename; + uint32_t field_scripts_init; + uint8_t *field_state_background_count; + ff8_field_state_background **field_state_backgrounds; uint32_t load_field_models; + uint32_t chara_one_read_file; + uint32_t chara_one_seek_file; + uint32_t chara_one_upload_texture; uint32_t worldmap_main_loop; uint32_t worldmap_enter_main; uint32_t worldmap_sub_53F310; diff --git a/src/ff8/field/background.cpp b/src/ff8/field/background.cpp new file mode 100644 index 00000000..5ebcca08 --- /dev/null +++ b/src/ff8/field/background.cpp @@ -0,0 +1,144 @@ +/****************************************************************************/ +// 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 "background.h" +#include "../../image/tim.h" +#include "../../saveload.h" +#include "../../log.h" + +bool ff8_background_tiles_looks_alike(const Tile &tile, const Tile &other) +{ + return tile.texID == other.texID + && tile.palID == other.palID + && tile.srcX == other.srcX + && tile.srcY == other.srcY + && tile.blendType == other.blendType; +} + +std::vector ff8_background_parse_tiles(const uint8_t *map_data) +{ + std::vector tiles; + + while (true) { + Tile tile; + + memcpy(&tile, map_data, sizeof(Tile)); + + if (tile.x == 0x7fff) { + break; + } + + uint8_t texture_id = tile.texID & 0xF; + Tim::Bpp bpp = Tim::Bpp((tile.texID >> 7) & 3); + uint8_t pal_id = (tile.palID >> 6) & 0xF; + + if (trace_all || trace_vram) ffnx_info("tile %d dst %d %d %d src %d %d texid %d bpp %d palId %d blendType %d param %d %d\n", tiles.size(), tile.x, tile.y, tile.z, tile.srcX, tile.srcY, texture_id, int(bpp), pal_id, tile.blendType, tile.parameter, tile.state); + + tiles.push_back(tile); + + map_data += sizeof(Tile); + } + + return tiles; +} + +bool ff8_background_save_textures(const std::vector &tiles, const uint8_t *mim_data, const char *filename) +{ + if (trace_all || trace_vram) ffnx_trace("%s %s\n", __func__, filename); + + const uint16_t* const palettes_data = reinterpret_cast(mim_data + 0x1000); + const uint8_t* const textures_data = mim_data + 0x3000; + + const uint8_t cols_count = tiles.size() / (TEXTURE_HEIGHT / TILE_SIZE) + int(tiles.size() % (TEXTURE_HEIGHT / TILE_SIZE) != 0); + const uint16_t width = cols_count * TILE_SIZE; + const uint32_t image_data_size = width * TEXTURE_HEIGHT * sizeof(uint32_t); + + uint32_t* const image_data_start = new uint32_t[width * TEXTURE_HEIGHT]; + + if (image_data_start == nullptr) { + return false; + } + + // Fill with zeroes (transparent image) + memset(image_data_start, 0, image_data_size); + + uint32_t tile_id = 0; + + for (const Tile &tile: tiles) { + Tim::Bpp bpp = Tim::Bpp((tile.texID >> 7) & 3); + uint8_t texture_id = tile.texID & 0xF; + uint8_t pal_id = (tile.palID >> 6) & 0xF; + const uint8_t *texture_data_start = textures_data + texture_id * TEXTURE_WIDTH_BYTES + tile.srcY * MIM_DATA_WIDTH_BYTES; + const uint16_t *palette_data_start = bpp == Tim::Bpp16 ? nullptr : palettes_data + pal_id * PALETTE_SIZE; + uint8_t row = tile_id / cols_count, col = tile_id % cols_count; + uint32_t *target = image_data_start + row * TILE_SIZE * width; + + if (bpp == Tim::Bpp16) { + const uint16_t *texture_data = reinterpret_cast(texture_data_start) + tile.srcX; + target += col * TILE_SIZE; + + for (int y = 0; y < TILE_SIZE; ++y) { + for (int x = 0; x < TILE_SIZE; ++x) { + *(target + x) = fromR5G5B5Color(*(texture_data + x), true); + } + + target += width; + texture_data += MIM_DATA_WIDTH_BYTES / 2; + } + } else if (bpp == Tim::Bpp8) { + const uint8_t *texture_data = texture_data_start + tile.srcX; + target += col * TILE_SIZE; + + for (int y = 0; y < TILE_SIZE; ++y) { + for (int x = 0; x < TILE_SIZE; ++x) { + *(target + x) = fromR5G5B5Color(palette_data_start[*(texture_data + x)], true); + } + + target += width; + texture_data += MIM_DATA_WIDTH_BYTES; + } + } else { + const uint8_t *texture_data = texture_data_start + tile.srcX / 2; + target += col * TILE_SIZE; + + for (int y = 0; y < TILE_SIZE; ++y) { + for (int x = 0; x < TILE_SIZE / 2; ++x) { + uint8_t index = *(texture_data + x); + *(target + x * 2) = fromR5G5B5Color(palette_data_start[index & 0xF], true); + *(target + x * 2 + 1) = fromR5G5B5Color(palette_data_start[index >> 4], true); + } + + target += width; + texture_data += MIM_DATA_WIDTH_BYTES; + } + } + + ++tile_id; + } + + save_texture(image_data_start, image_data_size, width, TEXTURE_HEIGHT, uint32_t(-1), filename, false); + + delete[] image_data_start; + + return true; +} diff --git a/src/ff8/field/background.h b/src/ff8/field/background.h new file mode 100644 index 00000000..c3ad4ba0 --- /dev/null +++ b/src/ff8/field/background.h @@ -0,0 +1,55 @@ +/****************************************************************************/ +// 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 + +constexpr int TEXTURE_WIDTH_BYTES = 128; // Real texture width depends on the texture depth (bpp4 => 256, bpp8 => 128, bpp16 => 64) +constexpr int TEXTURE_WIDTH_BPP16 = 64; +constexpr int TEXTURE_WIDTH_BPP8 = 128; +constexpr int TEXTURE_WIDTH_BPP4 = 256; +constexpr int TEXTURE_HEIGHT = 256; +constexpr int VRAM_PAGE_MIM_MAX_COUNT = 13; +constexpr int MIM_DATA_WIDTH_BYTES = TEXTURE_WIDTH_BYTES * VRAM_PAGE_MIM_MAX_COUNT; +constexpr int MIM_DATA_HEIGHT = TEXTURE_HEIGHT; +constexpr int TILE_SIZE = 16; +constexpr int PALETTE_SIZE = 256; + +struct Tile { + int16_t x, y, z; + uint16_t texID; // 2 bits = depth | 2 bits = blend | 1 bit = draw | 4 bits = textureID + uint16_t palID; // 6 bits = Always 30 | 4 bits = PaletteID | 6 bits = Always 0 + uint8_t srcX, srcY; + uint8_t layerID; // 0-7 + uint8_t blendType; // 0-4 + uint8_t parameter, state; +}; + +// A tile looks like another if it uses the same texture with the same palette and uses the same blending +bool ff8_background_tiles_looks_alike(const Tile &tile, const Tile &other); + +std::vector ff8_background_parse_tiles(const uint8_t *map_data); +bool ff8_background_save_textures(const std::vector &tiles, const uint8_t *mim_data, const char *filename); diff --git a/src/ff8/field/chara_one.cpp b/src/ff8/field/chara_one.cpp new file mode 100644 index 00000000..97662382 --- /dev/null +++ b/src/ff8/field/chara_one.cpp @@ -0,0 +1,149 @@ +/****************************************************************************/ +// 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::unordered_map ff8_chara_one_parse_models(const uint8_t *chara_one_data, size_t size) +{ + std::unordered_map models; + + const uint8_t *cur = chara_one_data; + + uint32_t count; + memcpy(&count, cur, 4); + cur += 4; + + for (uint32_t i = 0; i < count && cur - chara_one_data < size - 16; ++i) { + uint32_t offset; + memcpy(&offset, cur, 4); + cur += 4; + + if (offset == 0) { + break; + } + + uint32_t section_size; + memcpy(§ion_size, cur, 4); + cur += 4; + + uint32_t flag; + memcpy(&flag, cur, 4); + cur += 4; + + if (flag == section_size) { + memcpy(&flag, cur, 4); + cur += 4; + } + + CharaOneModel model = CharaOneModel(); + + if (flag >> 24 != 0xd0) { // NPCs (not main characters) + uint32_t timOffset; + + ffnx_info("%s: %d %d %d\n", __func__, i, int(cur - chara_one_data), size); + + if ((flag & 0xFFFFFF) == 0) { + model.texturesData.push_back(0); + } + + while (cur - chara_one_data < size) { + memcpy(&timOffset, cur, 4); + cur += 4; + + if (timOffset == 0xFFFFFFFF) { + break; + } + + model.texturesData.push_back(timOffset & 0xFFFFFF); + } + } else { + model.isMch = true; + } + + if (cur - chara_one_data < 16) { + break; + } + + cur += 4; + char name[5] = ""; + for (uint8_t j = 0; j < 4; ++j) { + if (cur[j] > 'z' || cur[j] < '0') { + name[j] = '\0'; + break; + } + + name[j] = cur[j]; + } + strncpy(model.name, name, 4); + + models[offset + 4] = model; + + cur += 12; + } + + return models; +} + +void ff8_mch_parse_model(CharaOneModel &model, const uint8_t *mch_data, size_t size) +{ + if(size < 0x100) { + ffnx_warning("%s: empty MCH\n", __func__); + return; + } + + const uint8_t *cur = mch_data; + uint32_t tim_offset = 0; + + while (cur - mch_data < 0x100 - 4) { + memcpy(&tim_offset, cur, 4); + cur += 4; + + if(tim_offset == 0xFFFFFFFF) { + break; + } + + model.texturesData.push_back(tim_offset & 0xFFFFFF); + } +} + +bool ff8_chara_one_model_save_textures(const CharaOneModel &model, const uint8_t *chara_one_model_data, const char *dirname) +{ + if (trace_all || trace_vram) ffnx_trace("%s: %s\n", __func__, dirname); + + int texture_id = 0; + for (uint32_t texture_pointer: model.texturesData) { + char name[MAX_PATH] = {}; + snprintf(name, sizeof(name), "%s/%s-%d", dirname, model.name, texture_id); + Tim tim = Tim::fromTimData(chara_one_model_data + texture_pointer); + + if (!tim.save(name)) { + return false; + } + ++texture_id; + } + + return true; +} diff --git a/src/ff8/field/chara_one.h b/src/ff8/field/chara_one.h new file mode 100644 index 00000000..c4341b34 --- /dev/null +++ b/src/ff8/field/chara_one.h @@ -0,0 +1,39 @@ +/****************************************************************************/ +// 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 +#include + +struct CharaOneModel { + char name[6]; + bool isMch; + std::vector texturesData; +}; + +std::unordered_map ff8_chara_one_parse_models(const uint8_t *chara_one_data, size_t size); +void ff8_mch_parse_model(CharaOneModel &model, const uint8_t *mch_data, size_t size); +bool ff8_chara_one_model_save_textures(const CharaOneModel &models, const uint8_t *chara_one_model_data, const char *dirname); diff --git a/src/ff8/texture_packer.cpp b/src/ff8/texture_packer.cpp index b4478e12..3012dfb4 100644 --- a/src/ff8/texture_packer.cpp +++ b/src/ff8/texture_packer.cpp @@ -27,9 +27,8 @@ #include TexturePacker::TexturePacker() : - _vram(nullptr) + _vram(nullptr), _vramTextureIds(VRAM_WIDTH * VRAM_HEIGHT, INVALID_TEXTURE), _disableDrawTexturesBackground(false) { - memset(_vramTextureIds, INVALID_TEXTURE, VRAM_WIDTH * VRAM_HEIGHT * sizeof(ModdedTextureId)); } void TexturePacker::cleanVramTextureIds(const TextureInfos &texture) @@ -37,7 +36,7 @@ void TexturePacker::cleanVramTextureIds(const TextureInfos &texture) for (int prevY = 0; prevY < texture.h(); ++prevY) { int clearKey = texture.x() + (texture.y() + prevY) * VRAM_WIDTH; - std::fill_n(_vramTextureIds + clearKey, texture.w(), INVALID_TEXTURE); + std::fill_n(_vramTextureIds.begin() + clearKey, texture.w(), INVALID_TEXTURE); } } @@ -64,6 +63,17 @@ void TexturePacker::cleanTextures(ModdedTextureId previousTextureId, bool keepMo previousTexture.destroyImage(); _externalTextures.erase(previousTextureId); } + else if (!keepMods && _backgroundTextures.contains(previousTextureId)) + { + TextureBackground &previousTexture = _backgroundTextures.at(previousTextureId); + + if (trace_all || trace_vram) ffnx_info("TexturePacker::%s: clear texture background %s (textureId = %d)\n", __func__, previousTexture.name().c_str(), previousTextureId); + + cleanVramTextureIds(previousTexture); + + previousTexture.destroyImage(); + _backgroundTextures.erase(previousTextureId); + } else if (_textureRedirections.contains(previousTextureId)) { TextureRedirection &previousTexture = _textureRedirections.at(previousTextureId); @@ -77,22 +87,57 @@ void TexturePacker::cleanTextures(ModdedTextureId previousTextureId, bool keepMo } } -void TexturePacker::setTexture(const char *name, const uint8_t *source, int x, int y, int w, int h, uint8_t bpp, bool isPal) +void TexturePacker::setVramTextureId(ModdedTextureId textureId, int x, int y, int w, int h, bool keepMods) { - bool hasNamedTexture = name != nullptr && *name != '\0'; + for (int i = 0; i < h; ++i) + { + int vramY = y + i; - if (trace_all || trace_vram) ffnx_trace("TexturePacker::%s %s x=%d y=%d w=%d h=%d bpp=%d isPal=%d\n", __func__, hasNamedTexture ? name : "N/A", x, y, w, h, bpp, isPal); + for (int j = 0; j < w; ++j) + { + int vramX = x + j; + int key = vramX + vramY * VRAM_WIDTH; + ModdedTextureId previousTextureId = _vramTextureIds.at(key); + + if (previousTextureId != INVALID_TEXTURE) + { + cleanTextures(previousTextureId, keepMods); + } + + _vramTextureIds[key] = textureId; + } + } +} + +void TexturePacker::uploadTexture(const uint8_t *source, int x, int y, int w, int h) +{ + if (trace_all || trace_vram) ffnx_trace("TexturePacker::%s x=%d y=%d w=%d h=%d\n", __func__, x, y, w, h); uint8_t *vram = vramSeek(x, y); const int vramLineWidth = VRAM_DEPTH * VRAM_WIDTH; const int lineWidth = VRAM_DEPTH * w; - Texture tex; + + for (int i = 0; i < h; ++i) + { + memcpy(vram, source, lineWidth); + + source += lineWidth; + vram += vramLineWidth; + } +} + +void TexturePacker::setTexture(const char *name, int x, int y, int w, int h, Tim::Bpp bpp, bool isPal) +{ + bool hasNamedTexture = name != nullptr && *name != '\0'; + + if (trace_all || trace_vram) ffnx_trace("TexturePacker::%s %s x=%d y=%d w=%d h=%d bpp=%d isPal=%d\n", __func__, hasNamedTexture ? name : "N/A", x, y, w, h, bpp, isPal); + ModdedTextureId textureId = INVALID_TEXTURE; if (hasNamedTexture && !isPal) { - tex = Texture(name, x, y, w, h, bpp); - textureId = x + y * VRAM_WIDTH; + Texture tex(name, x, y, w, h, bpp); + textureId = makeTextureId(x, y); if (tex.createImage()) { @@ -104,29 +149,26 @@ void TexturePacker::setTexture(const char *name, const uint8_t *source, int x, i } } - for (int i = 0; i < h; ++i) - { - memcpy(vram, source, lineWidth); - - int vramY = y + i; - - for (int j = 0; j < w; ++j) - { - int vramX = x + j; - int key = vramX + vramY * VRAM_WIDTH; - ModdedTextureId previousTextureId = _vramTextureIds[key]; + setVramTextureId(textureId, x, y, w, h); +} - if (previousTextureId != INVALID_TEXTURE) - { - cleanTextures(previousTextureId); - } +void TexturePacker::setTextureBackground(const char *name, int x, int y, int w, int h, const std::vector &mapTiles) +{ + if (trace_all || trace_vram) ffnx_trace("TexturePacker::%s %s x=%d y=%d w=%d h=%d tileCount=%d\n", __func__, name, x, y, w, h, mapTiles.size()); - _vramTextureIds[key] = textureId; - } + TextureBackground tex(name, x, y, w, h, mapTiles); + ModdedTextureId textureId = makeTextureId(x, y); - source += lineWidth; - vram += vramLineWidth; + if (tex.createImage()) + { + _backgroundTextures[textureId] = tex; + } + else + { + _textures[textureId] = tex; } + + setVramTextureId(textureId, x, y, w, h); } bool TexturePacker::setTextureRedirection(const TextureInfos &oldTexture, const TextureInfos &newTexture, uint32_t *imageData) @@ -138,27 +180,10 @@ bool TexturePacker::setTextureRedirection(const TextureInfos &oldTexture, const TextureRedirection redirection(oldTexture, newTexture); if (redirection.isValid() && redirection.createImage(imageData)) { - ModdedTextureId textureId = oldTexture.x() + oldTexture.y() * VRAM_WIDTH; + ModdedTextureId textureId = makeTextureId(oldTexture.x(), oldTexture.y()); _textureRedirections[textureId] = redirection; - for (int y = 0; y < oldTexture.h(); ++y) - { - int vramY = oldTexture.y() + y; - - for (int x = 0; x < oldTexture.w(); ++x) - { - int vramX = oldTexture.x() + x; - int key = vramX + vramY * VRAM_WIDTH; - ModdedTextureId previousTextureId = _vramTextureIds[key]; - - if (previousTextureId != INVALID_TEXTURE) - { - cleanTextures(previousTextureId, true); - } - - _vramTextureIds[key] = textureId; - } - } + setVramTextureId(textureId, oldTexture.x(), oldTexture.y(), oldTexture.w(), oldTexture.h(), true); return true; } @@ -168,7 +193,7 @@ bool TexturePacker::setTextureRedirection(const TextureInfos &oldTexture, const uint8_t TexturePacker::getMaxScale(const uint8_t *texData) const { - if (_externalTextures.empty() && _textureRedirections.empty()) + if (_externalTextures.empty() && _backgroundTextures.empty() && _textureRedirections.empty()) { return 1; } @@ -191,7 +216,7 @@ uint8_t TexturePacker::getMaxScale(const uint8_t *texData) const for (int x = 0; x < 64; ++x) { int vramX = tiledTex.x + x; - ModdedTextureId textureId = _vramTextureIds[vramX + vramY * VRAM_WIDTH]; + ModdedTextureId textureId = _vramTextureIds.at(vramX + vramY * VRAM_WIDTH); if (textureId != INVALID_TEXTURE) { @@ -201,6 +226,10 @@ uint8_t TexturePacker::getMaxScale(const uint8_t *texData) const { scale = _externalTextures.at(textureId).scale(); } + else if (_backgroundTextures.contains(textureId)) + { + scale = _backgroundTextures.at(textureId).scale(); + } else if (_textureRedirections.contains(textureId)) { scale = _textureRedirections.at(textureId).scale(); @@ -219,7 +248,7 @@ uint8_t TexturePacker::getMaxScale(const uint8_t *texData) const void TexturePacker::getTextureNames(const uint8_t *texData, std::list &names) const { - if (_externalTextures.empty() && _textures.empty()) + if (_externalTextures.empty() && _backgroundTextures.empty() && _textures.empty()) { return; } @@ -241,7 +270,7 @@ void TexturePacker::getTextureNames(const uint8_t *texData, std::listbitsperpixel : -1)); - if (_tiledTexs.contains(texData)) + auto it = _tiledTexs.find(texData); + + if (it != _tiledTexs.end()) { - const TiledTex &tex = _tiledTexs.at(texData); + const TiledTex &tex = it->second; if (trace_all || trace_vram) ffnx_trace("TexturePacker::%s tex=(%d, %d) bpp=%d paletteIndex=%d\n", __func__, tex.x, tex.y, tex.bpp, paletteIndex); @@ -283,14 +334,14 @@ TexturePacker::TextureTypes TexturePacker::drawTextures(const uint8_t *texData, TexturePacker::TextureTypes TexturePacker::drawTextures(uint32_t *target, const TiledTex &tiledTex, int targetW, int targetH, uint8_t scale, uint32_t paletteIndex) { - if (_externalTextures.empty() && _textureRedirections.empty()) + if (_externalTextures.empty() && _backgroundTextures.empty() && _textureRedirections.empty()) { return NoTexture; } int w = targetW; - if (tiledTex.bpp <= 2) + if (tiledTex.bpp <= int(Tim::Bpp16)) { w /= 4 >> tiledTex.bpp; } @@ -301,19 +352,27 @@ TexturePacker::TextureTypes TexturePacker::drawTextures(uint32_t *target, const return NoTexture; } - TextureTypes drawnTextureTypes = NoTexture; + if (_disableDrawTexturesBackground && (trace_all || trace_vram)) ffnx_info("TexturePacker::%s disabled\n", __func__); - int scaledW = w * scale, - scaledH = targetH * scale; + TextureTypes drawnTextureTypes = NoTexture; for (int y = 0; y < targetH; ++y) { int vramY = tiledTex.y + y; + if (vramY >= VRAM_HEIGHT) { + break; + } + for (int x = 0; x < w; ++x) { int vramX = tiledTex.x + x; - ModdedTextureId textureId = _vramTextureIds[vramX + vramY * VRAM_WIDTH]; + + if (vramX >= VRAM_WIDTH) { + break; + } + + ModdedTextureId textureId = _vramTextureIds.at(vramX + vramY * VRAM_WIDTH); if (textureId != INVALID_TEXTURE) { @@ -328,6 +387,17 @@ TexturePacker::TextureTypes TexturePacker::drawTextures(uint32_t *target, const drawnTextureTypes = TextureTypes(int(drawnTextureTypes) | int(ExternalTexture)); } + else if (_backgroundTextures.contains(textureId) && ! _disableDrawTexturesBackground) + { + const TextureBackground &texture = _backgroundTextures.at(textureId); + + int textureX = vramX - texture.x(), + textureY = vramY - texture.y(); + + texture.copyRect(textureX, textureY, tiledTex.bpp, target, x, y, targetW, scale); + + drawnTextureTypes = TextureTypes(int(drawnTextureTypes) | int(ExternalTexture)); + } else if (_textureRedirections.contains(textureId)) { const TextureRedirection &redirection = _textureRedirections.at(textureId); @@ -348,14 +418,14 @@ TexturePacker::TextureTypes TexturePacker::drawTextures(uint32_t *target, const return drawnTextureTypes; } -void TexturePacker::registerTiledTex(const uint8_t *texData, int x, int y, uint8_t bpp, int palX, int palY) +void TexturePacker::registerTiledTex(const uint8_t *texData, int x, int y, Tim::Bpp bpp, int palX, int palY) { - if (trace_all || trace_vram) ffnx_trace("%s pointer=0x%X x=%d y=%d bpp=%d palX=%d palY=%d\n", __func__, texData, x, y, bpp, palX, palY); + if (trace_all || trace_vram) ffnx_trace("%s pointer=0x%X x=%d y=%d bpp=%d palX=%d palY=%d psx_texture_pages=%X\n", __func__, texData, x, y, bpp, palX, palY, ff8_externals.psx_texture_pages); _tiledTexs[texData] = TiledTex(x, y, bpp, palX, palY); } -bool TexturePacker::saveVram(const char *fileName, uint8_t bpp) const +bool TexturePacker::saveVram(const char *fileName, Tim::Bpp bpp) const { uint16_t palette[256] = {}; @@ -365,16 +435,16 @@ bool TexturePacker::saveVram(const char *fileName, uint8_t bpp) const tim_infos.img_w = VRAM_WIDTH; tim_infos.img_h = VRAM_HEIGHT; - if (bpp < 2) + if (bpp < Tim::Bpp16) { tim_infos.pal_data = palette; tim_infos.pal_h = 1; - tim_infos.pal_w = bpp == 0 ? 16 : 256; + tim_infos.pal_w = bpp == Tim::Bpp4 ? 16 : 256; // Greyscale palette for (int i = 0; i < tim_infos.pal_w; ++i) { - uint8_t color = bpp == 0 ? i * 16 : i; + uint8_t color = bpp == Tim::Bpp4 ? i * 16 : i; palette[i] = color | (color << 5) | (color << 10); } } @@ -383,17 +453,57 @@ bool TexturePacker::saveVram(const char *fileName, uint8_t bpp) const } TexturePacker::TextureInfos::TextureInfos() : - _x(0), _y(0), _w(0), _h(0), _bpp(255) + _x(0), _y(0), _w(0), _h(0), _bpp(Tim::Bpp4) { } TexturePacker::TextureInfos::TextureInfos( int x, int y, int w, int h, - uint8_t bpp + Tim::Bpp bpp ) : _x(x), _y(y), _w(w), _h(h), _bpp(bpp) { } +bimg::ImageContainer *TexturePacker::TextureInfos::createImageContainer(const char *name, uint8_t palette_index, bool hasPal) +{ + char filename[MAX_PATH] = {}, langPath[16] = {}; + + if(trace_all || trace_loaders || trace_vram) ffnx_trace("texture file name (VRAM): %s\n", name); + + if(save_textures) return nullptr; + + ff8_fs_lang_string(langPath); + strcat(langPath, "/"); + + for (uint8_t lang = 0; lang < 2; lang++) + { + for (size_t idx = 0; idx < mod_ext.size(); idx++) + { + if (hasPal) { + _snprintf(filename, sizeof(filename), "%s/%s/%s%s_%02i.%s", basedir, mod_path.c_str(), langPath, name, palette_index, mod_ext[idx].c_str()); + } else { + _snprintf(filename, sizeof(filename), "%s/%s/%s%s.%s", basedir, mod_path.c_str(), langPath, name, mod_ext[idx].c_str()); + } + bimg::ImageContainer *image = newRenderer.createImageContainer(filename, bimg::TextureFormat::BGRA8); + + if (image != nullptr) + { + if (trace_all || trace_loaders || trace_vram) ffnx_trace("Using texture: %s\n", filename); + + return image; + } + else if (trace_all || trace_loaders || trace_vram) + { + ffnx_warning("Texture does not exist, skipping: %s\n", filename); + } + } + + *langPath = '\0'; + } + + return nullptr; +} + uint8_t TexturePacker::TextureInfos::computeScale(int sourcePixelW, int sourceH, int targetPixelW, int targetH) { if (targetPixelW < sourcePixelW @@ -426,7 +536,7 @@ uint8_t TexturePacker::TextureInfos::computeScale(int sourcePixelW, int sourceH, } void TexturePacker::TextureInfos::copyRect( - const uint32_t *sourceRGBA, int sourceX, int sourceY, int sourceW, uint8_t sourceScale, uint8_t sourceDepth, + const uint32_t *sourceRGBA, int sourceXBpp2, int sourceY, int sourceW, uint8_t sourceScale, Tim::Bpp sourceDepth, uint32_t *targetRGBA, int targetX, int targetY, int targetW, uint8_t targetScale) { if (targetScale < sourceScale) @@ -434,24 +544,22 @@ void TexturePacker::TextureInfos::copyRect( return; } - uint8_t targetRectWidth = (4 >> sourceDepth) * targetScale, + const uint8_t targetRectWidth = (4 >> int(sourceDepth)) * targetScale, targetRectHeight = targetScale, - sourceRectWidth = (4 >> sourceDepth) * sourceScale, + sourceRectWidth = (4 >> int(sourceDepth)) * sourceScale, sourceRectHeight = sourceScale; - uint8_t scaleRatio = targetScale / sourceScale; + const uint8_t scaleRatio = targetScale / sourceScale; - targetX *= targetRectWidth; - targetY *= targetRectHeight; targetW *= targetScale; - sourceX *= sourceRectWidth; - sourceY *= sourceRectHeight; + targetRGBA += targetX * targetRectWidth + targetY * targetRectHeight * targetW; + sourceRGBA += sourceXBpp2 * sourceRectWidth + sourceY * sourceRectHeight * sourceW; for (int y = 0; y < targetRectHeight; ++y) { for (int x = 0; x < targetRectWidth; ++x) { - *(targetRGBA + targetX + x + (targetY + y) * targetW) = *(sourceRGBA + sourceX + x / scaleRatio + (sourceY + y / scaleRatio) * sourceW); + *(targetRGBA + x + y * targetW) = *(sourceRGBA + x / scaleRatio + y / scaleRatio * sourceW); } } } @@ -464,54 +572,34 @@ TexturePacker::Texture::Texture() : TexturePacker::Texture::Texture( const char *name, int x, int y, int w, int h, - uint8_t bpp + Tim::Bpp bpp ) : TextureInfos(x, y, w, h, bpp), _image(nullptr), _name(name), _scale(1) { } -bool TexturePacker::Texture::createImage(uint8_t palette_index) +bool TexturePacker::Texture::createImage(uint8_t palette_index, bool has_pal) { char filename[MAX_PATH], langPath[16] = {}; - if(trace_all || trace_loaders || trace_vram) ffnx_trace("texture file name (VRAM): %s\n", _name.c_str()); + _image = createImageContainer(_name.c_str(), palette_index, has_pal); - ff8_fs_lang_string(langPath); - strcat(langPath, "/"); - - for (int lang = 0; lang < 2; lang++) + if (_image == nullptr) { - for (int idx = 0; idx < mod_ext.size(); idx++) - { - _snprintf(filename, sizeof(filename), "%s/%s/%s%s_%02i.%s", basedir, mod_path.c_str(), langPath, _name.c_str(), palette_index, mod_ext[idx].c_str()); - _image = newRenderer.createImageContainer(filename, bimg::TextureFormat::BGRA8); - - if (_image != nullptr) - { - if (trace_all || trace_loaders || trace_vram) ffnx_trace("Using texture: %s\n", filename); - - uint8_t scale = computeScale(); - - if (scale == 0) - { - destroyImage(); - - return false; - } + return false; + } - _scale = scale; + uint8_t scale = computeScale(); - return true; - } - else if (trace_all || trace_loaders || trace_vram) - { - ffnx_warning("Texture does not exist, skipping: %s\n", filename); - } - } + if (scale == 0) + { + destroyImage(); - *langPath = '\0'; + return false; } - return false; + _scale = scale; + + return true; } void TexturePacker::Texture::destroyImage() @@ -527,22 +615,110 @@ uint8_t TexturePacker::Texture::computeScale() const return TextureInfos::computeScale(pixelW(), h(), _image->m_width, _image->m_height); } -void TexturePacker::Texture::copyRect(int textureX, int textureY, uint32_t *target, int targetX, int targetY, int targetW, uint8_t targetScale) const +void TexturePacker::Texture::copyRect(int sourceXBpp2, int sourceY, uint32_t *target, int targetX, int targetY, int targetW, uint8_t targetScale) const { TextureInfos::copyRect( - (const uint32_t *)_image->m_data, textureX, textureY, _image->m_width, _scale, _bpp, + (const uint32_t *)_image->m_data, sourceXBpp2, sourceY, _image->m_width, _scale, bpp(), target, targetX, targetY, targetW, targetScale ); } +TexturePacker::TextureBackground::TextureBackground() : + Texture() +{ +} + +TexturePacker::TextureBackground::TextureBackground( + const char *name, + int x, int y, int w, int h, + const std::vector &mapTiles +) : Texture(name, x, y, w, h, Tim::Bpp16), _mapTiles(mapTiles) +{ + // Build tileIdsByPosition for fast lookup + _tileIdsByPosition.reserve(mapTiles.size()); + size_t tileId = 0; + for (const Tile &tile: mapTiles) { + const uint8_t textureId = tile.texID & 0xF; + const uint16_t key = uint16_t(textureId) | (uint16_t(tile.srcX / TILE_SIZE) << 4) | (uint16_t(tile.srcY / TILE_SIZE) << 8) | (((uint16_t(tile.texID) >> 7) & 3) << 12); + + auto it = _tileIdsByPosition.find(key); + // Remove some duplicates (but keep those which render differently) + if (it == _tileIdsByPosition.end() || ! ff8_background_tiles_looks_alike(tile, mapTiles.at(it->second))) { + _tileIdsByPosition.insert(std::pair(key, tileId)); + } + ++tileId; + } + _colsCount = mapTiles.size() / (TEXTURE_HEIGHT / TILE_SIZE) + int(mapTiles.size() % (TEXTURE_HEIGHT / TILE_SIZE) != 0); +} + +void TexturePacker::TextureBackground::copyRect(int sourceXBpp2, int sourceY, Tim::Bpp textureBpp, uint32_t *target, int targetX, int targetY, int targetW, uint8_t targetScale) const +{ + const uint8_t textureId = sourceXBpp2 / TEXTURE_WIDTH_BPP16; + const uint16_t sourceX = (sourceXBpp2 % TEXTURE_WIDTH_BPP16) << (2 - int(textureBpp)); + const uint16_t key = uint16_t(textureId) | (uint16_t(sourceX / TILE_SIZE) << 4) | (uint16_t(sourceY / TILE_SIZE) << 8) | (uint16_t(textureBpp) << 12); + + auto [begin, end] = _tileIdsByPosition.equal_range(key); + if (begin == end) { + return; + } + + bool multiMatch = _tileIdsByPosition.count(key) > 1, matched = false; + + // Iterate over matching tiles + for (const std::pair &pair: std::ranges::subrange{begin, end}) { + const size_t tileId = pair.second; + const Tile &tile = _mapTiles.at(tileId); + + if (multiMatch && tile.parameter != 255) { + if (tile.parameter < *ff8_externals.field_state_background_count) { + const ff8_field_state_background *field_state_backgrounds = *ff8_externals.field_state_backgrounds; + + if (tile.state != field_state_backgrounds[tile.parameter].bgstate >> 6) { + continue; + } + } else { + ffnx_warning("TextureBackground::%s: group script not found for background parameter %d\n", __func__, tile.parameter); + + continue; + } + } + + if (matched) { + ffnx_warning("TextureBackground::%s: multiple tiles found for the same position (tile id %d)\n", __func__, tileId); + + break; + } + + const Tim::Bpp bpp = Tim::Bpp((tile.texID >> 7) & 3); + const int col = tileId % _colsCount, row = tileId / _colsCount; + const int imageXBpp2 = col * TILE_SIZE / (4 >> int(bpp)) + sourceXBpp2 % (4 << int(bpp)), imageY = row * TILE_SIZE + sourceY % TILE_SIZE; + + TextureInfos::copyRect( + (const uint32_t *)image()->m_data, imageXBpp2, imageY, image()->m_width, scale(), bpp, + target, targetX, targetY, targetW, targetScale + ); + + matched = true; + } + + if (matched == false) { + ffnx_warning("TextureBackground::%s: tile not matched textureId=%d, sourceX=%d, sourceY=%d\n", __func__, textureId, sourceX, sourceY); + } +} + +uint8_t TexturePacker::TextureBackground::computeScale() const +{ + return TextureInfos::computeScale(_colsCount * TILE_SIZE, TEXTURE_HEIGHT, image()->m_width, image()->m_height); +} + TexturePacker::TiledTex::TiledTex() - : x(0), y(0), palX(0), palY(0), bpp(0) + : x(0), y(0), palX(0), palY(0), bpp(Tim::Bpp4), renderedOnce(false) { } TexturePacker::TiledTex::TiledTex( - int x, int y, uint8_t bpp, int palX, int palY -) : x(x), y(y), palX(palX), palY(palY), bpp(bpp) + int x, int y, Tim::Bpp bpp, int palX, int palY +) : x(x), y(y), palX(palX), palY(palY), bpp(bpp), renderedOnce(false) { } diff --git a/src/ff8/texture_packer.h b/src/ff8/texture_packer.h index 6e277c54..55d8823f 100644 --- a/src/ff8/texture_packer.h +++ b/src/ff8/texture_packer.h @@ -23,12 +23,14 @@ #pragma once #include -#include +#include #include +#include #include #include "../ff8.h" #include "../image/tim.h" +#include "field/background.h" typedef uint32_t ModdedTextureId; @@ -45,7 +47,7 @@ class TexturePacker { TextureInfos(); TextureInfos( int x, int y, int w, int h, - uint8_t bpp + Tim::Bpp bpp ); inline int x() const { return _x; @@ -62,19 +64,20 @@ class TexturePacker { inline int h() const { return _h; } - inline uint8_t bpp() const { + inline Tim::Bpp bpp() const { return _bpp; } protected: + static bimg::ImageContainer *createImageContainer(const char *name, uint8_t palette_index, bool hasPal); static uint8_t computeScale(int sourcePixelW, int sourceH, int targetPixelW, int targetH); static void copyRect( - const uint32_t *sourceRGBA, int sourceX, int sourceY, int sourceW, uint8_t sourceScale, uint8_t sourceDepth, + const uint32_t *sourceRGBA, int sourceXBpp2, int sourceYBpp2, int sourceW, uint8_t sourceScale, Tim::Bpp sourceDepth, uint32_t *targetRGBA, int targetX, int targetY, int targetW, uint8_t targetScale ); - + private: int _x, _y; int _w, _h; - uint8_t _bpp; + Tim::Bpp _bpp; }; enum TextureTypes { @@ -87,27 +90,33 @@ class TexturePacker { inline void setVram(uint8_t *vram) { _vram = vram; } - void setTexture(const char *name, const uint8_t *texture, int x, int y, int w, int h, uint8_t bpp, bool isPal); + void uploadTexture(const uint8_t *texture, int x, int y, int w, int h); + void setTexture(const char *name, int x, int y, int w, int h, Tim::Bpp bpp, bool isPal); + void setTextureBackground(const char *name, int x, int y, int w, int h, const std::vector &mapTiles); // Override a part of the VRAM from another part of the VRAM, typically with biggest textures (Worldmap) bool setTextureRedirection(const TextureInfos &oldTexture, const TextureInfos &newTexture, uint32_t *imageData); uint8_t getMaxScale(const uint8_t *texData) const; void getTextureNames(const uint8_t *texData, std::list &names) const; - void registerTiledTex(const uint8_t *texData, int x, int y, uint8_t bpp, int palX = 0, int palY = 0); + void registerTiledTex(const uint8_t *texData, int x, int y, Tim::Bpp bpp, int palX = 0, int palY = 0); + void disableDrawTexturesBackground(bool disabled); TextureTypes drawTextures(const uint8_t *texData, struct texture_format *tex_format, uint32_t *target, const uint32_t *originalImageData, int originalW, int originalH, uint8_t scale, uint32_t paletteIndex); - bool saveVram(const char *fileName, uint8_t bpp) const; + bool saveVram(const char *fileName, Tim::Bpp bpp) const; private: inline uint8_t *vramSeek(int x, int y) const { return _vram + VRAM_DEPTH * (x + y * VRAM_WIDTH); } + inline static ModdedTextureId makeTextureId(int x, int y) { + return x + y * VRAM_WIDTH; + } class Texture : public TextureInfos { public: Texture(); Texture( const char *name, int x, int y, int w, int h, - uint8_t bpp + Tim::Bpp bpp ); inline const std::string &name() const { return _name; @@ -115,7 +124,7 @@ class TexturePacker { inline uint8_t scale() const { return _scale; } - bool createImage(uint8_t palette_index = 0); + bool createImage(uint8_t palette_index = 0, bool has_pal = true); void destroyImage(); inline bool hasImage() const { return _image != nullptr; @@ -123,19 +132,42 @@ class TexturePacker { inline bool isValid() const { return _scale != 0; } - void copyRect(int textureX, int textureY, uint32_t *target, int targetX, int targetY, int targetW, uint8_t targetScale) const; + void copyRect(int sourceXBpp2, int sourceYBpp2, uint32_t *target, int targetX, int targetY, int targetW, uint8_t targetScale) const; + protected: + const bimg::ImageContainer *image() const { + return _image; + } + virtual uint8_t computeScale() const; private: - uint8_t computeScale() const; bimg::ImageContainer *_image; std::string _name; uint8_t _scale; }; + class TextureBackground : public Texture { + public: + TextureBackground(); + TextureBackground( + const char *name, + int x, int y, int w, int h, + const std::vector &mapTiles + ); + bool createImage() { + return Texture::createImage(0, false); + } + void copyRect(int sourceXBpp2, int sourceYBpp2, Tim::Bpp textureBpp, uint32_t *target, int targetX, int targetY, int targetW, uint8_t targetScale) const; + private: + virtual uint8_t computeScale() const override; + std::vector _mapTiles; + std::unordered_multimap _tileIdsByPosition; + uint8_t _colsCount; + }; struct TiledTex { TiledTex(); - TiledTex(int x, int y, uint8_t bpp, int palX, int palY); + TiledTex(int x, int y, Tim::Bpp bpp, int palX, int palY); int x, y; int palX, palY; - uint8_t bpp; + Tim::Bpp bpp; + bool renderedOnce; }; struct TextureRedirection : public TextureInfos { TextureRedirection(); @@ -164,14 +196,18 @@ class TexturePacker { TextureInfos _oldTexture; uint8_t _scale; }; + void setVramTextureId(ModdedTextureId textureId, int x, int y, int w, int h, bool keepMods = false); TextureTypes drawTextures(uint32_t *target, const TiledTex &tiledTex, int w, int h, uint8_t scale, uint32_t paletteIndex); void cleanVramTextureIds(const TextureInfos &texture); void cleanTextures(ModdedTextureId textureId, bool keepMods = false); uint8_t *_vram; // uint16_t[VRAM_WIDTH * VRAM_HEIGHT] aka uint8_t[VRAM_WIDTH * VRAM_HEIGHT * VRAM_DEPTH] - std::map _tiledTexs; - ModdedTextureId _vramTextureIds[VRAM_WIDTH * VRAM_HEIGHT]; - std::map _textures; - std::map _externalTextures; - std::map _textureRedirections; + std::unordered_map _tiledTexs; + std::vector _vramTextureIds; // ModdedTextureId[VRAM_WIDTH * VRAM_HEIGHT] + std::unordered_map _textures; + std::unordered_map _externalTextures; + std::unordered_map _textureRedirections; + std::unordered_map _backgroundTextures; + + bool _disableDrawTexturesBackground; }; diff --git a/src/ff8/vram.cpp b/src/ff8/vram.cpp index 5c2f95a8..25155030 100644 --- a/src/ff8/vram.cpp +++ b/src/ff8/vram.cpp @@ -24,6 +24,11 @@ #include "../ff8.h" #include "../patch.h" #include "../image/tim.h" +#include "field/background.h" +#include "field/chara_one.h" + +#include +#include TexturePacker texturePacker; @@ -34,10 +39,19 @@ int next_psxvram_y = -1; int next_psxvram_pal_x = -1; int next_psxvram_pal_y = -1; int next_texl_id = 0; -uint8_t next_bpp = 2; +Tim::Bpp next_bpp = Tim::Bpp16; uint8_t next_scale = 1; int8_t texl_id_left = -1; int8_t texl_id_right = -1; +// Field background +uint8_t *mim_texture_buffer = nullptr; +// Field models +std::unordered_map chara_one_models; +std::vector chara_one_loaded_models; +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; void ff8_upload_vram(int16_t *pos_and_size, uint8_t *texture_buffer) { @@ -47,9 +61,10 @@ void ff8_upload_vram(int16_t *pos_and_size, uint8_t *texture_buffer) const int h = pos_and_size[3]; bool isPal = next_pal_data != nullptr && (uint8_t *)next_pal_data == texture_buffer; - if (trace_all || trace_vram) ffnx_trace("%s x=%d y=%d w=%d h=%d bpp=%d isPal=%d\n", __func__, x, y, w, h, next_bpp, isPal); + if (trace_all || trace_vram) ffnx_trace("%s x=%d y=%d w=%d h=%d bpp=%d isPal=%d texture_buffer=0x%X\n", __func__, x, y, w, h, next_bpp, isPal, texture_buffer); - texturePacker.setTexture(next_texture_name, texture_buffer, x, y, w, h, next_bpp, isPal); + texturePacker.uploadTexture(texture_buffer, x, y, w, h); + texturePacker.setTexture(next_texture_name, x, y, w, h, next_bpp, isPal); ff8_externals.sub_464850(x, y, x + w - 1, h + y - 1); @@ -119,7 +134,7 @@ int read_vram_to_buffer_with_palette1_parent_call2(texture_page *tex_page, int r 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=%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); + 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); if (next_psxvram_x == -1) { @@ -127,7 +142,7 @@ void read_vram_to_buffer(uint8_t *vram, int vram_w_2048, uint8_t *target, int ta } else { - texturePacker.registerTiledTex(target, next_psxvram_x, next_psxvram_y, bpp); + texturePacker.registerTiledTex(target, next_psxvram_x, next_psxvram_y, Tim::Bpp(bpp)); } ff8_externals.read_vram_1(vram, vram_w_2048, target, target_w, w, h, bpp); @@ -135,7 +150,7 @@ void read_vram_to_buffer(uint8_t *vram, int vram_w_2048, uint8_t *target, int ta void read_vram_to_buffer_with_palette1(uint8_t *vram, int vram_w_2048, uint8_t *target, int target_w, int w, int h, int bpp, uint16_t *vram_palette) { - if (trace_all || trace_vram) ffnx_trace("%s: vram_pos=(%d, %d) target=%X target_w=%d w=%d h=%d bpp=%d vram_palette=%X\n", __func__, next_psxvram_x, next_psxvram_y, int(target), target_w, w, h, bpp, int(vram_palette)); + if (trace_all || trace_vram) ffnx_trace("%s: vram_pos=(%d, %d) target=0x%X target_w=%d w=%d h=%d bpp=%d vram_palette=%X\n", __func__, next_psxvram_x, next_psxvram_y, int(target), target_w, w, h, bpp, int(vram_palette)); if (next_psxvram_x == -1) { @@ -143,7 +158,7 @@ void read_vram_to_buffer_with_palette1(uint8_t *vram, int vram_w_2048, uint8_t * } else { - texturePacker.registerTiledTex(target, next_psxvram_x, next_psxvram_y, bpp, next_psxvram_pal_x, next_psxvram_pal_y); + texturePacker.registerTiledTex(target, next_psxvram_x, next_psxvram_y, Tim::Bpp(bpp), next_psxvram_pal_x, next_psxvram_pal_y); } ff8_externals.read_vram_2_paletted(vram, vram_w_2048, target, target_w, w, h, bpp, vram_palette); @@ -151,7 +166,7 @@ void read_vram_to_buffer_with_palette1(uint8_t *vram, int vram_w_2048, uint8_t * void read_vram_to_buffer_with_palette2(uint8_t *vram, uint8_t *target, int w, int h, int bpp, uint16_t *vram_palette) { - if (trace_all || trace_vram) ffnx_trace("%s: vram_pos=(%d, %d) target=%X w=%d h=%d bpp=%d vram_palette=%X\n", __func__, next_psxvram_x, next_psxvram_y, int(target), w, h, bpp, int(vram_palette)); + if (trace_all || trace_vram) ffnx_trace("%s: vram_pos=(%d, %d) target=0x%X w=%d h=%d bpp=%d vram_palette=%X\n", __func__, next_psxvram_x, next_psxvram_y, int(target), w, h, bpp, int(vram_palette)); if (next_psxvram_x == -1) { @@ -159,7 +174,7 @@ void read_vram_to_buffer_with_palette2(uint8_t *vram, uint8_t *target, int w, in } else { - texturePacker.registerTiledTex(target, next_psxvram_x, next_psxvram_y, bpp, next_psxvram_pal_x, next_psxvram_pal_y); + texturePacker.registerTiledTex(target, next_psxvram_x, next_psxvram_y, Tim::Bpp(bpp), next_psxvram_pal_x, next_psxvram_pal_y); } ff8_externals.read_vram_3_paletted(vram, target, w, h, bpp, vram_palette); @@ -171,7 +186,7 @@ uint32_t ff8_credits_open_texture(char *fileName, char *buffer) // {name}.lzs strncpy(next_texture_name, strrchr(fileName, '\\') + 1, sizeof(next_texture_name)); - next_bpp = 2; + next_bpp = Tim::Bpp16; uint32_t ret = ff8_externals.credits_open_file(fileName, buffer); @@ -185,7 +200,7 @@ void ff8_cdcheck_error_upload_vram(int16_t *pos_and_size, uint8_t *texture_buffe if (trace_all || trace_vram) ffnx_trace("%s\n", __func__); strncpy(next_texture_name, "discerr.lzs", sizeof(next_texture_name)); - next_bpp = 2; + next_bpp = Tim::Bpp16; if (save_textures) Tim::fromLzsData(texture_buffer - 8).save(next_texture_name); @@ -199,12 +214,12 @@ void ff8_upload_vram_triple_triad_1(int16_t *pos_and_size, uint8_t *texture_buff if (texture_buffer == ff8_externals.cardgame_tim_texture_intro) { strncpy(next_texture_name, "cardgame/intro", sizeof(next_texture_name)); - next_bpp = 2; + next_bpp = Tim::Bpp16; } else if (texture_buffer == ff8_externals.cardgame_tim_texture_game) { strncpy(next_texture_name, "cardgame/game", sizeof(next_texture_name)); - next_bpp = 2; + next_bpp = Tim::Bpp16; } if (save_textures && *next_texture_name != '\0') @@ -213,7 +228,7 @@ void ff8_upload_vram_triple_triad_1(int16_t *pos_and_size, uint8_t *texture_buff tim.img_w = pos_and_size[2]; tim.img_h = pos_and_size[3]; tim.img_data = texture_buffer; - Tim(next_bpp, tim).save(next_texture_name); + Tim(Tim::Bpp16, tim).save(next_texture_name); } ff8_upload_vram(pos_and_size, texture_buffer); @@ -228,7 +243,7 @@ void ff8_upload_vram_triple_triad_2_texture_name(uint8_t *texture_buffer) { if (save_textures) Tim::fromTimData(texture_buffer - 20).saveMultiPaletteGrid(next_texture_name, 28, 4, 128, 2, true); } - next_bpp = 1; + next_bpp = Tim::Bpp8; } else if (texture_buffer >= ff8_externals.cardgame_tim_texture_icons && texture_buffer < ff8_externals.cardgame_tim_texture_font) { @@ -237,7 +252,7 @@ void ff8_upload_vram_triple_triad_2_texture_name(uint8_t *texture_buffer) { if (save_textures) Tim::fromTimData(texture_buffer - 20).save(next_texture_name, 0, 0, true); } - next_bpp = 0; + next_bpp = Tim::Bpp4; } } @@ -247,7 +262,7 @@ void ff8_upload_vram_triple_triad_2_palette(int16_t *pos_and_size, uint8_t *text next_pal_data = (uint16_t *)texture_buffer; ff8_upload_vram_triple_triad_2_texture_name(texture_buffer); - next_bpp = 2; + next_bpp = Tim::Bpp16; ff8_upload_vram(pos_and_size, texture_buffer); } @@ -278,7 +293,7 @@ uint32_t ff8_wm_section_38_prepare_texture_for_upload(uint8_t *tim_file_data, ff snprintf(next_texture_name, MAX_PATH, "world/dat/wmset/section38/texture%d", timId); - next_bpp = bpp; + next_bpp = Tim::Bpp(bpp); uint32_t ret = ff8_externals.worldmap_prepare_tim_for_upload(tim_file_data, tim_infos); @@ -332,7 +347,7 @@ void ff8_wm_texl_palette_upload_vram(int16_t *pos_and_size, uint8_t *texture_buf if (save_textures) tim.saveMultiPaletteGrid(next_texture_name, 4, 4, 0, 4, true); - next_bpp = 1; + next_bpp = Tim::Bpp8; ff8_upload_vram(pos_and_size, texture_buffer); @@ -377,8 +392,8 @@ void ff8_wm_texl_palette_upload_vram(int16_t *pos_and_size, uint8_t *texture_buf return; // TODO } - TexturePacker::TextureInfos oldTexture(oldX, oldY, tim.imageWidth() / 8, tim.imageHeight() / 2, 0), - newTexture(newX, 256, tim.imageWidth() / 2, tim.imageHeight(), tim.bpp()); + TexturePacker::TextureInfos oldTexture(oldX, oldY, tim.imageWidth() / 8, tim.imageHeight() / 2, Tim::Bpp4), + newTexture(newX, 256, tim.imageWidth() / 2, tim.imageHeight(), Tim::Bpp(tim.bpp())); uint32_t image_data_size = newTexture.pixelW() * newTexture.h() * 4; uint32_t *image = (uint32_t*)driver_malloc(image_data_size); @@ -405,6 +420,143 @@ void ff8_wm_texl_palette_upload_vram(int16_t *pos_and_size, uint8_t *texture_buf ff8_externals.psx_texture_pages[0].struc_50_array[19].vram_needs_reload = 0xFF; } +void ff8_field_mim_palette_upload_vram(int16_t *pos_and_size, uint8_t *texture_buffer) +{ + if (trace_all || trace_vram) ffnx_trace("%s\n", __func__); + + mim_texture_buffer = texture_buffer; + + ff8_upload_vram(pos_and_size, texture_buffer); +} + +uint32_t ff8_field_read_map_data(char *filename, uint8_t *map_data) +{ + if (trace_all || trace_vram) ffnx_trace("%s %s\n", __func__, filename); + + uint32_t ret = ff8_externals.sm_pc_read(filename, map_data); + + char tex_filename[MAX_PATH] = {}; + + snprintf(tex_filename, MAX_PATH, "field/mapdata/%s/%s", get_current_field_name(), get_current_field_name()); + + std::vector tiles = ff8_background_parse_tiles(map_data); + + if (save_textures) { + ff8_background_save_textures(tiles, mim_texture_buffer, tex_filename); + } + + texturePacker.setTextureBackground(tex_filename, 0, 256, VRAM_PAGE_MIM_MAX_COUNT * TEXTURE_WIDTH_BPP16, TEXTURE_HEIGHT, tiles); + + return ret; +} + +int ff8_field_chara_one_read_file_header(int fd, uint8_t *const data, size_t size) +{ + int read = ((int(*)(int,uint8_t*const,size_t))ff8_externals.chara_one_read_file)(fd, data, size); + + if (trace_all || trace_vram) ffnx_trace("%s: size=%d\n", __func__, size); + + chara_one_models = ff8_chara_one_parse_models(data, size); + chara_one_loaded_models.clear(); + chara_one_current_model = 0; + chara_one_current_mch = 0; + chara_one_current_texture = 0; + + return read; +} + +int ff8_field_chara_one_seek_to_model(int fd, int pos, int whence) +{ + if (trace_all || trace_vram) ffnx_trace("%s: pos=0x%X whence=%d\n", __func__, pos, whence); + + chara_one_current_pos = pos; + + return ((int(*)(int,int,int))ff8_externals.chara_one_seek_file)(fd, pos, whence); +} + +int ff8_field_chara_one_read_model(int fd, uint8_t *const data, size_t size) +{ + int read = ((int(*)(int,uint8_t*const,size_t))ff8_externals.chara_one_read_file)(fd, data, size); + + if (trace_all || trace_vram) ffnx_trace("%s: size=%d\n", __func__, size); + + if (chara_one_models.contains(chara_one_current_pos)) { + if (save_textures) { + char filename[MAX_PATH]; + snprintf(filename, sizeof(filename), "field/model/second_chr"); + ff8_chara_one_model_save_textures(chara_one_models[chara_one_current_pos], data, filename); + } + + chara_one_loaded_models.push_back(chara_one_current_pos); + } + + return read; +} + +int ff8_field_chara_one_read_mch(int fd, uint8_t *const data, size_t size) +{ + int read = ((int(*)(int,uint8_t*const,size_t))ff8_externals.chara_one_read_file)(fd, data, size); + + if (trace_all || trace_vram) ffnx_trace("%s: size=%d\n", __func__, size); + + int id = 0; + for (uint32_t addr: chara_one_loaded_models) { + if (chara_one_models[addr].isMch) { + if (id == chara_one_current_mch) { + ff8_mch_parse_model(chara_one_models[addr], data, size); + if (save_textures) { + char filename[MAX_PATH]; + snprintf(filename, sizeof(filename), "field/model/main_chr"); + ff8_chara_one_model_save_textures(chara_one_models[addr], data, filename); + } + + break; + } + ++id; + } + } + + ++chara_one_current_mch; + + return read; +} + +int ff8_field_texture_upload_one(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 bpp=%d a3=%d image_buffer=0x%X\n", __func__, bpp, a3, image_buffer); + + while (chara_one_current_model < chara_one_loaded_models.size()) { + CharaOneModel model = chara_one_models[chara_one_loaded_models.at(chara_one_current_model)]; + + if (chara_one_current_texture < model.texturesData.size()) { + next_bpp = Tim::Bpp(bpp); + snprintf(next_texture_name, MAX_PATH, "field/model/%s_chr/%s-%d", model.isMch ? "main" : "second", model.name, chara_one_current_texture); + + ++chara_one_current_texture; + break; + } + + ++chara_one_current_model; + chara_one_current_texture = 0; + } + + return ((int(*)(char*,char,char,int,int16_t,int,int16_t))ff8_externals.chara_one_upload_texture)(image_buffer, bpp, a3, x, y, w, h); +} + +DWORD *create_graphics_object_load_texture_call2(int a1, int a2, char *path, void *data, ff8_game_obj *game_object) +{ + if (trace_all || trace_vram) ffnx_trace("%s path=%s\n", __func__, path == nullptr ? "(none)" : path); + + // Optimization: when the game creates 3D objects for field background, it does upload textures, but it does not display it + texturePacker.disableDrawTexturesBackground(true); + + DWORD *ret = ((DWORD*(*)(int,int,char*,void*,ff8_game_obj*))ff8_externals.sub_4076B6)(a1, a2, path, data, game_object); + + texturePacker.disableDrawTexturesBackground(false); + + return ret; +} + void vram_init() { texturePacker.setVram((uint8_t *)ff8_externals.psxvram_buffer); @@ -425,6 +577,15 @@ void vram_init() replace_call(ff8_externals.upload_psxvram_texl_pal_call2, ff8_wm_texl_palette_upload_vram); replace_call(ff8_externals.open_file_world_sub_52D670_texl_call1, ff8_wm_open_data); replace_call(ff8_externals.open_file_world_sub_52D670_texl_call2, ff8_wm_open_data); + // field: mim/map + replace_call(ff8_externals.upload_mim_file + 0x2E, ff8_field_mim_palette_upload_vram); + replace_call(ff8_externals.read_field_data + (JP_VERSION ? 0x990 : 0x915), ff8_field_read_map_data); + // field: chara.one + replace_call(ff8_externals.load_field_models + 0x15F, ff8_field_chara_one_read_file_header); + replace_call(ff8_externals.load_field_models + 0x582, ff8_field_chara_one_seek_to_model); + replace_call(ff8_externals.load_field_models + 0x594, ff8_field_chara_one_read_model); + replace_call(ff8_externals.load_field_models + 0x879, ff8_field_chara_one_read_mch); + replace_call(ff8_externals.load_field_models + 0xB72, ff8_field_texture_upload_one); replace_function(ff8_externals.upload_psx_vram, ff8_upload_vram); @@ -449,6 +610,8 @@ void vram_init() replace_call(ff8_externals.sub_464DB0 + 0xEC, read_vram_to_buffer_with_palette1); replace_call(ff8_externals.sub_465720 + 0xA5, read_vram_to_buffer_with_palette1); + replace_call(ff8_externals._load_texture + 0x24E, create_graphics_object_load_texture_call2); + // Not used? replace_call(ff8_externals.sub_4649A0 + 0x13F, read_vram_to_buffer_with_palette2); } diff --git a/src/ff8_data.cpp b/src/ff8_data.cpp index 4e21cd2e..a2ad932c 100644 --- a/src/ff8_data.cpp +++ b/src/ff8_data.cpp @@ -329,7 +329,13 @@ void ff8_find_externals() ff8_externals.upload_mim_file = get_relative_call(ff8_externals.read_field_data, JP_VERSION ? 0x723 : 0x729); ff8_externals.field_filename = (char *)get_absolute_value(ff8_externals.read_field_data, 0xF0); + ff8_externals.field_scripts_init = get_relative_call(ff8_externals.read_field_data, JP_VERSION ? 0xEDC : 0xE49); + ff8_externals.field_state_background_count = (uint8_t *)get_absolute_value(ff8_externals.field_scripts_init, 0x2CD + 0x1); + ff8_externals.field_state_backgrounds = (ff8_field_state_background **)get_absolute_value(ff8_externals.field_scripts_init, 0x50B + 0x2); 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_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); diff --git a/src/image/tim.cpp b/src/image/tim.cpp index 99d18fdf..6e1ad04e 100644 --- a/src/image/tim.cpp +++ b/src/image/tim.cpp @@ -26,10 +26,10 @@ #include "../log.h" #include "../saveload.h" -Tim::Tim(uint8_t bpp, const ff8_tim &tim) : +Tim::Tim(Bpp bpp, const ff8_tim &tim) : _bpp(bpp), _tim(tim) { - _tim.img_w *= 4 >> bpp; + _tim.img_w *= 4 >> int(bpp); } uint32_t PaletteDetectionStrategyFixed::palOffset(uint16_t, uint16_t) const @@ -59,7 +59,7 @@ PaletteDetectionStrategyGrid::PaletteDetectionStrategyGrid(const Tim *const tim, { if (_colorsPerPal == 0) { - _colorsPerPal = tim->bpp() == 0 ? 16 : 256; + _colorsPerPal = tim->bpp() == Tim::Bpp4 ? 16 : 256; } _palCols = tim->_tim.pal_w / _colorsPerPal; _cellWidth = tim->_tim.img_w / _cellCols; @@ -68,7 +68,7 @@ PaletteDetectionStrategyGrid::PaletteDetectionStrategyGrid(const Tim *const tim, bool PaletteDetectionStrategyGrid::isValid() const { - if (_tim->_bpp >= 2) + if (_tim->_bpp == Tim::Bpp16) { ffnx_error("PaletteDetectionStrategyGrid::%s bpp should not be 2\n", __func__); return false; @@ -125,7 +125,7 @@ bool Tim::toRGBA32(uint32_t *target, PaletteDetectionStrategy *paletteDetectionS return false; } - if (_bpp == 0) + if (_bpp == Bpp4) { if (_tim.pal_data == nullptr || paletteDetectionStrategy == nullptr) { @@ -151,7 +151,7 @@ bool Tim::toRGBA32(uint32_t *target, PaletteDetectionStrategy *paletteDetectionS } } } - else if (_bpp == 1) + else if (_bpp == Bpp8) { if (_tim.pal_data == nullptr || paletteDetectionStrategy == nullptr) { @@ -173,7 +173,7 @@ bool Tim::toRGBA32(uint32_t *target, PaletteDetectionStrategy *paletteDetectionS } } } - else if (_bpp == 2) + else if (_bpp == Bpp16) { uint16_t *img_data16 = (uint16_t *)_tim.img_data; @@ -209,8 +209,7 @@ bool Tim::save(const char *fileName, PaletteDetectionStrategy *paletteDetectionS { if (toRGBA32(image_data, paletteDetectionStrategy, withAlpha)) { - // TODO: is animated - save_texture(image_data, image_data_size, _tim.img_w, _tim.img_h, paletteDetectionStrategy->palIndex(), fileName, false); + save_texture(image_data, image_data_size, _tim.img_w, _tim.img_h, paletteDetectionStrategy != nullptr ? paletteDetectionStrategy->palIndex() : 0, fileName, false); } driver_free(image_data); @@ -219,15 +218,15 @@ bool Tim::save(const char *fileName, PaletteDetectionStrategy *paletteDetectionS return true; } -Tim Tim::fromLzsData(uint8_t *uncompressed_data) +Tim Tim::fromLzsData(const uint8_t *uncompressed_data) { - uint16_t *header = (uint16_t *)uncompressed_data; + const uint16_t *header = (const uint16_t *)uncompressed_data; ff8_tim tim_infos = ff8_tim(); tim_infos.img_w = header[2]; tim_infos.img_h = header[3]; - tim_infos.img_data = uncompressed_data + 8; + tim_infos.img_data = (uint8_t *)uncompressed_data + 8; - return Tim(2, tim_infos); + return Tim(Bpp::Bpp16, tim_infos); } struct TimDataHeader { @@ -236,9 +235,9 @@ struct TimDataHeader { uint16_t w, h; }; -Tim Tim::fromTimData(uint8_t *data) +Tim Tim::fromTimData(const uint8_t *data) { - uint8_t bpp = data[4] & 3; + Bpp bpp = Bpp(data[4] & 3); bool hasPal = (data[4] & 8) != 0; TimDataHeader palHeader = TimDataHeader(); ff8_tim tim_infos = ff8_tim(); @@ -257,7 +256,7 @@ Tim Tim::fromTimData(uint8_t *data) TimDataHeader imgHeader = TimDataHeader(); memcpy(&imgHeader, data + 8 + palHeader.size, sizeof(imgHeader)); - tim_infos.img_data = data + 8 + palHeader.size + sizeof(imgHeader); + tim_infos.img_data = (uint8_t *)data + 8 + palHeader.size + sizeof(imgHeader); tim_infos.img_x = imgHeader.x; tim_infos.img_y = imgHeader.y; tim_infos.img_w = imgHeader.w; @@ -268,11 +267,11 @@ Tim Tim::fromTimData(uint8_t *data) uint16_t Tim::colorsPerPal() const { - if (_bpp == 1) + if (_bpp == Bpp8) { return 256; } - else if (_bpp == 0) + else if (_bpp == Bpp4) { return 16; } diff --git a/src/image/tim.h b/src/image/tim.h index c52b0693..4923bf4e 100644 --- a/src/image/tim.h +++ b/src/image/tim.h @@ -85,9 +85,15 @@ class Tim { friend class PaletteDetectionStrategyFixed; friend class PaletteDetectionStrategyGrid; public: - Tim(uint8_t bpp, const ff8_tim &tim); + enum Bpp { + Bpp4 = 0, + Bpp8 = 1, + Bpp16 = 2 + }; + + Tim(Bpp bpp, const ff8_tim &tim); uint16_t colorsPerPal() const; - inline uint8_t bpp() const { + inline Bpp bpp() const { return _bpp; } inline uint16_t imageX() const { @@ -124,11 +130,11 @@ class Tim { uint32_t *target, uint8_t cellCols, uint8_t cellRows, uint8_t colorsPerPal = 0, uint8_t palColsPerRow = 1, bool withAlpha = false ) const; - static Tim fromLzsData(uint8_t *uncompressed_data); - static Tim fromTimData(uint8_t *data); + static Tim fromLzsData(const uint8_t *uncompressed_data); + static Tim fromTimData(const uint8_t *data); private: bool save(const char *fileName, PaletteDetectionStrategy *paletteDetectionStrategy, bool withAlpha) const; bool toRGBA32(uint32_t *target, PaletteDetectionStrategy *paletteDetectionStrategy, bool withAlpha) const; ff8_tim _tim; - uint8_t _bpp; + Bpp _bpp; }; diff --git a/src/music.cpp b/src/music.cpp index 5c396570..2b35c92d 100644 --- a/src/music.cpp +++ b/src/music.cpp @@ -906,7 +906,7 @@ uint32_t ff8_volume_fade(uint32_t channel, uint32_t steps, uint32_t volume1, uin return 1; } -uint32_t ff8_volume_sync() +uint32_t ff8_volume_sync(int a1) { if (trace_all || trace_music) ffnx_trace("%s\n", __func__); diff --git a/src/saveload.cpp b/src/saveload.cpp index cf0d2382..5965e63e 100644 --- a/src/saveload.cpp +++ b/src/saveload.cpp @@ -84,8 +84,14 @@ void save_texture(const void *data, uint32_t dataSize, uint32_t width, uint32_t hash = XXH3_64bits(data, dataSize); _snprintf(xxhash_filename, sizeof(xxhash_filename), "%s/%s/%s_%02i_%llx.png", basedir, mod_path.c_str(), name, palette_index, hash); } - else + else if (palette_index != uint32_t(-1)) + { _snprintf(filename, sizeof(filename), "%s/%s/%s_%02i.png", basedir, mod_path.c_str(), name, palette_index); + } + else + { + _snprintf(filename, sizeof(filename), "%s/%s/%s.png", basedir, mod_path.c_str(), name); + } normalize_path(filename);