This commit is contained in:
Justin Scofield
2024-12-31 19:21:24 -05:00
18 changed files with 427 additions and 456 deletions

View File

@@ -171,8 +171,6 @@ bool StringReplace(std::string &str, const std::string &from,
return true;
}
std::shared_ptr<ExperimentFlags::Flags> ExperimentFlags::flags_;
uint32_t Get24LocalFromPC(uint8_t *data, int addr, bool pc) {
uint32_t ret =
(PcToSnes(addr) & 0xFF0000) | (data[addr + 1] << 8) | data[addr];

View File

@@ -96,57 +96,44 @@ class ExperimentFlags {
} overworld;
};
ExperimentFlags() = default;
virtual ~ExperimentFlags() = default;
auto flags() const {
if (!flags_) {
flags_ = std::make_shared<Flags>();
}
Flags *flags = flags_.get();
return flags;
}
Flags *mutable_flags() {
if (!flags_) {
flags_ = std::make_shared<Flags>();
}
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> flags_;
};
/**

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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; }

View File

@@ -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_);
}

View File

@@ -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_;

View File

@@ -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());
}

View File

@@ -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; }

View File

@@ -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);
}
};

View File

@@ -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<int>(PB) << ":" << std::hex << PC << ": 0x"

View File

@@ -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) {}

View File

@@ -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<uint8_t> 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<uint8_t> compressed_data(b2, b2 + bd);
free(b2);
return compressed_data;
}
std::vector<uint8_t> 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<uint8_t> 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<CompressionPiecePointer> 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<CompressionPiece>(
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<std::vector<uint8_t>> CompressV2(const uchar* data,
}
// Worst case should be a copy of the string with extended header
auto compressed_chain = std::make_shared<CompressionPiece>(1, 1, "aaa", 2);
std::string worst_case = "aaa";
auto compressed_chain =
std::make_shared<CompressionPiece>(1, 1, worst_case, 2);
auto compressed_chain_start = compressed_chain;
CompressionCommand current_cmd = {/*argument*/ {{}},
@@ -589,286 +871,6 @@ absl::StatusOr<std::vector<uint8_t>> 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<std::vector<uint8_t>> CompressGraphics(const uchar* data,
const int pos,
const int length) {
@@ -886,9 +888,6 @@ absl::StatusOr<std::vector<uint8_t>> CompressOverworld(
return CompressV3(data, pos, length, kNintendoMode1);
}
// ============================================================================
// Compression V3
void CheckByteRepeatV3(CompressionContext& context) {
uint pos = context.src_pos;
@@ -1208,15 +1207,17 @@ absl::StatusOr<CompressionPiece> 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<std::vector<uint8_t>> 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<std::vector<uint8_t>> DecompressOverworld(
} // namespace lc_lz2
} // namespace gfx
} // namespace yaze
} // namespace yaze

View File

@@ -3,6 +3,7 @@
#include <memory>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
@@ -13,6 +14,13 @@
namespace yaze {
namespace gfx {
std::vector<uint8_t> HyruleMagicCompress(uint8_t const* const src,
int const oldsize, int* const size,
int const flag);
std::vector<uint8_t> 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<CompressionPiece> 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<std::vector<uint8_t>> CompressV2(const uchar* data, const int start,
const int length, int mode = 1,
bool check = false);
absl::StatusOr<std::vector<uint8_t>> CompressV2(const uchar* data,
const int start,
const int length, int mode = 1,
bool check = false);
absl::StatusOr<std::vector<uint8_t>> CompressGraphics(const uchar* data, const int pos,
const int length);
absl::StatusOr<std::vector<uint8_t>> CompressOverworld(const uchar* data, const int pos,
const int length);
absl::StatusOr<std::vector<uint8_t>> CompressOverworld(const std::vector<uint8_t> data,
const int pos, const int length);
absl::StatusOr<std::vector<uint8_t>> CompressGraphics(const uchar* data,
const int pos,
const int length);
absl::StatusOr<std::vector<uint8_t>> CompressOverworld(const uchar* data,
const int pos,
const int length);
absl::StatusOr<std::vector<uint8_t>> CompressOverworld(
const std::vector<uint8_t> data, const int pos, const int length);
absl::StatusOr<CompressionPiecePointer> SplitCompressionPiece(
CompressionPiecePointer& piece, int mode);
std::vector<uint8_t> CreateCompressionString(CompressionPiecePointer& start, int mode);
std::vector<uint8_t> 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<uint8_t> data;
std::vector<uint8_t> 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<std::vector<uint8_t>> CompressV3(const std::vector<uint8_t>& 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<std::vector<uint8_t>> CompressV3(
const std::vector<uint8_t>& data, const int start, const int length,
int mode = 1, bool check = false);
std::string SetBuffer(const std::vector<uint8_t>& 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<uint8_t>& buffer, int buffer_pos, int offset,
int length);
void memfill(const uchar* data, std::vector<uint8_t>& 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<std::vector<uint8_t>> DecompressV2(const uchar* data, int offset,
int size = 0x800, int mode = 1);
absl::StatusOr<std::vector<uint8_t>> DecompressGraphics(const uchar* data, int pos, int size);
absl::StatusOr<std::vector<uint8_t>> DecompressOverworld(const uchar* data, int pos, int size);
absl::StatusOr<std::vector<uint8_t>> DecompressOverworld(const std::vector<uint8_t> data,
int pos, int size);
int size = 0x800,
int mode = 1);
absl::StatusOr<std::vector<uint8_t>> DecompressGraphics(const uchar* data,
int pos, int size);
absl::StatusOr<std::vector<uint8_t>> DecompressOverworld(const uchar* data,
int pos, int size);
absl::StatusOr<std::vector<uint8_t>> DecompressOverworld(
const std::vector<uint8_t> data, int pos, int size);
} // namespace lc_lz2
} // namespace gfx
} // namespace yaze
#endif // YAZE_APP_GFX_COMPRESSION_H

View File

@@ -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

View File

@@ -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.

View File

@@ -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<uint8_t> 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<uint8_t> 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<uint8_t>(size_a);
@@ -571,8 +555,8 @@ absl::Status Overworld::SaveOverworldMaps() {
pos = kOverworldMapDataOverflow; // 0x0F8780;
}
auto compare_array = [](const std::vector<uint8_t> &array1,
const std::vector<uint8_t> &array2) -> bool {
const auto compare_array = [](const std::vector<uint8_t> &array1,
const std::vector<uint8_t> &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;
}

View File

@@ -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();