ROM and Compression library updates

Remove Compress and Decompress from ROM
Move Editor parent class to its own file
Move 65816 editor constants to widgets
Update compression_test and snes_palette_test
Start version constant classes (experimental)
Move SetupROM for editors to load renderer
This commit is contained in:
scawful
2023-08-09 00:18:05 -04:00
parent 9ea107bc8d
commit a3f68b70b4
14 changed files with 256 additions and 378 deletions

View File

@@ -2,36 +2,13 @@
#define YAZE_CORE_COMMON_H
#include <cstdint>
#include <string>
#include <memory>
#include <string>
namespace yaze {
namespace app {
namespace core {
class Editor {
public:
Editor() = default;
virtual ~Editor() = default;
virtual void Cut() = 0;
virtual void Copy() = 0;
virtual void Paste() = 0;
virtual void Undo() = 0;
virtual void Redo() = 0;
virtual void SelectAll() = 0;
virtual void Delete() = 0;
virtual void Find() = 0;
virtual void Replace() = 0;
virtual void Goto() = 0;
};
unsigned int SnesToPc(unsigned int addr);
int AddressFromBytes(uint8_t addr1, uint8_t addr2, uint8_t addr3);
int HexToDec(char *input, int length);

View File

@@ -123,31 +123,50 @@ constexpr int kScreenWidth = 1440;
constexpr int kScreenHeight = 900;
// ============================================================================
// 65816 LanguageDefinition
// Z3 Version Constants
// ============================================================================
static const char *const kKeywords[] = {
"ADC", "AND", "ASL", "BCC", "BCS", "BEQ", "BIT", "BMI", "BNE",
"BPL", "BRA", "BRL", "BVC", "BVS", "CLC", "CLD", "CLI", "CLV",
"CMP", "CPX", "CPY", "DEC", "DEX", "DEY", "EOR", "INC", "INX",
"INY", "JMP", "JSR", "JSL", "LDA", "LDX", "LDY", "LSR", "MVN",
"NOP", "ORA", "PEA", "PER", "PHA", "PHB", "PHD", "PHP", "PHX",
"PHY", "PLA", "PLB", "PLD", "PLP", "PLX", "PLY", "REP", "ROL",
"ROR", "RTI", "RTL", "RTS", "SBC", "SEC", "SEI", "SEP", "STA",
"STP", "STX", "STY", "STZ", "TAX", "TAY", "TCD", "TCS", "TDC",
"TRB", "TSB", "TSC", "TSX", "TXA", "TXS", "TXY", "TYA", "TYX",
"WAI", "WDM", "XBA", "XCE", "ORG", "LOROM", "HIROM", "NAMESPACE", "DB"};
enum class Z3_Version {
US = 1,
JP = 2,
SD = 3,
};
template <Z3_Version version>
class VersionConstants;
template <>
class VersionConstants<Z3_Version::US> {
public:
static constexpr uint32_t kGgxAnimatedPointer = 0x10275;
static constexpr uint32_t kOverworldGfxGroups1 = 0x5D97;
static constexpr uint32_t kOverworldGfxGroups2 = 0x6073;
static constexpr uint32_t compressedAllMap32PointersHigh = 0x1794D;
static constexpr uint32_t compressedAllMap32PointersLow = 0x17B2D;
};
template <>
class VersionConstants<Z3_Version::JP> {
public:
static constexpr uint32_t kGgxAnimatedPointer = 0x10624;
static constexpr uint32_t kOverworldGfxGroups1 = 0x5DD7;
static constexpr uint32_t kOverworldGfxGroups2 = 0x60B3;
// LONGPointers all tiles of maps[High] (mapid* 3)
static constexpr uint32_t compressedAllMap32PointersHigh = 0x176B1;
// LONGPointers all tiles of maps[Low] (mapid* 3)
static constexpr uint32_t compressedAllMap32PointersLow = 0x17891;
static constexpr uint32_t overworldMapPalette = 0x7D1C; // JP
static constexpr uint32_t overworldMapPaletteGroup = 0x67E74;
static constexpr uint32_t overworldMapSize = 0x1273B; // JP
static constexpr uint32_t overlayPointers = 0x3FAF4;
static constexpr uint32_t overlayPointersBank = 0x07;
static constexpr uint32_t overworldTilesType = 0x7FD94;
};
static const char *const kIdentifiers[] = {
"abort", "abs", "acos", "asin", "atan", "atexit",
"atof", "atoi", "atol", "ceil", "clock", "cosh",
"ctime", "div", "exit", "fabs", "floor", "fmod",
"getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph",
"ispunct", "isspace", "isupper", "kbhit", "log10", "log2",
"log", "memcmp", "modf", "pow", "putchar", "putenv",
"puts", "rand", "remove", "rename", "sinh", "sqrt",
"srand", "strcat", "strcmp", "strerror", "time", "tolower",
"toupper"};
// ============================================================================
// Magic numbers

21
src/app/core/editor.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef YAZE_APP_CORE_EDITOR_H
#define YAZE_APP_CORE_EDITOR_H
#include "absl/status/status.h"
class Editor {
public:
Editor() = default;
virtual ~Editor() = default;
virtual absl::Status Cut() = 0;
virtual absl::Status Copy() = 0;
virtual absl::Status Paste() = 0;
virtual absl::Status Undo() = 0;
virtual absl::Status Redo() = 0;
virtual absl::Status Update() = 0;
};
#endif // YAZE_APP_CORE_EDITOR_H

View File

@@ -60,6 +60,11 @@ bool BeginCentered(const char *name) {
void MasterEditor::SetupScreen(std::shared_ptr<SDL_Renderer> renderer) {
sdl_renderer_ = renderer;
rom_.SetupRenderer(renderer);
overworld_editor_.SetupROM(rom_);
graphics_editor_.SetupROM(rom_);
screen_editor_.SetupROM(rom_);
palette_editor_.SetupROM(rom_);
music_editor_.SetupROM(rom_);
}
void MasterEditor::UpdateScreen() {
@@ -90,11 +95,6 @@ void MasterEditor::DrawFileDialog() {
std::string filePathName =
ImGuiFileDialog::Instance()->GetFilePathName();
status_ = rom_.LoadFromFile(filePathName);
overworld_editor_.SetupROM(rom_);
graphics_editor_.SetupROM(rom_);
screen_editor_.SetupROM(rom_);
palette_editor_.SetupROM(rom_);
music_editor_.SetupROM(rom_);
});
}

View File

@@ -63,7 +63,6 @@ class MasterEditor {
absl::Status prev_status_;
std::shared_ptr<SDL_Renderer> sdl_renderer_;
std::shared_ptr<core::Editor> current_editor_;
AssemblyEditor assembly_editor_;
DungeonEditor dungeon_editor_;

View File

@@ -10,6 +10,7 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "app/core/editor.h"
#include "app/core/pipeline.h"
#include "app/editor/palette_editor.h"
#include "app/gfx/bitmap.h"
@@ -53,14 +54,15 @@ constexpr absl::string_view kTileSelectorTab = "##TileSelectorTabBar";
constexpr absl::string_view kOWEditTable = "##OWEditTable";
constexpr absl::string_view kOWMapTable = "#MapSettingsTable";
class OverworldEditor : public SharedROM {
class OverworldEditor : public Editor, public SharedROM {
public:
absl::Status Update();
absl::Status Undo() const { return absl::UnimplementedError("Undo"); }
absl::Status Redo() const { return absl::UnimplementedError("Redo"); }
absl::Status Cut() const { return absl::UnimplementedError("Cut"); }
absl::Status Copy() const { return absl::UnimplementedError("Copy"); }
absl::Status Paste() const { return absl::UnimplementedError("Paste"); }
absl::Status Update() final;
absl::Status Undo() { return absl::UnimplementedError("Undo"); }
absl::Status Redo() { return absl::UnimplementedError("Redo"); }
absl::Status Cut() { return absl::UnimplementedError("Cut"); }
absl::Status Copy() { return absl::UnimplementedError("Copy"); }
absl::Status Paste() { return absl::UnimplementedError("Paste"); }
void SetupROM(ROM &rom) {
rom_ = rom;
shared_rom_ = std::make_shared<ROM>(rom_);

View File

@@ -470,7 +470,8 @@ absl::Status ValidateCompressionResult(CompressionPiecePointer& chain_head,
ROM temp_rom;
RETURN_IF_ERROR(
temp_rom.LoadFromBytes(CreateCompressionString(chain_head->next, mode)))
ASSIGN_OR_RETURN(auto decomp_data, temp_rom.Decompress(0, temp_rom.size()))
ASSIGN_OR_RETURN(auto decomp_data,
DecompressV2(temp_rom.data(), 0, temp_rom.size()))
if (!std::equal(decomp_data.begin() + start, decomp_data.end(),
temp_rom.begin())) {
return absl::InternalError(absl::StrFormat(
@@ -538,7 +539,9 @@ absl::StatusOr<Bytes> CompressV2(const uchar* data, const int start,
uint max_win = 2;
uint cmd_with_max = kCommandDirectCopy;
ValidateForByteGainV2(current_cmd, max_win, cmd_with_max);
ValidateForByteGain(current_cmd.data_size, current_cmd.cmd_size, max_win,
cmd_with_max);
// ValidateForByteGainV2(current_cmd, max_win, cmd_with_max);
if (cmd_with_max == kCommandDirectCopy) {
// This is the worst case scenario
@@ -553,7 +556,6 @@ absl::StatusOr<Bytes> CompressV2(const uchar* data, const int start,
auto new_comp_piece = std::make_shared<CompressionPiece>(
kCommandDirectCopy, comp_accumulator, buffer, comp_accumulator);
compressed_chain->next = new_comp_piece;
compressed_chain = new_comp_piece;
comp_accumulator = 0;
}
} else {

View File

@@ -94,8 +94,10 @@ void CompressionCommandAlternativeV2(const uchar* data,
absl::StatusOr<Bytes> CompressV2(const uchar* data, const int start,
const int length, int mode = 1,
bool check = false);
absl::StatusOr<Bytes> CompressGraphics(const int pos, const int length);
absl::StatusOr<Bytes> CompressOverworld(const int pos, const int length);
absl::StatusOr<Bytes> CompressGraphics(const uchar* data, const int pos,
const int length);
absl::StatusOr<Bytes> CompressOverworld(const uchar* data, const int pos,
const int length);
std::string SetBuffer(const uchar* data, int src_pos, int comp_accumulator);
void memfill(const uchar* data, Bytes& buffer, int buffer_pos, int offset,

View File

@@ -10,11 +10,38 @@ namespace yaze {
namespace gui {
namespace widgets {
// ============================================================================
// 65816 LanguageDefinition
// ============================================================================
static const char *const kKeywords[] = {
"ADC", "AND", "ASL", "BCC", "BCS", "BEQ", "BIT", "BMI", "BNE",
"BPL", "BRA", "BRL", "BVC", "BVS", "CLC", "CLD", "CLI", "CLV",
"CMP", "CPX", "CPY", "DEC", "DEX", "DEY", "EOR", "INC", "INX",
"INY", "JMP", "JSR", "JSL", "LDA", "LDX", "LDY", "LSR", "MVN",
"NOP", "ORA", "PEA", "PER", "PHA", "PHB", "PHD", "PHP", "PHX",
"PHY", "PLA", "PLB", "PLD", "PLP", "PLX", "PLY", "REP", "ROL",
"ROR", "RTI", "RTL", "RTS", "SBC", "SEC", "SEI", "SEP", "STA",
"STP", "STX", "STY", "STZ", "TAX", "TAY", "TCD", "TCS", "TDC",
"TRB", "TSB", "TSC", "TSX", "TXA", "TXS", "TXY", "TYA", "TYX",
"WAI", "WDM", "XBA", "XCE", "ORG", "LOROM", "HIROM", "NAMESPACE", "DB"};
static const char *const kIdentifiers[] = {
"abort", "abs", "acos", "asin", "atan", "atexit",
"atof", "atoi", "atol", "ceil", "clock", "cosh",
"ctime", "div", "exit", "fabs", "floor", "fmod",
"getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph",
"ispunct", "isspace", "isupper", "kbhit", "log10", "log2",
"log", "memcmp", "modf", "pow", "putchar", "putenv",
"puts", "rand", "remove", "rename", "sinh", "sqrt",
"srand", "strcat", "strcmp", "strerror", "time", "tolower",
"toupper"};
TextEditor::LanguageDefinition GetAssemblyLanguageDef() {
TextEditor::LanguageDefinition language_65816;
for (auto &k : app::core::kKeywords) language_65816.mKeywords.emplace(k);
for (auto &k : kKeywords) language_65816.mKeywords.emplace(k);
for (auto &k : app::core::kIdentifiers) {
for (auto &k : kIdentifiers) {
TextEditor::Identifier id;
id.mDeclaration = "Built-in function";
language_65816.mIdentifiers.insert(std::make_pair(std::string(k), id));

View File

@@ -44,196 +44,6 @@ int GetGraphicsAddress(const uchar* data, uint8_t offset) {
} // namespace
// TODO TEST compressed data border for each cmd
absl::StatusOr<Bytes> ROM::Compress(const int start, const int length, int mode,
bool check) {
if (length == 0) {
return Bytes();
}
// Worse case should be a copy of the string with extended header
auto compressed_chain = std::make_shared<CompressionPiece>(1, 1, "aaa", 2);
auto compressed_chain_start = compressed_chain;
gfx::lc_lz2::CommandArgumentArray cmd_args = {{}};
gfx::lc_lz2::DataSizeArray data_size_taken = {0, 0, 0, 0, 0};
gfx::lc_lz2::CommandSizeArray cmd_size = {0, 1, 2, 1, 2};
uint src_data_pos = start;
uint last_pos = start + length - 1;
uint comp_accumulator = 0; // Used when skipping using copy
while (true) {
data_size_taken.fill({});
cmd_args.fill({{}});
gfx::lc_lz2::CheckByteRepeat(rom_data_.data(), data_size_taken, cmd_args,
src_data_pos, last_pos);
gfx::lc_lz2::CheckWordRepeat(rom_data_.data(), data_size_taken, cmd_args,
src_data_pos, last_pos);
gfx::lc_lz2::CheckIncByte(rom_data_.data(), data_size_taken, cmd_args,
src_data_pos, last_pos);
gfx::lc_lz2::CheckIntraCopy(rom_data_.data(), data_size_taken, cmd_args,
src_data_pos, last_pos, start);
uint max_win = 2;
uint cmd_with_max = kCommandDirectCopy;
gfx::lc_lz2::ValidateForByteGain(data_size_taken, cmd_size, max_win,
cmd_with_max);
if (cmd_with_max == kCommandDirectCopy) {
// This is the worst case scenario
// Progress through the next byte, in case there's a different
// compression command we can implement before we hit 32 bytes.
src_data_pos++;
comp_accumulator++;
// Arbitrary choice to do a 32 bytes grouping for copy.
if (comp_accumulator == 32 || src_data_pos > last_pos) {
std::string buffer;
for (int i = 0; i < comp_accumulator; ++i) {
buffer.push_back(rom_data_[i + src_data_pos - comp_accumulator]);
}
auto new_comp_piece = std::make_shared<CompressionPiece>(
kCommandDirectCopy, comp_accumulator, buffer, comp_accumulator);
compressed_chain->next = new_comp_piece;
compressed_chain = new_comp_piece;
comp_accumulator = 0;
}
} else {
gfx::lc_lz2::CompressionCommandAlternative(
rom_data_.data(), compressed_chain, cmd_size, cmd_args, src_data_pos,
comp_accumulator, cmd_with_max, max_win);
}
if (src_data_pos > last_pos) {
printf("Breaking compression loop\n");
break;
}
if (check) {
RETURN_IF_ERROR(gfx::lc_lz2::ValidateCompressionResult(
compressed_chain_start, mode, start, src_data_pos))
}
}
// Skipping compression chain header
gfx::lc_lz2::MergeCopy(compressed_chain_start->next);
gfx::lc_lz2::PrintCompressionChain(compressed_chain_start);
return gfx::lc_lz2::CreateCompressionString(compressed_chain_start->next,
mode);
}
absl::StatusOr<Bytes> ROM::CompressGraphics(const int pos, const int length) {
return Compress(pos, length, gfx::lc_lz2::kNintendoMode2);
}
absl::StatusOr<Bytes> ROM::CompressOverworld(const int pos, const int length) {
return Compress(pos, length, gfx::lc_lz2::kNintendoMode1);
}
// ============================================================================
absl::StatusOr<Bytes> ROM::Decompress(int offset, int size, int mode) {
if (size == 0) {
return Bytes();
}
Bytes buffer(size, 0);
uint length = 0;
uint buffer_pos = 0;
uchar command = 0;
uchar header = rom_data_[offset];
while (header != kSnesByteMax) {
if ((header & gfx::lc_lz2::kExpandedMod) == gfx::lc_lz2::kExpandedMod) {
// Expanded Command
command = ((header >> 2) & kCommandMod);
length = (((header << 8) | rom_data_[offset + 1]) &
gfx::lc_lz2::kExpandedLengthMod);
offset += 2; // Advance 2 bytes in ROM
} else {
// Normal Command
command = ((header >> 5) & kCommandMod);
length = (header & gfx::lc_lz2::kNormalLengthMod);
offset += 1; // Advance 1 byte in ROM
}
length += 1; // each commands is at least of size 1 even if index 00
switch (command) {
case gfx::lc_lz2::kCommandDirectCopy: // Does not advance in the ROM
memcpy(buffer.data() + buffer_pos, rom_data_.data() + offset, length);
buffer_pos += length;
offset += length;
break;
case gfx::lc_lz2::kCommandByteFill:
memset(buffer.data() + buffer_pos, (int)(rom_data_[offset]), length);
buffer_pos += length;
offset += 1; // Advances 1 byte in the ROM
break;
case gfx::lc_lz2::kCommandWordFill: {
auto a = rom_data_[offset];
auto b = rom_data_[offset + 1];
for (int i = 0; i < length; i = i + 2) {
buffer[buffer_pos + i] = a;
if ((i + 1) < length) buffer[buffer_pos + i + 1] = b;
}
buffer_pos += length;
offset += 2; // Advance 2 byte in the ROM
} break;
case gfx::lc_lz2::kCommandIncreasingFill: {
auto inc_byte = rom_data_[offset];
for (int i = 0; i < length; i++) {
buffer[buffer_pos] = inc_byte++;
buffer_pos++;
}
offset += 1; // Advance 1 byte in the ROM
} break;
case gfx::lc_lz2::kCommandRepeatingBytes: {
ushort s1 = ((rom_data_[offset + 1] & kSnesByteMax) << 8);
ushort s2 = (rom_data_[offset] & kSnesByteMax);
int addr = (s1 | s2);
if (mode == gfx::lc_lz2::kNintendoMode1) { // Reversed byte order for
// overworld maps
addr = (rom_data_[offset + 1] & kSnesByteMax) |
((rom_data_[offset] & kSnesByteMax) << 8);
}
if (addr > offset) {
return absl::InternalError(absl::StrFormat(
"Decompress: Offset for command copy exceeds current position "
"(Offset : %#04x | Pos : %#06x)\n",
addr, offset));
}
if (buffer_pos + length >= size) {
size *= 2;
buffer.resize(size);
}
memcpy(buffer.data() + buffer_pos, buffer.data() + addr, length);
buffer_pos += length;
offset += 2;
} break;
default: {
std::cout << absl::StrFormat(
"Decompress: Invalid header (Offset : %#06x, Command: %#04x)\n",
offset, command);
} break;
}
// check next byte
header = rom_data_[offset];
}
return buffer;
}
absl::StatusOr<Bytes> ROM::DecompressGraphics(int pos, int size) {
return Decompress(pos, size, gfx::lc_lz2::kNintendoMode2);
}
absl::StatusOr<Bytes> ROM::DecompressOverworld(int pos, int size) {
return Decompress(pos, size, gfx::lc_lz2::kNintendoMode1);
}
// ============================================================================
absl::StatusOr<Bytes> ROM::Load2bppGraphics() {
Bytes sheet;
const uint8_t sheets[] = {113, 114, 218, 219, 220, 221};
@@ -307,16 +117,11 @@ absl::Status ROM::LoadFromFile(const absl::string_view& filename,
absl::StrCat("Could not open ROM file: ", filename));
}
bool has_header = false;
int header_count = 0x200;
size_ = std::filesystem::file_size(filename);
rom_data_.resize(size_);
for (auto i = 0; i < size_; ++i) {
char byte_to_read = ' ';
file.read(&byte_to_read, sizeof(char));
if (byte_to_read == 0x00) {
has_header = true;
}
rom_data_[i] = byte_to_read;
}
@@ -442,6 +247,7 @@ absl::Status ROM::UpdatePaletteColor(const std::string& groupName,
if (colorIndex < palette_groups_[groupName][paletteIndex].size()) {
// Update the color value in the palette
palette_groups_[groupName][paletteIndex][colorIndex] = newColor;
palette_groups_[groupName][paletteIndex][colorIndex].setModified(true);
} else {
return absl::AbortedError(
"Error: Invalid color index in UpdatePaletteColor.");

View File

@@ -69,16 +69,6 @@ const absl::flat_hash_map<std::string, uint32_t> paletteGroupColorCounts = {
class ROM {
public:
// Compression function
absl::StatusOr<Bytes> Compress(const int start, const int length,
int mode = 1, bool check = false);
absl::StatusOr<Bytes> CompressGraphics(const int pos, const int length);
absl::StatusOr<Bytes> CompressOverworld(const int pos, const int length);
absl::StatusOr<Bytes> Decompress(int offset, int size = 0x800, int mode = 1);
absl::StatusOr<Bytes> DecompressGraphics(int pos, int size);
absl::StatusOr<Bytes> DecompressOverworld(int pos, int size);
// Load functions
absl::StatusOr<Bytes> Load2bppGraphics();
absl::Status LoadAllGraphicsData();
@@ -125,6 +115,8 @@ class ROM {
auto push_back(uchar byte) { rom_data_.push_back(byte); }
auto version() const { return version_; }
void malloc(int n_bytes) {
rom_data_.clear();
rom_data_.reserve(n_bytes);
@@ -178,6 +170,7 @@ class ROM {
Bytes rom_data_;
Bytes graphics_buffer_;
core::Z3_Version version_;
gfx::BitmapTable graphics_bin_;
std::shared_ptr<SDL_Renderer> renderer_;

View File

@@ -1,7 +1,19 @@
#include "overworld.h"
#include <SDL.h>
#include <future>
#include <memory>
#include <vector>
#include "absl/status/status.h"
#include "app/core/constants.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/compression.h"
#include "app/gfx/snes_tile.h"
#include "app/rom.h"
#include "app/zelda3/overworld_map.h"
#include "app/zelda3/sprite/sprite.h"
namespace yaze {
namespace app {
@@ -434,8 +446,10 @@ absl::Status Overworld::DecompressAllMapTiles() {
lowest = p2;
}
ASSIGN_OR_RETURN(auto bytes, rom_.DecompressOverworld(p2, 1000))
ASSIGN_OR_RETURN(auto bytes2, rom_.DecompressOverworld(p1, 1000))
ASSIGN_OR_RETURN(auto bytes,
gfx::lc_lz2::DecompressOverworld(rom_.data(), p2, 1000))
ASSIGN_OR_RETURN(auto bytes2,
gfx::lc_lz2::DecompressOverworld(rom_.data(), p1, 1000))
OrganizeMapTiles(bytes, bytes2, i, sx, sy, ttpos);
sx++;