diff --git a/src/app/core/common.cc b/src/app/core/common.cc index 18e28907..0dfc1045 100644 --- a/src/app/core/common.cc +++ b/src/app/core/common.cc @@ -171,8 +171,6 @@ bool StringReplace(std::string &str, const std::string &from, return true; } -std::shared_ptr ExperimentFlags::flags_; - uint32_t Get24LocalFromPC(uint8_t *data, int addr, bool pc) { uint32_t ret = (PcToSnes(addr) & 0xFF0000) | (data[addr + 1] << 8) | data[addr]; diff --git a/src/app/core/common.h b/src/app/core/common.h index 50e524a3..9d0da523 100644 --- a/src/app/core/common.h +++ b/src/app/core/common.h @@ -96,57 +96,44 @@ class ExperimentFlags { } overworld; }; - ExperimentFlags() = default; - virtual ~ExperimentFlags() = default; - auto flags() const { - if (!flags_) { - flags_ = std::make_shared(); - } - Flags *flags = flags_.get(); - return flags; - } - Flags *mutable_flags() { - if (!flags_) { - flags_ = std::make_shared(); - } - return flags_.get(); + static Flags &get() { + static Flags instance; + return instance; } + std::string Serialize() const { std::string result; result += - "kLogInstructions: " + std::to_string(flags_->kLogInstructions) + "\n"; + "kLogInstructions: " + std::to_string(get().kLogInstructions) + "\n"; result += - "kSaveAllPalettes: " + std::to_string(flags_->kSaveAllPalettes) + "\n"; + "kSaveAllPalettes: " + std::to_string(get().kSaveAllPalettes) + "\n"; + result += "kSaveGfxGroups: " + std::to_string(get().kSaveGfxGroups) + "\n"; result += - "kSaveGfxGroups: " + std::to_string(flags_->kSaveGfxGroups) + "\n"; - result += "kSaveWithChangeQueue: " + - std::to_string(flags_->kSaveWithChangeQueue) + "\n"; + "kSaveWithChangeQueue: " + std::to_string(get().kSaveWithChangeQueue) + + "\n"; result += "kDrawDungeonRoomGraphics: " + - std::to_string(flags_->kDrawDungeonRoomGraphics) + "\n"; + std::to_string(get().kDrawDungeonRoomGraphics) + "\n"; result += "kNewFileDialogWrapper: " + - std::to_string(flags_->kNewFileDialogWrapper) + "\n"; + std::to_string(get().kNewFileDialogWrapper) + "\n"; result += "kLoadTexturesAsStreaming: " + - std::to_string(flags_->kLoadTexturesAsStreaming) + "\n"; + std::to_string(get().kLoadTexturesAsStreaming) + "\n"; result += - "kSaveDungeonMaps: " + std::to_string(flags_->kSaveDungeonMaps) + "\n"; - result += "kLogToConsole: " + std::to_string(flags_->kLogToConsole) + "\n"; + "kSaveDungeonMaps: " + std::to_string(get().kSaveDungeonMaps) + "\n"; + result += "kLogToConsole: " + std::to_string(get().kLogToConsole) + "\n"; result += "kDrawOverworldSprites: " + - std::to_string(flags_->overworld.kDrawOverworldSprites) + "\n"; + std::to_string(get().overworld.kDrawOverworldSprites) + "\n"; result += "kSaveOverworldMaps: " + - std::to_string(flags_->overworld.kSaveOverworldMaps) + "\n"; + std::to_string(get().overworld.kSaveOverworldMaps) + "\n"; result += "kSaveOverworldEntrances: " + - std::to_string(flags_->overworld.kSaveOverworldEntrances) + "\n"; + std::to_string(get().overworld.kSaveOverworldEntrances) + "\n"; result += "kSaveOverworldExits: " + - std::to_string(flags_->overworld.kSaveOverworldExits) + "\n"; + std::to_string(get().overworld.kSaveOverworldExits) + "\n"; result += "kSaveOverworldItems: " + - std::to_string(flags_->overworld.kSaveOverworldItems) + "\n"; + std::to_string(get().overworld.kSaveOverworldItems) + "\n"; result += "kSaveOverworldProperties: " + - std::to_string(flags_->overworld.kSaveOverworldProperties) + "\n"; + std::to_string(get().overworld.kSaveOverworldProperties) + "\n"; return result; } - - private: - static std::shared_ptr flags_; }; /** diff --git a/src/app/core/project.h b/src/app/core/project.h index 8d844ee4..b12cc3a7 100644 --- a/src/app/core/project.h +++ b/src/app/core/project.h @@ -24,7 +24,7 @@ constexpr char kEndOfProjectFile[] = "EndOfProjectFile"; * user can have different rom file names for a single project and keep track of * backups. */ -struct Project : public core::ExperimentFlags { +struct Project { absl::Status Create(const std::string& project_name) { name = project_name; project_opened_ = true; diff --git a/src/app/editor/dungeon/dungeon_editor.cc b/src/app/editor/dungeon/dungeon_editor.cc index 975e0410..0887fffb 100644 --- a/src/app/editor/dungeon/dungeon_editor.cc +++ b/src/app/editor/dungeon/dungeon_editor.cc @@ -77,7 +77,7 @@ absl::Status DungeonEditor::Initialize() { rooms_.emplace_back(zelda3::Room(/*room_id=*/i)); rooms_[i].LoadHeader(); rooms_[i].LoadRoomFromROM(); - if (flags()->kDrawDungeonRoomGraphics) { + if (core::ExperimentFlags::get().kDrawDungeonRoomGraphics) { rooms_[i].LoadRoomGraphics(); } diff --git a/src/app/editor/dungeon/dungeon_editor.h b/src/app/editor/dungeon/dungeon_editor.h index 5d426105..076150bd 100644 --- a/src/app/editor/dungeon/dungeon_editor.h +++ b/src/app/editor/dungeon/dungeon_editor.h @@ -1,11 +1,11 @@ #ifndef YAZE_APP_EDITOR_DUNGEONEDITOR_H #define YAZE_APP_EDITOR_DUNGEONEDITOR_H -#include "app/core/common.h" #include "absl/container/flat_hash_map.h" +#include "app/core/common.h" +#include "app/editor/editor.h" #include "app/editor/graphics/gfx_group_editor.h" #include "app/editor/graphics/palette_editor.h" -#include "app/editor/editor.h" #include "app/gui/canvas.h" #include "app/rom.h" #include "imgui/imgui.h" @@ -39,9 +39,7 @@ constexpr ImGuiTableFlags kDungeonTableFlags = * tile selector, and object renderer. Additionally, it handles loading room * entrances, calculating usage statistics, and rendering set usage. */ -class DungeonEditor : public Editor, - public SharedRom, - public core::ExperimentFlags { +class DungeonEditor : public Editor, public SharedRom { public: DungeonEditor() { type_ = EditorType::kDungeon; } diff --git a/src/app/editor/editor_manager.cc b/src/app/editor/editor_manager.cc index 7b813d72..48db5bf2 100644 --- a/src/app/editor/editor_manager.cc +++ b/src/app/editor/editor_manager.cc @@ -683,7 +683,7 @@ void EditorManager::LoadRom() { } void EditorManager::SaveRom() { - if (flags()->kSaveDungeonMaps) { + if (core::ExperimentFlags::get().kSaveDungeonMaps) { status_ = screen_editor_.SaveDungeonMaps(); RETURN_VOID_IF_ERROR(status_); } diff --git a/src/app/editor/editor_manager.h b/src/app/editor/editor_manager.h index 5dcaaa51..04846fe6 100644 --- a/src/app/editor/editor_manager.h +++ b/src/app/editor/editor_manager.h @@ -35,7 +35,7 @@ namespace editor { * variable points to the currently active editor in the tab view. * */ -class EditorManager : public SharedRom, public core::ExperimentFlags { +class EditorManager : public SharedRom { public: EditorManager() { current_editor_ = &overworld_editor_; diff --git a/src/app/editor/overworld/overworld_editor.cc b/src/app/editor/overworld/overworld_editor.cc index 049a3199..285027a3 100644 --- a/src/app/editor/overworld/overworld_editor.cc +++ b/src/app/editor/overworld/overworld_editor.cc @@ -621,7 +621,7 @@ void OverworldEditor::CheckForMousePan() { void OverworldEditor::DrawOverworldCanvas() { if (all_gfx_loaded_) { - if (flags()->overworld.kLoadCustomOverworld) { + if (core::ExperimentFlags::get().overworld.kLoadCustomOverworld) { DrawCustomOverworldMapSettings(); } else { DrawOverworldMapSettings(); @@ -1009,22 +1009,22 @@ void OverworldEditor::DrawOverworldSprites() { } absl::Status OverworldEditor::Save() { - if (flags()->overworld.kSaveOverworldMaps) { + if (core::ExperimentFlags::get().overworld.kSaveOverworldMaps) { RETURN_IF_ERROR(overworld_.CreateTile32Tilemap()); RETURN_IF_ERROR(overworld_.SaveMap32Tiles()); RETURN_IF_ERROR(overworld_.SaveMap16Tiles()); RETURN_IF_ERROR(overworld_.SaveOverworldMaps()); } - if (flags()->overworld.kSaveOverworldEntrances) { + if (core::ExperimentFlags::get().overworld.kSaveOverworldEntrances) { RETURN_IF_ERROR(overworld_.SaveEntrances()); } - if (flags()->overworld.kSaveOverworldExits) { + if (core::ExperimentFlags::get().overworld.kSaveOverworldExits) { RETURN_IF_ERROR(overworld_.SaveExits()); } - if (flags()->overworld.kSaveOverworldItems) { + if (core::ExperimentFlags::get().overworld.kSaveOverworldItems) { RETURN_IF_ERROR(overworld_.SaveItems()); } - if (flags()->overworld.kSaveOverworldProperties) { + if (core::ExperimentFlags::get().overworld.kSaveOverworldProperties) { RETURN_IF_ERROR(overworld_.SaveMapProperties()); } return absl::OkStatus(); @@ -1082,7 +1082,7 @@ absl::Status OverworldEditor::LoadGraphics() { overworld_.current_map_bitmap_data(), maps_bmp_[i], palette)); } - if (flags()->overworld.kDrawOverworldSprites) { + if (core::ExperimentFlags::get().overworld.kDrawOverworldSprites) { RETURN_IF_ERROR(LoadSpriteGraphics()); } diff --git a/src/app/editor/overworld/overworld_editor.h b/src/app/editor/overworld/overworld_editor.h index d81e0515..c5dd58df 100644 --- a/src/app/editor/overworld/overworld_editor.h +++ b/src/app/editor/overworld/overworld_editor.h @@ -72,9 +72,7 @@ constexpr absl::string_view kOWMapTable = "#MapSettingsTable"; * Provides access to the GfxGroupEditor and Tile16Editor through popup windows. * */ -class OverworldEditor : public Editor, - public gfx::GfxContext, - public core::ExperimentFlags { +class OverworldEditor : public Editor, public gfx::GfxContext { public: OverworldEditor(Rom& rom) : rom_(rom) { type_ = EditorType::kOverworld; } diff --git a/src/app/editor/system/flags.h b/src/app/editor/system/flags.h index 6c00b978..fbada123 100644 --- a/src/app/editor/system/flags.h +++ b/src/app/editor/system/flags.h @@ -7,51 +7,53 @@ namespace yaze { namespace editor { +using core::ExperimentFlags; using ImGui::BeginMenu; using ImGui::Checkbox; using ImGui::EndMenu; using ImGui::MenuItem; using ImGui::Separator; -struct FlagsMenu : public core::ExperimentFlags { +struct FlagsMenu { void Draw() { if (BeginMenu("Overworld Flags")) { Checkbox("Enable Overworld Sprites", - &mutable_flags()->overworld.kDrawOverworldSprites); + &ExperimentFlags::get().overworld.kDrawOverworldSprites); Separator(); Checkbox("Save Overworld Maps", - &mutable_flags()->overworld.kSaveOverworldMaps); + &ExperimentFlags::get().overworld.kSaveOverworldMaps); Checkbox("Save Overworld Entrances", - &mutable_flags()->overworld.kSaveOverworldEntrances); + &ExperimentFlags::get().overworld.kSaveOverworldEntrances); Checkbox("Save Overworld Exits", - &mutable_flags()->overworld.kSaveOverworldExits); + &ExperimentFlags::get().overworld.kSaveOverworldExits); Checkbox("Save Overworld Items", - &mutable_flags()->overworld.kSaveOverworldItems); + &ExperimentFlags::get().overworld.kSaveOverworldItems); Checkbox("Save Overworld Properties", - &mutable_flags()->overworld.kSaveOverworldProperties); + &ExperimentFlags::get().overworld.kSaveOverworldProperties); Checkbox("Load Custom Overworld", - &mutable_flags()->overworld.kLoadCustomOverworld); + &ExperimentFlags::get().overworld.kLoadCustomOverworld); ImGui::EndMenu(); } if (BeginMenu("Dungeon Flags")) { Checkbox("Draw Dungeon Room Graphics", - &mutable_flags()->kDrawDungeonRoomGraphics); + &ExperimentFlags::get().kDrawDungeonRoomGraphics); Separator(); - Checkbox("Save Dungeon Maps", &mutable_flags()->kSaveDungeonMaps); + Checkbox("Save Dungeon Maps", &ExperimentFlags::get().kSaveDungeonMaps); ImGui::EndMenu(); } Checkbox("Use built-in file dialog", - &mutable_flags()->kNewFileDialogWrapper); - Checkbox("Enable Console Logging", &mutable_flags()->kLogToConsole); + &ExperimentFlags::get().kNewFileDialogWrapper); + Checkbox("Enable Console Logging", &ExperimentFlags::get().kLogToConsole); Checkbox("Enable Texture Streaming", - &mutable_flags()->kLoadTexturesAsStreaming); + &ExperimentFlags::get().kLoadTexturesAsStreaming); Checkbox("Log Instructions to Debugger", - &mutable_flags()->kLogInstructions); - Checkbox("Save All Palettes", &mutable_flags()->kSaveAllPalettes); - Checkbox("Save Gfx Groups", &mutable_flags()->kSaveGfxGroups); - Checkbox("Save Graphics Sheets", &mutable_flags()->kSaveGraphicsSheet); + &ExperimentFlags::get().kLogInstructions); + Checkbox("Save All Palettes", &ExperimentFlags::get().kSaveAllPalettes); + Checkbox("Save Gfx Groups", &ExperimentFlags::get().kSaveGfxGroups); + Checkbox("Save Graphics Sheets", + &ExperimentFlags::get().kSaveGraphicsSheet); } }; diff --git a/src/app/emu/cpu/cpu.cc b/src/app/emu/cpu/cpu.cc index 665c46e0..b2aa9361 100644 --- a/src/app/emu/cpu/cpu.cc +++ b/src/app/emu/cpu/cpu.cc @@ -1803,7 +1803,7 @@ void Cpu::ExecuteInstruction(uint8_t opcode) { void Cpu::LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand, bool immediate, bool accumulator_mode) { - if (flags()->kLogInstructions) { + if (core::ExperimentFlags::get().kLogInstructions) { std::ostringstream oss; oss << "$" << std::uppercase << std::setw(2) << std::setfill('0') << static_cast(PB) << ":" << std::hex << PC << ": 0x" diff --git a/src/app/emu/cpu/cpu.h b/src/app/emu/cpu/cpu.h index bdd2e460..dded241e 100644 --- a/src/app/emu/cpu/cpu.h +++ b/src/app/emu/cpu/cpu.h @@ -34,7 +34,7 @@ class InstructionEntry { std::string instruction; // Human-readable instruction text }; -class Cpu : public core::ExperimentFlags { +class Cpu { public: explicit Cpu(Memory& mem, Clock& vclock, CpuCallbacks& callbacks) : memory(mem), clock(vclock), callbacks_(callbacks) {} diff --git a/src/app/gfx/compression.cc b/src/app/gfx/compression.cc index dfe09557..20d5a05e 100644 --- a/src/app/gfx/compression.cc +++ b/src/app/gfx/compression.cc @@ -6,7 +6,6 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" -#include "absl/strings/str_cat.h" #include "app/core/constants.h" #include "app/rom.h" @@ -15,9 +14,291 @@ namespace yaze { namespace gfx { -namespace lc_lz2 { +std::vector HyruleMagicCompress(uint8_t const* const src, + int const oldsize, int* const size, + int const flag) { + unsigned char* b2 = + (unsigned char*)malloc(0x1000); // allocate a 2^12 sized buffer -// Compression commands + int i, j, k, l, m = 0, n, o = 0, bd = 0, p, q = 0, r; + + for (i = 0; i < oldsize;) { + l = src[i]; // grab a char from the buffer. + + k = 0; + + r = !!q; // r = the same logical value (0 or 1) as q, but not the same + // value necesarily. + + for (j = 0; j < i - 1; j++) { + if (src[j] == l) { + m = oldsize - j; + + for (n = 0; n < m; n++) + if (src[n + j] != src[n + i]) break; + + if (n > k) k = n, o = j; + } + } + + for (n = i + 1; n < oldsize; n++) { + if (src[n] != l) { + // look for chars identical to the first one. + // stop if we can't find one. + // n will reach i+k+1 for some k >= 0. + + break; + } + } + + n -= i; // offset back by i. i.e. n = k+1 as above. + + if (n > 1 + r) + p = 1; + else { + m = src[i + 1]; + + for (n = i + 2; n < oldsize; n++) { + if (src[n] != l) break; + + n++; + + if (src[n] != m) break; + } + + n -= i; + + if (n > 2 + r) + p = 2; + else { + m = oldsize - i; + + for (n = 1; n < m; n++) + if (src[i + n] != l + n) break; + + if (n > 1 + r) + p = 3; + else + p = 0; + } + } + + if (k > 3 + r && k > n + (p & 1)) p = 4, n = k; + + if (!p) + q++, i++; + else { + if (q) { + q--; + + if (q > 31) { + b2[bd++] = (unsigned char)(224 + (q >> 8)); + } + + b2[bd++] = (unsigned char)q; + q++; + + memcpy(b2 + bd, src + i - q, q); + + bd += q; + q = 0; + } + + i += n; + n--; + + if (n > 31) { + b2[bd++] = (unsigned char)(224 + (n >> 8) + (p << 2)); + b2[bd++] = (unsigned char)n; + } else + b2[bd++] = (unsigned char)((p << 5) + n); + + switch (p) { + case 1: + case 3: + b2[bd++] = (unsigned char)l; + break; + + case 2: + b2[bd++] = (unsigned char)l; + b2[bd++] = (unsigned char)m; + + break; + + case 4: + if (flag) { + b2[bd++] = (unsigned char)(o >> 8); + b2[bd++] = (unsigned char)o; + } else { + b2[bd++] = (unsigned char)o; + b2[bd++] = (unsigned char)(o >> 8); + } + } + + continue; + } + } + + if (q) { + q--; + + if (q > 31) { + b2[bd++] = (unsigned char)(224 + (q >> 8)); + } + + b2[bd++] = (unsigned char)q; + q++; + + memcpy(b2 + bd, src + i - q, q); + + bd += q; + } + + b2[bd++] = 255; + b2 = (unsigned char*)realloc(b2, bd); + *size = bd; + + std::vector compressed_data(b2, b2 + bd); + free(b2); + return compressed_data; +} + +std::vector HyruleMagicDecompress(uint8_t const* src, int* const size, + int const p_big_endian) { + unsigned char* b2 = (unsigned char*)malloc(1024); + + int bd = 0, bs = 1024; + + unsigned char a; + unsigned char b; + unsigned short c, d; + + for (;;) { + // retrieve a uchar from the buffer. + a = *(src++); + + // end the decompression routine if we encounter 0xff. + if (a == 0xff) break; + + // examine the top 3 bits of a. + b = (a >> 5); + + if (b == 7) // i.e. 0b 111 + { + // get bits 0b 0001 1100 + b = ((a >> 2) & 7); + + // get bits 0b 0000 0011, multiply by 256, OR with the next byte. + c = ((a & 0x0003) << 8); + c |= *(src++); + } else + // or get bits 0b 0001 1111 + c = (uint16_t)(a & 31); + + c++; + + if ((bd + c) > (bs - 512)) { + // need to increase the buffer size. + bs += 1024; + b2 = (uint8_t*)realloc(b2, bs); + } + + // 7 was handled, here we handle other decompression codes. + + switch (b) { + case 0: // 0b 000 + + // raw copy + + // copy info from the src buffer to our new buffer, + // at offset bd (which we'll be increasing; + memcpy(b2 + bd, src, c); + + // increment the src pointer accordingly. + src += c; + + bd += c; + + break; + + case 1: // 0b 001 + + // rle copy + + // make c duplicates of one byte, inc the src pointer. + memset(b2 + bd, *(src++), c); + + // increase the b2 offset. + bd += c; + + break; + + case 2: // 0b 010 + + // rle 16-bit alternating copy + + d = core::ldle16b(src); + + src += 2; + + while (c > 1) { + // copy that 16-bit number c/2 times into the b2 buffer. + core::stle16b(b2 + bd, d); + + bd += 2; + c -= 2; // hence c/2 + } + + if (c) // if there's a remainder of c/2, this handles it. + b2[bd++] = (char)d; + + break; + + case 3: // 0b 011 + + // incrementing copy + + // get the current src byte. + a = *(src++); + + while (c--) { + // increment that byte and copy to b2 in c iterations. + // e.g. a = 4, b2 will have 4,5,6,7,8... written to it. + b2[bd++] = a++; + } + + break; + + default: // 0b 100, 101, 110 + + // lz copy + + if (p_big_endian) { + d = (*src << 8) + src[1]; + } else { + d = core::ldle16b(src); + } + + while (c--) { + // copy from a different location in the buffer. + b2[bd++] = b2[d++]; + } + + src += 2; + } + } + + b2 = (unsigned char*)realloc(b2, bd); + + if (size) (*size) = bd; + + // return the unsigned char* buffer b2, which contains the uncompressed data. + std::vector decompressed_data(b2, b2 + bd); + free(b2); + return decompressed_data; +} + +namespace lc_lz2 { void PrintCompressionPiece(const CompressionPiecePointer& piece) { std::cout << "Command: " << std::to_string(piece->command) << "\n"; @@ -178,9 +459,6 @@ void CompressionCommandAlternative(const uchar* rom_data, comp_accumulator = 0; } -// ============================================================================ -// Compression V2 - void CheckByteRepeatV2(const uchar* data, uint& src_pos, const uint last_pos, CompressionCommand& cmd) { uint i = 0; @@ -379,15 +657,17 @@ absl::StatusOr SplitCompressionPiece( new_piece->argument[0] = (char)(piece->argument[0] + kMaxLengthCompression); break; - case kCommandDirectCopy: + case kCommandDirectCopy: { + std::string empty; piece->argument_length = kMaxLengthCompression; new_piece = std::make_shared( - piece->command, length_left, nullptr, length_left); + piece->command, length_left, empty, length_left); // MEMCPY for (int i = 0; i < length_left; ++i) { new_piece->argument[i] = piece->argument[i + kMaxLengthCompression]; } break; + } case kCommandRepeatingBytes: { piece->argument_length = kMaxLengthCompression; uint offset = piece->argument[0] + (piece->argument[1] << 8); @@ -525,7 +805,9 @@ absl::StatusOr> CompressV2(const uchar* data, } // Worst case should be a copy of the string with extended header - auto compressed_chain = std::make_shared(1, 1, "aaa", 2); + std::string worst_case = "aaa"; + auto compressed_chain = + std::make_shared(1, 1, worst_case, 2); auto compressed_chain_start = compressed_chain; CompressionCommand current_cmd = {/*argument*/ {{}}, @@ -589,286 +871,6 @@ absl::StatusOr> CompressV2(const uchar* data, return CreateCompressionString(compressed_chain_start->next, mode); } -// Hyrule Magic -uint8_t* Compress(uint8_t const* const src, int const oldsize, int* const size, - int const flag) { - unsigned char* b2 = - (unsigned char*)malloc(0x1000); // allocate a 2^12 sized buffer - - int i, j, k, l, m = 0, n, o = 0, bd = 0, p, q = 0, r; - - for (i = 0; i < oldsize;) { - l = src[i]; // grab a char from the buffer. - - k = 0; - - r = !!q; // r = the same logical value (0 or 1) as q, but not the same - // value necesarily. - - for (j = 0; j < i - 1; j++) { - if (src[j] == l) { - m = oldsize - j; - - for (n = 0; n < m; n++) - if (src[n + j] != src[n + i]) break; - - if (n > k) k = n, o = j; - } - } - - for (n = i + 1; n < oldsize; n++) { - if (src[n] != l) { - // look for chars identical to the first one. - // stop if we can't find one. - // n will reach i+k+1 for some k >= 0. - - break; - } - } - - n -= i; // offset back by i. i.e. n = k+1 as above. - - if (n > 1 + r) - p = 1; - else { - m = src[i + 1]; - - for (n = i + 2; n < oldsize; n++) { - if (src[n] != l) break; - - n++; - - if (src[n] != m) break; - } - - n -= i; - - if (n > 2 + r) - p = 2; - else { - m = oldsize - i; - - for (n = 1; n < m; n++) - if (src[i + n] != l + n) break; - - if (n > 1 + r) - p = 3; - else - p = 0; - } - } - - if (k > 3 + r && k > n + (p & 1)) p = 4, n = k; - - if (!p) - q++, i++; - else { - if (q) { - q--; - - if (q > 31) { - b2[bd++] = (unsigned char)(224 + (q >> 8)); - } - - b2[bd++] = (unsigned char)q; - q++; - - memcpy(b2 + bd, src + i - q, q); - - bd += q; - q = 0; - } - - i += n; - n--; - - if (n > 31) { - b2[bd++] = (unsigned char)(224 + (n >> 8) + (p << 2)); - b2[bd++] = (unsigned char)n; - } else - b2[bd++] = (unsigned char)((p << 5) + n); - - switch (p) { - case 1: - case 3: - b2[bd++] = (unsigned char)l; - break; - - case 2: - b2[bd++] = (unsigned char)l; - b2[bd++] = (unsigned char)m; - - break; - - case 4: - if (flag) { - b2[bd++] = (unsigned char)(o >> 8); - b2[bd++] = (unsigned char)o; - } else { - b2[bd++] = (unsigned char)o; - b2[bd++] = (unsigned char)(o >> 8); - } - } - - continue; - } - } - - if (q) { - q--; - - if (q > 31) { - b2[bd++] = (unsigned char)(224 + (q >> 8)); - } - - b2[bd++] = (unsigned char)q; - q++; - - memcpy(b2 + bd, src + i - q, q); - - bd += q; - } - - b2[bd++] = 255; - b2 = (unsigned char*)realloc(b2, bd); - *size = bd; - - return b2; -} - -uint8_t* Uncompress(uint8_t const* src, int* const size, - int const p_big_endian) { - unsigned char* b2 = (unsigned char*)malloc(1024); - - int bd = 0, bs = 1024; - - unsigned char a; - unsigned char b; - unsigned short c, d; - - for (;;) { - // retrieve a uchar from the buffer. - a = *(src++); - - // end the decompression routine if we encounter 0xff. - if (a == 0xff) break; - - // examine the top 3 bits of a. - b = (a >> 5); - - if (b == 7) // i.e. 0b 111 - { - // get bits 0b 0001 1100 - b = ((a >> 2) & 7); - - // get bits 0b 0000 0011, multiply by 256, OR with the next byte. - c = ((a & 0x0003) << 8); - c |= *(src++); - } else - // or get bits 0b 0001 1111 - c = (uint16_t)(a & 31); - - c++; - - if ((bd + c) > (bs - 512)) { - // need to increase the buffer size. - bs += 1024; - b2 = (uint8_t*)realloc(b2, bs); - } - - // 7 was handled, here we handle other decompression codes. - - switch (b) { - case 0: // 0b 000 - - // raw copy - - // copy info from the src buffer to our new buffer, - // at offset bd (which we'll be increasing; - memcpy(b2 + bd, src, c); - - // increment the src pointer accordingly. - src += c; - - bd += c; - - break; - - case 1: // 0b 001 - - // rle copy - - // make c duplicates of one byte, inc the src pointer. - memset(b2 + bd, *(src++), c); - - // increase the b2 offset. - bd += c; - - break; - - case 2: // 0b 010 - - // rle 16-bit alternating copy - - d = core::ldle16b(src); - - src += 2; - - while (c > 1) { - // copy that 16-bit number c/2 times into the b2 buffer. - core::stle16b(b2 + bd, d); - - bd += 2; - c -= 2; // hence c/2 - } - - if (c) // if there's a remainder of c/2, this handles it. - b2[bd++] = (char)d; - - break; - - case 3: // 0b 011 - - // incrementing copy - - // get the current src byte. - a = *(src++); - - while (c--) { - // increment that byte and copy to b2 in c iterations. - // e.g. a = 4, b2 will have 4,5,6,7,8... written to it. - b2[bd++] = a++; - } - - break; - - default: // 0b 100, 101, 110 - - // lz copy - - if (p_big_endian) { - d = (*src << 8) + src[1]; - } else { - d = core::ldle16b(src); - } - - while (c--) { - // copy from a different location in the buffer. - b2[bd++] = b2[d++]; - } - - src += 2; - } - } - - b2 = (unsigned char*)realloc(b2, bd); - - if (size) (*size) = bd; - - // return the unsigned char* buffer b2, which contains the uncompressed data. - return b2; -} - absl::StatusOr> CompressGraphics(const uchar* data, const int pos, const int length) { @@ -886,9 +888,6 @@ absl::StatusOr> CompressOverworld( return CompressV3(data, pos, length, kNintendoMode1); } -// ============================================================================ -// Compression V3 - void CheckByteRepeatV3(CompressionContext& context) { uint pos = context.src_pos; @@ -1208,15 +1207,17 @@ absl::StatusOr SplitCompressionPieceV3( piece.argument_length); new_piece.argument[0] = (char)(piece.argument[0] + kMaxLengthCompression); break; - case kCommandDirectCopy: + case kCommandDirectCopy: { piece.argument_length = kMaxLengthCompression; - new_piece = - CompressionPiece(piece.command, length_left, nullptr, length_left); + std::string empty_string = ""; + new_piece = CompressionPiece(piece.command, length_left, empty_string, + length_left); // MEMCPY for (int i = 0; i < length_left; ++i) { new_piece.argument[i] = piece.argument[i + kMaxLengthCompression]; } break; + } case kCommandRepeatingBytes: { piece.argument_length = kMaxLengthCompression; uint offset = piece.argument[0] + (piece.argument[1] << 8); @@ -1339,8 +1340,6 @@ absl::StatusOr> CompressV3( context.compressed_data.end()); } -// Decompression - std::string SetBuffer(const uchar* data, int src_pos, int comp_accumulator) { std::string buffer; for (int i = 0; i < comp_accumulator; ++i) { @@ -1472,5 +1471,4 @@ absl::StatusOr> DecompressOverworld( } // namespace lc_lz2 } // namespace gfx - -} // namespace yaze \ No newline at end of file +} // namespace yaze diff --git a/src/app/gfx/compression.h b/src/app/gfx/compression.h index ec5b2f5e..f074a788 100644 --- a/src/app/gfx/compression.h +++ b/src/app/gfx/compression.h @@ -3,6 +3,7 @@ #include #include +#include #include "absl/status/status.h" #include "absl/status/statusor.h" @@ -13,6 +14,13 @@ namespace yaze { namespace gfx { +std::vector HyruleMagicCompress(uint8_t const* const src, + int const oldsize, int* const size, + int const flag); + +std::vector HyruleMagicDecompress(uint8_t const* src, int* const size, + int const p_big_endian); + /** * @namespace yaze::gfx::lc_lz2 * @brief Contains the LC_LZ2 compression algorithm. @@ -74,7 +82,7 @@ struct CompressionPiece { std::string argument; std::shared_ptr next = nullptr; CompressionPiece() = default; - CompressionPiece(int cmd, int len, std::string args, int arg_len) + CompressionPiece(int cmd, int len, std::string& args, int arg_len) : command(cmd), length(len), argument_length(arg_len), argument(args) {} }; using CompressionPiece = struct CompressionPiece; @@ -138,31 +146,33 @@ void CompressionCommandAlternativeV2(const uchar* data, /** * @brief Compresses a buffer of data using the LC_LZ2 algorithm. - * \deprecated Use Compress and Uncompress instead. + * \deprecated Use HyruleMagicDecompress instead. */ -absl::StatusOr> CompressV2(const uchar* data, const int start, - const int length, int mode = 1, - bool check = false); +absl::StatusOr> CompressV2(const uchar* data, + const int start, + const int length, int mode = 1, + bool check = false); -absl::StatusOr> CompressGraphics(const uchar* data, const int pos, - const int length); -absl::StatusOr> CompressOverworld(const uchar* data, const int pos, - const int length); -absl::StatusOr> CompressOverworld(const std::vector data, - const int pos, const int length); +absl::StatusOr> CompressGraphics(const uchar* data, + const int pos, + const int length); +absl::StatusOr> CompressOverworld(const uchar* data, + const int pos, + const int length); +absl::StatusOr> CompressOverworld( + const std::vector data, const int pos, const int length); absl::StatusOr SplitCompressionPiece( CompressionPiecePointer& piece, int mode); -std::vector CreateCompressionString(CompressionPiecePointer& start, int mode); +std::vector CreateCompressionString(CompressionPiecePointer& start, + int mode); absl::Status ValidateCompressionResult(CompressionPiecePointer& chain_head, int mode, int start, int src_data_pos); CompressionPiecePointer MergeCopy(CompressionPiecePointer& start); -// Compression V3 - struct CompressionContext { std::vector data; std::vector compressed_data; @@ -209,42 +219,35 @@ void FinalizeCompression(CompressionContext& context); /** * @brief Compresses a buffer of data using the LC_LZ2 algorithm. - * \deprecated Use Compress and Uncompress instead. + * \deprecated Use HyruleMagicCompress */ -absl::StatusOr> CompressV3(const std::vector& data, - const int start, const int length, - int mode = 1, bool check = false); - -// Hyrule Magic -uint8_t* Compress(uint8_t const* const src, int const oldsize, int* const size, - int const flag); - -uint8_t* Uncompress(uint8_t const* src, int* const size, - int const p_big_endian); - -// Decompression +absl::StatusOr> CompressV3( + const std::vector& data, const int start, const int length, + int mode = 1, bool check = false); std::string SetBuffer(const std::vector& data, int src_pos, int comp_accumulator); std::string SetBuffer(const uchar* data, int src_pos, int comp_accumulator); -void memfill(const uchar* data, std::vector& buffer, int buffer_pos, int offset, - int length); +void memfill(const uchar* data, std::vector& buffer, int buffer_pos, + int offset, int length); /** * @brief Decompresses a buffer of data using the LC_LZ2 algorithm. - * \deprecated Use Compress and Uncompress instead. + * @note Works well for graphics but not overworld data. Prefer Hyrule Magic + * routines for overworld data. */ absl::StatusOr> DecompressV2(const uchar* data, int offset, - int size = 0x800, int mode = 1); -absl::StatusOr> DecompressGraphics(const uchar* data, int pos, int size); -absl::StatusOr> DecompressOverworld(const uchar* data, int pos, int size); -absl::StatusOr> DecompressOverworld(const std::vector data, - int pos, int size); + int size = 0x800, + int mode = 1); +absl::StatusOr> DecompressGraphics(const uchar* data, + int pos, int size); +absl::StatusOr> DecompressOverworld(const uchar* data, + int pos, int size); +absl::StatusOr> DecompressOverworld( + const std::vector data, int pos, int size); } // namespace lc_lz2 - } // namespace gfx - } // namespace yaze #endif // YAZE_APP_GFX_COMPRESSION_H diff --git a/src/app/rom.cc b/src/app/rom.cc index 9a6397a8..3ffcaa05 100644 --- a/src/app/rom.cc +++ b/src/app/rom.cc @@ -150,7 +150,7 @@ absl::Status Rom::SaveAllGraphicsData() { final_data = gfx::Bpp8SnesToIndexed(sheet_data, 8); int size = 0; if (compressed) { - auto compressed_data = gfx::lc_lz2::Compress( + auto compressed_data = gfx::HyruleMagicCompress( final_data.data(), final_data.size(), &size, 1); for (int j = 0; j < size; j++) { sheet_data[j] = compressed_data[j]; @@ -315,9 +315,12 @@ absl::Status Rom::SaveToFile(bool backup, bool save_new, std::string filename) { } // Run the other save functions - if (flags()->kSaveAllPalettes) RETURN_IF_ERROR(SaveAllPalettes()); - if (flags()->kSaveGfxGroups) RETURN_IF_ERROR(SaveGroupsToRom()); - if (flags()->kSaveGraphicsSheet) RETURN_IF_ERROR(SaveAllGraphicsData()); + if (core::ExperimentFlags::get().kSaveAllPalettes) + RETURN_IF_ERROR(SaveAllPalettes()); + if (core::ExperimentFlags::get().kSaveGfxGroups) + RETURN_IF_ERROR(SaveGroupsToRom()); + if (core::ExperimentFlags::get().kSaveGraphicsSheet) + RETURN_IF_ERROR(SaveAllGraphicsData()); if (save_new) { // Create a file of the same name and append the date between the filename diff --git a/src/app/rom.h b/src/app/rom.h index c6e9f46b..ebf7850c 100644 --- a/src/app/rom.h +++ b/src/app/rom.h @@ -131,7 +131,7 @@ constexpr uint32_t kMaxGraphics = 0xC3FB5; /** * @brief The Rom class is used to load, save, and modify Rom data. */ -class Rom : public core::ExperimentFlags { +class Rom { public: /** * @brief Loads the players 4bpp graphics sheet from Rom data. diff --git a/src/app/zelda3/overworld/overworld.cc b/src/app/zelda3/overworld/overworld.cc index 6bb874bd..241a1552 100644 --- a/src/app/zelda3/overworld/overworld.cc +++ b/src/app/zelda3/overworld/overworld.cc @@ -21,7 +21,8 @@ absl::Status Overworld::Load(Rom &rom) { AssembleMap16Tiles(); RETURN_IF_ERROR(DecompressAllMapTiles()) - const bool load_custom_overworld = flags()->overworld.kLoadCustomOverworld; + const bool load_custom_overworld = + core::ExperimentFlags::get().overworld.kLoadCustomOverworld; for (int map_index = 0; map_index < kNumOverworldMaps; ++map_index) overworld_maps_.emplace_back(map_index, rom_, load_custom_overworld); @@ -107,7 +108,8 @@ absl::Status Overworld::AssembleMap32Tiles() { rom_.version_constants().kMap32TileTR, rom_.version_constants().kMap32TileBL, rom_.version_constants().kMap32TileBR}; - if (rom()->data()[kMap32ExpandedFlagPos] != 0x04) { + if (rom()->data()[kMap32ExpandedFlagPos] != 0x04 && + core::ExperimentFlags::get().overworld.kLoadCustomOverworld) { map32address[0] = rom_.version_constants().kMap32TileTL; map32address[1] = kMap32TileTRExpanded; map32address[2] = kMap32TileBLExpanded; @@ -155,7 +157,8 @@ absl::Status Overworld::AssembleMap32Tiles() { void Overworld::AssembleMap16Tiles() { int tpos = kMap16Tiles; int num_tile16 = kNumTile16Individual; - if (rom()->data()[kMap16ExpandedFlagPos] != 0x0F) { + if (rom()->data()[kMap16ExpandedFlagPos] != 0x0F && + core::ExperimentFlags::get().overworld.kLoadCustomOverworld) { tpos = kMap16TilesExpanded; num_tile16 = NumberOfMap16Ex; expanded_tile16_ = true; @@ -214,8 +217,11 @@ absl::Status Overworld::DecompressAllMapTiles() { return core::SnesToPc(p); }; - uint32_t lowest = 0x0FFFFF; - uint32_t highest = 0x0F8000; + constexpr uint32_t kBaseLowest = 0x0FFFFF; + constexpr uint32_t kBaseHighest = 0x0F8000; + + uint32_t lowest = kBaseLowest; + uint32_t highest = kBaseHighest; int sx = 0; int sy = 0; int c = 0; @@ -230,24 +236,12 @@ absl::Status Overworld::DecompressAllMapTiles() { if (p1 >= highest) highest = p1; if (p2 >= highest) highest = p2; - if (p1 <= lowest && p1 > 0x0F8000) lowest = p1; - if (p2 <= lowest && p2 > 0x0F8000) lowest = p2; + if (p1 <= lowest && p1 > kBaseHighest) lowest = p1; + if (p2 <= lowest && p2 > kBaseHighest) lowest = p2; - std::vector bytes, bytes2; int size1, size2; - auto decomp = gfx::lc_lz2::Uncompress(rom()->data() + p2, &size1, 1); - bytes.resize(size1); - for (int j = 0; j < size1; j++) { - bytes[j] = decomp[j]; - } - free(decomp); - decomp = gfx::lc_lz2::Uncompress(rom()->data() + p1, &size2, 1); - bytes2.resize(size2); - for (int j = 0; j < size2; j++) { - bytes2[j] = decomp[j]; - } - free(decomp); - + auto bytes = gfx::HyruleMagicDecompress(rom()->data() + p2, &size1, 1); + auto bytes2 = gfx::HyruleMagicDecompress(rom()->data() + p1, &size2, 1); OrganizeMapTiles(bytes, bytes2, i, sx, sy, ttpos); sx++; @@ -302,7 +296,8 @@ void Overworld::LoadEntrances() { int ow_entrance_pos_ptr = kOverworldEntrancePos; int ow_entrance_id_ptr = kOverworldEntranceEntranceId; int num_entrances = 129; - if (rom()->data()[kOverworldEntranceExpandedFlagPos] != 0xB8) { + if (rom()->data()[kOverworldEntranceExpandedFlagPos] != 0xB8 && + core::ExperimentFlags::get().overworld.kLoadCustomOverworld) { ow_entrance_map_ptr = kOverworldEntranceMapExpanded; ow_entrance_pos_ptr = kOverworldEntrancePosExpanded; ow_entrance_id_ptr = kOverworldEntranceEntranceIdExpanded; @@ -377,7 +372,7 @@ absl::Status Overworld::LoadExits() { uint16_t px = (uint16_t)((rom_data[OWExitXPlayer + (i * 2) + 1] << 8) + rom_data[OWExitXPlayer + (i * 2)]); - if (rom()->flags()->kLogToConsole) { + if (core::ExperimentFlags::get().kLogToConsole) { std::cout << "Exit: " << i << " RoomID: " << exit_room_id << " MapID: " << exit_map_id << " VRAM: " << exit_vram << " YScroll: " << exit_y_scroll @@ -538,24 +533,13 @@ absl::Status Overworld::SaveOverworldMaps() { } } - std::vector a, b; int size_a, size_b; // Compress single_map_1 and single_map_2 - auto a_char = gfx::lc_lz2::Compress(single_map_1.data(), 256, &size_a, 1); - auto b_char = gfx::lc_lz2::Compress(single_map_2.data(), 256, &size_b, 1); - if (a_char == nullptr || b_char == nullptr) { + auto a = gfx::HyruleMagicCompress(single_map_1.data(), 256, &size_a, 1); + auto b = gfx::HyruleMagicCompress(single_map_2.data(), 256, &size_b, 1); + if (a.empty() || b.empty()) { return absl::AbortedError("Error compressing map gfx."); } - // Copy the compressed data to a and b - a.resize(size_a); - b.resize(size_b); - // Copy the arrays manually - for (int k = 0; k < size_a; k++) { - a[k] = a_char[k]; - } - for (int k = 0; k < size_b; k++) { - b[k] = b_char[k]; - } // Save compressed data and pointers map_data_p1[i] = std::vector(size_a); @@ -571,8 +555,8 @@ absl::Status Overworld::SaveOverworldMaps() { pos = kOverworldMapDataOverflow; // 0x0F8780; } - auto compare_array = [](const std::vector &array1, - const std::vector &array2) -> bool { + const auto compare_array = [](const std::vector &array1, + const std::vector &array2) -> bool { if (array1.size() != array2.size()) { return false; } @@ -1075,7 +1059,7 @@ absl::Status Overworld::CreateTile32Tilemap() { unique_tiles.size(), LimitOfMap32)); } - if (flags()->kLogToConsole) { + if (core::ExperimentFlags::get().kLogToConsole) { std::cout << "Number of unique Tiles32: " << tiles32_unique_.size() << " Saved:" << tiles32_unique_.size() << " Out of: " << LimitOfMap32 << std::endl; @@ -1528,7 +1512,7 @@ absl::Status Overworld::SaveItems() { return absl::AbortedError("Too many items"); } - if (flags()->kLogToConsole) { + if (core::ExperimentFlags::get().kLogToConsole) { std::cout << "End of Items : " << data_pos << std::endl; } diff --git a/src/app/zelda3/overworld/overworld.h b/src/app/zelda3/overworld/overworld.h index d936e927..bcb43d80 100644 --- a/src/app/zelda3/overworld/overworld.h +++ b/src/app/zelda3/overworld/overworld.h @@ -110,7 +110,7 @@ constexpr int NumberOfMap32 = Map32PerScreen * kNumOverworldMaps; * This class is responsible for loading and saving the overworld data, * as well as creating the tilesets and tilemaps for the overworld. */ -class Overworld : public SharedRom, public core::ExperimentFlags { +class Overworld : public SharedRom { public: absl::Status Load(Rom &rom); absl::Status LoadOverworldMaps();