backend-infra-engineer: Pre-0.2.2 2024 Q4 snapshot
This commit is contained in:
@@ -1,40 +0,0 @@
|
||||
include(app/editor/CMakeLists.txt)
|
||||
include(app/zelda3/CMakeLists.txt)
|
||||
|
||||
add_executable(
|
||||
yaze
|
||||
app/yaze.cc
|
||||
app/rom.cc
|
||||
${YAZE_APP_EMU_SRC}
|
||||
${YAZE_APP_CORE_SRC}
|
||||
${YAZE_APP_EDITOR_SRC}
|
||||
${YAZE_APP_GFX_SRC}
|
||||
${YAZE_APP_ZELDA3_SRC}
|
||||
${YAZE_GUI_SRC}
|
||||
${IMGUI_SRC}
|
||||
${IMGUI_TEST_ENGINE_SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
yaze PUBLIC
|
||||
lib/
|
||||
app/
|
||||
${CMAKE_SOURCE_DIR}/src/
|
||||
${CMAKE_SOURCE_DIR}/src/lib/imgui_test_engine
|
||||
${PNG_INCLUDE_DIRS}
|
||||
${SDL2_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
yaze PUBLIC
|
||||
${ABSL_TARGETS}
|
||||
${SDL_TARGETS}
|
||||
${PNG_LIBRARIES}
|
||||
${CMAKE_DL_LIBS}
|
||||
ImGuiTestEngine
|
||||
ImGui
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
target_link_libraries(yaze PUBLIC ${COCOA_LIBRARY})
|
||||
endif()
|
||||
73
src/app/app.cmake
Normal file
73
src/app/app.cmake
Normal file
@@ -0,0 +1,73 @@
|
||||
include(app/core/core.cmake)
|
||||
include(app/editor/editor.cmake)
|
||||
include(app/gfx/gfx.cmake)
|
||||
include(app/gui/gui.cmake)
|
||||
include(app/zelda3/zelda3.cmake)
|
||||
|
||||
if (APPLE)
|
||||
add_executable(
|
||||
yaze
|
||||
MACOSX_BUNDLE
|
||||
app/main.cc
|
||||
app/rom.cc
|
||||
${YAZE_APP_EMU_SRC}
|
||||
${YAZE_APP_CORE_SRC}
|
||||
${YAZE_APP_EDITOR_SRC}
|
||||
${YAZE_APP_GFX_SRC}
|
||||
${YAZE_APP_ZELDA3_SRC}
|
||||
${YAZE_GUI_SRC}
|
||||
${IMGUI_SRC}
|
||||
|
||||
# Bundled Resources
|
||||
${YAZE_RESOURCE_FILES}
|
||||
)
|
||||
else()
|
||||
add_executable(
|
||||
yaze
|
||||
app/main.cc
|
||||
app/rom.cc
|
||||
${YAZE_APP_EMU_SRC}
|
||||
${YAZE_APP_CORE_SRC}
|
||||
${YAZE_APP_EDITOR_SRC}
|
||||
${YAZE_APP_GFX_SRC}
|
||||
${YAZE_APP_ZELDA3_SRC}
|
||||
${YAZE_GUI_SRC}
|
||||
${IMGUI_SRC}
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(
|
||||
yaze PUBLIC
|
||||
lib/
|
||||
app/
|
||||
${ASAR_INCLUDE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/incl/
|
||||
${CMAKE_SOURCE_DIR}/src/
|
||||
${CMAKE_SOURCE_DIR}/src/lib/imgui_test_engine
|
||||
${PNG_INCLUDE_DIRS}
|
||||
${SDL2_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
yaze PUBLIC
|
||||
asar-static
|
||||
${ABSL_TARGETS}
|
||||
${SDL_TARGETS}
|
||||
${PNG_LIBRARIES}
|
||||
${CMAKE_DL_LIBS}
|
||||
ImGui
|
||||
ImGuiTestEngine
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
target_link_libraries(yaze PUBLIC ${COCOA_LIBRARY})
|
||||
endif()
|
||||
|
||||
if (WIN32 OR MINGW)
|
||||
target_link_libraries(
|
||||
yaze PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/build/build-windows/bin/libpng16.dll
|
||||
zlib
|
||||
mingw32
|
||||
ws2_32)
|
||||
endif()
|
||||
@@ -1,107 +1,60 @@
|
||||
#include "common.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include <zlib.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
std::shared_ptr<ExperimentFlags::Flags> ExperimentFlags::flags_;
|
||||
namespace {
|
||||
|
||||
std::string UppercaseHexByte(uint8_t byte, bool leading) {
|
||||
if (leading) {
|
||||
std::string result = absl::StrFormat("0x%02X", byte);
|
||||
return result;
|
||||
}
|
||||
std::string result = absl::StrFormat("%02X", byte);
|
||||
return result;
|
||||
}
|
||||
std::string UppercaseHexWord(uint16_t word) {
|
||||
std::string result = absl::StrFormat("0x%04X", word);
|
||||
return result;
|
||||
}
|
||||
std::string UppercaseHexLong(uint32_t dword) {
|
||||
std::string result = absl::StrFormat("0x%06X", dword);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t SnesToPc(uint32_t addr) {
|
||||
if (addr >= 0x808000) {
|
||||
addr -= 0x808000;
|
||||
}
|
||||
uint32_t temp = (addr & 0x7FFF) + ((addr / 2) & 0xFF8000);
|
||||
return (temp + 0x0);
|
||||
}
|
||||
|
||||
uint32_t PcToSnes(uint32_t addr) {
|
||||
uint8_t *b = reinterpret_cast<uint8_t *>(&addr);
|
||||
b[2] = static_cast<uint8_t>(b[2] * 2);
|
||||
|
||||
if (b[1] >= 0x80) {
|
||||
b[2] += 1;
|
||||
} else {
|
||||
b[1] += 0x80;
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
uint32_t MapBankToWordAddress(uint8_t bank, uint16_t addr) {
|
||||
uint32_t result = 0;
|
||||
result = (bank << 16) | addr;
|
||||
return result;
|
||||
}
|
||||
|
||||
int AddressFromBytes(uint8_t addr1, uint8_t addr2, uint8_t addr3) {
|
||||
return (addr1 << 16) | (addr2 << 8) | addr3;
|
||||
}
|
||||
|
||||
// hextodec has been imported from SNESDisasm to parse hex numbers
|
||||
int HexToDec(char *input, int length) {
|
||||
int result = 0;
|
||||
int value;
|
||||
int ceiling = length - 1;
|
||||
int power16 = 16;
|
||||
|
||||
int j = ceiling;
|
||||
|
||||
for (; j >= 0; j--) {
|
||||
if (input[j] >= 'A' && input[j] <= 'F') {
|
||||
value = input[j] - 'F';
|
||||
value += 15;
|
||||
} else {
|
||||
value = input[j] - '9';
|
||||
value += 9;
|
||||
void encode(uint64_t data, std::vector<uint8_t> &output) {
|
||||
while (true) {
|
||||
uint8_t x = data & 0x7f;
|
||||
data >>= 7;
|
||||
if (data == 0) {
|
||||
output.push_back(0x80 | x);
|
||||
break;
|
||||
}
|
||||
|
||||
if (j == ceiling) {
|
||||
result += value;
|
||||
continue;
|
||||
}
|
||||
|
||||
result += (value * power16);
|
||||
power16 *= 16;
|
||||
output.push_back(x);
|
||||
data--;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool StringReplace(std::string &str, const std::string &from,
|
||||
const std::string &to) {
|
||||
size_t start = str.find(from);
|
||||
if (start == std::string::npos) return false;
|
||||
uint64_t decode(const std::vector<uint8_t> &input, size_t &offset) {
|
||||
uint64_t data = 0;
|
||||
uint64_t shift = 1;
|
||||
while (true) {
|
||||
uint8_t x = input[offset++];
|
||||
data += (x & 0x7f) * shift;
|
||||
if (x & 0x80) break;
|
||||
shift <<= 7;
|
||||
data += shift;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
str.replace(start, from.length(), to);
|
||||
return true;
|
||||
uint32_t crc32(const std::vector<uint8_t> &data) {
|
||||
uint32_t crc = ::crc32(0L, Z_NULL, 0);
|
||||
return ::crc32(crc, data.data(), data.size());
|
||||
}
|
||||
|
||||
// "load little endian value at the given byte offset and shift to get its
|
||||
// value relative to the base offset (powers of 256, essentially)"
|
||||
unsigned ldle(uint8_t const *const p_arr, unsigned const p_index) {
|
||||
uint32_t v = p_arr[p_index];
|
||||
v <<= (8 * p_index);
|
||||
return v;
|
||||
}
|
||||
|
||||
void stle(uint8_t *const p_arr, size_t const p_index, unsigned const p_val) {
|
||||
@@ -125,25 +78,6 @@ void stle2(uint8_t *const p_arr, unsigned const p_val) {
|
||||
void stle3(uint8_t *const p_arr, unsigned const p_val) {
|
||||
stle(p_arr, 3, p_val);
|
||||
}
|
||||
void stle16b(uint8_t *const p_arr, uint16_t const p_val) {
|
||||
stle0(p_arr, p_val);
|
||||
stle1(p_arr, p_val);
|
||||
}
|
||||
// "Store little endian 16-bit value using a byte pointer, offset by an
|
||||
// index before dereferencing"
|
||||
void stle16b_i(uint8_t *const p_arr, size_t const p_index,
|
||||
uint16_t const p_val) {
|
||||
stle16b(p_arr + (p_index * 2), p_val);
|
||||
}
|
||||
// "load little endian value at the given byte offset and shift to get its
|
||||
// value relative to the base offset (powers of 256, essentially)"
|
||||
unsigned ldle(uint8_t const *const p_arr, unsigned const p_index) {
|
||||
uint32_t v = p_arr[p_index];
|
||||
|
||||
v <<= (8 * p_index);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
// Helper function to get the first byte in a little endian number
|
||||
uint32_t ldle0(uint8_t const *const p_arr) { return ldle(p_arr, 0); }
|
||||
@@ -156,32 +90,244 @@ uint32_t ldle2(uint8_t const *const p_arr) { return ldle(p_arr, 2); }
|
||||
|
||||
// Helper function to get the third byte in a little endian number
|
||||
uint32_t ldle3(uint8_t const *const p_arr) { return ldle(p_arr, 3); }
|
||||
// Load little endian halfword (16-bit) dereferenced from
|
||||
uint16_t ldle16b(uint8_t const *const p_arr) {
|
||||
uint16_t v = 0;
|
||||
|
||||
v |= (ldle0(p_arr) | ldle1(p_arr));
|
||||
} // namespace
|
||||
|
||||
return v;
|
||||
std::string UppercaseHexByte(uint8_t byte, bool leading) {
|
||||
if (leading) {
|
||||
std::string result = absl::StrFormat("0x%02X", byte);
|
||||
return result;
|
||||
}
|
||||
std::string result = absl::StrFormat("%02X", byte);
|
||||
return result;
|
||||
}
|
||||
// Load little endian halfword (16-bit) dereferenced from an arrays of bytes.
|
||||
// This version provides an index that will be multiplied by 2 and added to the
|
||||
// base address.
|
||||
uint16_t ldle16b_i(uint8_t const *const p_arr, size_t const p_index) {
|
||||
return ldle16b(p_arr + (2 * p_index));
|
||||
std::string UppercaseHexWord(uint16_t word, bool leading) {
|
||||
if (leading) {
|
||||
std::string result = absl::StrFormat("0x%04X", word);
|
||||
return result;
|
||||
}
|
||||
std::string result = absl::StrFormat("%04X", word);
|
||||
return result;
|
||||
}
|
||||
std::string UppercaseHexLong(uint32_t dword) {
|
||||
std::string result = absl::StrFormat("0x%06X", dword);
|
||||
return result;
|
||||
}
|
||||
std::string UppercaseHexLongLong(uint64_t qword) {
|
||||
std::string result = absl::StrFormat("0x%08X", qword);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Initialize the static member
|
||||
std::stack<ImGuiID> ImGuiIdIssuer::idStack;
|
||||
bool StringReplace(std::string &str, const std::string &from,
|
||||
const std::string &to) {
|
||||
size_t start = str.find(from);
|
||||
if (start == std::string::npos) return false;
|
||||
|
||||
str.replace(start, from.length(), to);
|
||||
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];
|
||||
uint32_t ret =
|
||||
(PcToSnes(addr) & 0xFF0000) | (data[addr + 1] << 8) | data[addr];
|
||||
if (pc) {
|
||||
return SnesToPc(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void stle16b_i(uint8_t *const p_arr, size_t const p_index,
|
||||
uint16_t const p_val) {
|
||||
stle16b(p_arr + (p_index * 2), p_val);
|
||||
}
|
||||
|
||||
void stle16b(uint8_t *const p_arr, uint16_t const p_val) {
|
||||
stle0(p_arr, p_val);
|
||||
stle1(p_arr, p_val);
|
||||
}
|
||||
|
||||
uint16_t ldle16b(uint8_t const *const p_arr) {
|
||||
uint16_t v = 0;
|
||||
v |= (ldle0(p_arr) | ldle1(p_arr));
|
||||
return v;
|
||||
}
|
||||
|
||||
uint16_t ldle16b_i(uint8_t const *const p_arr, size_t const p_index) {
|
||||
return ldle16b(p_arr + (2 * p_index));
|
||||
}
|
||||
|
||||
void CreateBpsPatch(const std::vector<uint8_t> &source,
|
||||
const std::vector<uint8_t> &target,
|
||||
std::vector<uint8_t> &patch) {
|
||||
patch.clear();
|
||||
patch.insert(patch.end(), {'B', 'P', 'S', '1'});
|
||||
|
||||
encode(source.size(), patch);
|
||||
encode(target.size(), patch);
|
||||
encode(0, patch); // No metadata
|
||||
|
||||
size_t source_offset = 0;
|
||||
size_t target_offset = 0;
|
||||
int64_t source_rel_offset = 0;
|
||||
int64_t target_rel_offset = 0;
|
||||
|
||||
while (target_offset < target.size()) {
|
||||
if (source_offset < source.size() &&
|
||||
source[source_offset] == target[target_offset]) {
|
||||
size_t length = 0;
|
||||
while (source_offset + length < source.size() &&
|
||||
target_offset + length < target.size() &&
|
||||
source[source_offset + length] == target[target_offset + length]) {
|
||||
length++;
|
||||
}
|
||||
encode((length - 1) << 2 | 0, patch); // SourceRead
|
||||
source_offset += length;
|
||||
target_offset += length;
|
||||
} else {
|
||||
size_t length = 0;
|
||||
while (
|
||||
target_offset + length < target.size() &&
|
||||
(source_offset + length >= source.size() ||
|
||||
source[source_offset + length] != target[target_offset + length])) {
|
||||
length++;
|
||||
}
|
||||
if (length > 0) {
|
||||
encode((length - 1) << 2 | 1, patch); // TargetRead
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
patch.push_back(target[target_offset + i]);
|
||||
}
|
||||
target_offset += length;
|
||||
}
|
||||
}
|
||||
|
||||
// SourceCopy
|
||||
if (source_offset < source.size()) {
|
||||
size_t length = 0;
|
||||
int64_t offset = source_offset - source_rel_offset;
|
||||
while (source_offset + length < source.size() &&
|
||||
target_offset + length < target.size() &&
|
||||
source[source_offset + length] == target[target_offset + length]) {
|
||||
length++;
|
||||
}
|
||||
if (length > 0) {
|
||||
encode((length - 1) << 2 | 2, patch);
|
||||
encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
|
||||
source_offset += length;
|
||||
target_offset += length;
|
||||
source_rel_offset = source_offset;
|
||||
}
|
||||
}
|
||||
|
||||
// TargetCopy
|
||||
if (target_offset > 0) {
|
||||
size_t length = 0;
|
||||
int64_t offset = target_offset - target_rel_offset;
|
||||
while (target_offset + length < target.size() &&
|
||||
target[target_offset - 1] == target[target_offset + length]) {
|
||||
length++;
|
||||
}
|
||||
if (length > 0) {
|
||||
encode((length - 1) << 2 | 3, patch);
|
||||
encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
|
||||
target_offset += length;
|
||||
target_rel_offset = target_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
patch.resize(patch.size() + 12); // Make space for the checksums
|
||||
uint32_t source_checksum = crc32(source);
|
||||
uint32_t target_checksum = crc32(target);
|
||||
uint32_t patch_checksum = crc32(patch);
|
||||
|
||||
memcpy(patch.data() + patch.size() - 12, &source_checksum, sizeof(uint32_t));
|
||||
memcpy(patch.data() + patch.size() - 8, &target_checksum, sizeof(uint32_t));
|
||||
memcpy(patch.data() + patch.size() - 4, &patch_checksum, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void ApplyBpsPatch(const std::vector<uint8_t> &source,
|
||||
const std::vector<uint8_t> &patch,
|
||||
std::vector<uint8_t> &target) {
|
||||
if (patch.size() < 4 || patch[0] != 'B' || patch[1] != 'P' ||
|
||||
patch[2] != 'S' || patch[3] != '1') {
|
||||
throw std::runtime_error("Invalid patch format");
|
||||
}
|
||||
|
||||
size_t patch_offset = 4;
|
||||
uint64_t target_size = decode(patch, patch_offset);
|
||||
uint64_t metadata_size = decode(patch, patch_offset);
|
||||
patch_offset += metadata_size;
|
||||
|
||||
target.resize(target_size);
|
||||
size_t source_offset = 0;
|
||||
size_t target_offset = 0;
|
||||
int64_t source_rel_offset = 0;
|
||||
int64_t target_rel_offset = 0;
|
||||
|
||||
while (patch_offset < patch.size() - 12) {
|
||||
uint64_t data = decode(patch, patch_offset);
|
||||
uint64_t command = data & 3;
|
||||
uint64_t length = (data >> 2) + 1;
|
||||
|
||||
switch (command) {
|
||||
case 0: // SourceRead
|
||||
while (length--) {
|
||||
target[target_offset++] = source[source_offset++];
|
||||
}
|
||||
break;
|
||||
case 1: // TargetRead
|
||||
while (length--) {
|
||||
target[target_offset++] = patch[patch_offset++];
|
||||
}
|
||||
break;
|
||||
case 2: // SourceCopy
|
||||
{
|
||||
int64_t offsetData = decode(patch, patch_offset);
|
||||
source_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
|
||||
while (length--) {
|
||||
target[target_offset++] = source[source_rel_offset++];
|
||||
}
|
||||
} break;
|
||||
case 3: // TargetCopy
|
||||
{
|
||||
uint64_t offsetData = decode(patch, patch_offset);
|
||||
target_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
|
||||
while (length--) {
|
||||
target[target_offset++] = target[target_rel_offset++];
|
||||
}
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("Invalid patch command");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t source_checksum;
|
||||
uint32_t target_checksum;
|
||||
uint32_t patch_checksum;
|
||||
memcpy(&source_checksum, patch.data() + patch.size() - 12, sizeof(uint32_t));
|
||||
memcpy(&target_checksum, patch.data() + patch.size() - 8, sizeof(uint32_t));
|
||||
memcpy(&patch_checksum, patch.data() + patch.size() - 4, sizeof(uint32_t));
|
||||
|
||||
if (source_checksum != crc32(source) || target_checksum != crc32(target) ||
|
||||
patch_checksum !=
|
||||
crc32(std::vector<uint8_t>(patch.begin(), patch.end() - 4))) {
|
||||
throw std::runtime_error("Checksum mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
absl::StatusOr<std::string> CheckVersion(const char *version) {
|
||||
std::string version_string = version;
|
||||
if (version_string != kYazeVersion) {
|
||||
std::string message =
|
||||
absl::StrFormat("Yaze version mismatch: expected %s, got %s",
|
||||
kYazeVersion.data(), version_string.c_str());
|
||||
return absl::InvalidArgumentError(message);
|
||||
}
|
||||
return version_string;
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
#ifndef YAZE_CORE_COMMON_H
|
||||
#define YAZE_CORE_COMMON_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
@@ -21,16 +20,21 @@ namespace app {
|
||||
*/
|
||||
namespace core {
|
||||
|
||||
std::string UppercaseHexByte(uint8_t byte, bool leading = false);
|
||||
std::string UppercaseHexWord(uint16_t word, bool leading = false);
|
||||
std::string UppercaseHexLong(uint32_t dword);
|
||||
std::string UppercaseHexLongLong(uint64_t qword);
|
||||
|
||||
bool StringReplace(std::string &str, const std::string &from,
|
||||
const std::string &to);
|
||||
|
||||
/**
|
||||
* @class ExperimentFlags
|
||||
* @brief A class to manage experimental feature flags.
|
||||
*/
|
||||
class ExperimentFlags {
|
||||
public:
|
||||
public:
|
||||
struct Flags {
|
||||
// Bitmap manager abstraction to manage graphics bin of Rom.
|
||||
bool kUseBitmapManager = true;
|
||||
|
||||
// Log instructions to the GUI debugger.
|
||||
bool kLogInstructions = true;
|
||||
|
||||
@@ -56,22 +60,18 @@ class ExperimentFlags {
|
||||
// Use the new platform specific file dialog wrappers.
|
||||
bool kNewFileDialogWrapper = true;
|
||||
|
||||
// Platform specific loading of fonts from the system. Currently
|
||||
// only supports macOS.
|
||||
bool kLoadSystemFonts = true;
|
||||
|
||||
// Uses texture streaming from SDL for my dynamic updates.
|
||||
bool kLoadTexturesAsStreaming = true;
|
||||
|
||||
// Save dungeon map edits to the Rom.
|
||||
bool kSaveDungeonMaps = false;
|
||||
|
||||
// Save graphics sheet to the Rom.
|
||||
bool kSaveGraphicsSheet = false;
|
||||
|
||||
// Log to the console.
|
||||
bool kLogToConsole = false;
|
||||
|
||||
// Load audio device for emulator
|
||||
bool kLoadAudioDevice = false;
|
||||
|
||||
// Overworld flags
|
||||
struct Overworld {
|
||||
// Load and render overworld sprites to the screen. Unstable.
|
||||
@@ -91,6 +91,9 @@ class ExperimentFlags {
|
||||
|
||||
// Save overworld properties to the Rom.
|
||||
bool kSaveOverworldProperties = true;
|
||||
|
||||
// Load custom overworld data from the ROM and enable UI.
|
||||
bool kLoadCustomOverworld = false;
|
||||
} overworld;
|
||||
};
|
||||
|
||||
@@ -109,8 +112,44 @@ class ExperimentFlags {
|
||||
}
|
||||
return flags_.get();
|
||||
}
|
||||
std::string Serialize() const {
|
||||
std::string result;
|
||||
result +=
|
||||
"kLogInstructions: " + std::to_string(flags_->kLogInstructions) + "\n";
|
||||
result +=
|
||||
"kUseNewImGuiInput: " + std::to_string(flags_->kUseNewImGuiInput) +
|
||||
"\n";
|
||||
result +=
|
||||
"kSaveAllPalettes: " + std::to_string(flags_->kSaveAllPalettes) + "\n";
|
||||
result +=
|
||||
"kSaveGfxGroups: " + std::to_string(flags_->kSaveGfxGroups) + "\n";
|
||||
result += "kSaveWithChangeQueue: " +
|
||||
std::to_string(flags_->kSaveWithChangeQueue) + "\n";
|
||||
result += "kDrawDungeonRoomGraphics: " +
|
||||
std::to_string(flags_->kDrawDungeonRoomGraphics) + "\n";
|
||||
result += "kNewFileDialogWrapper: " +
|
||||
std::to_string(flags_->kNewFileDialogWrapper) + "\n";
|
||||
result += "kLoadTexturesAsStreaming: " +
|
||||
std::to_string(flags_->kLoadTexturesAsStreaming) + "\n";
|
||||
result +=
|
||||
"kSaveDungeonMaps: " + std::to_string(flags_->kSaveDungeonMaps) + "\n";
|
||||
result += "kLogToConsole: " + std::to_string(flags_->kLogToConsole) + "\n";
|
||||
result += "kDrawOverworldSprites: " +
|
||||
std::to_string(flags_->overworld.kDrawOverworldSprites) + "\n";
|
||||
result += "kSaveOverworldMaps: " +
|
||||
std::to_string(flags_->overworld.kSaveOverworldMaps) + "\n";
|
||||
result += "kSaveOverworldEntrances: " +
|
||||
std::to_string(flags_->overworld.kSaveOverworldEntrances) + "\n";
|
||||
result += "kSaveOverworldExits: " +
|
||||
std::to_string(flags_->overworld.kSaveOverworldExits) + "\n";
|
||||
result += "kSaveOverworldItems: " +
|
||||
std::to_string(flags_->overworld.kSaveOverworldItems) + "\n";
|
||||
result += "kSaveOverworldProperties: " +
|
||||
std::to_string(flags_->overworld.kSaveOverworldProperties) + "\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
static std::shared_ptr<Flags> flags_;
|
||||
};
|
||||
|
||||
@@ -119,9 +158,8 @@ class ExperimentFlags {
|
||||
* @brief A class to manage a value that can be modified and notify when it
|
||||
* changes.
|
||||
*/
|
||||
template <typename T>
|
||||
class NotifyValue {
|
||||
public:
|
||||
template <typename T> class NotifyValue {
|
||||
public:
|
||||
NotifyValue() : value_(), modified_(false), temp_value_() {}
|
||||
NotifyValue(const T &value)
|
||||
: value_(value), modified_(false), temp_value_() {}
|
||||
@@ -154,69 +192,108 @@ class NotifyValue {
|
||||
|
||||
bool modified() const { return modified_; }
|
||||
|
||||
private:
|
||||
private:
|
||||
T value_;
|
||||
bool modified_;
|
||||
T temp_value_;
|
||||
};
|
||||
|
||||
class ImGuiIdIssuer {
|
||||
private:
|
||||
static std::stack<ImGuiID> idStack;
|
||||
static bool log_to_console = false;
|
||||
static std::string log_file_out = "log.txt";
|
||||
|
||||
public:
|
||||
// Generate and push a new ID onto the stack
|
||||
static ImGuiID GetNewID() {
|
||||
static int counter = 1; // Start from 1 to ensure uniqueness
|
||||
ImGuiID child_id = ImGui::GetID((void *)(intptr_t)counter++);
|
||||
idStack.push(child_id);
|
||||
return child_id;
|
||||
template <typename... Args>
|
||||
static void logf(const absl::FormatSpec<Args...> &format, const Args &...args) {
|
||||
std::string message = absl::StrFormat(format, args...);
|
||||
if (log_to_console) {
|
||||
std::cout << message << std::endl;
|
||||
}
|
||||
static std::ofstream fout(log_file_out, std::ios::out | std::ios::app);
|
||||
fout << message << std::endl;
|
||||
}
|
||||
|
||||
// Pop all IDs from the stack (can be called explicitly or upon program exit)
|
||||
static void Cleanup() {
|
||||
while (!idStack.empty()) {
|
||||
idStack.pop();
|
||||
}
|
||||
}
|
||||
struct StructuredLog {
|
||||
std::string raw_message;
|
||||
std::string category;
|
||||
};
|
||||
|
||||
static absl::flat_hash_map<std::string, std::vector<std::string>> log_categories;
|
||||
|
||||
template <typename... Args>
|
||||
static void logm(const std::string &category,
|
||||
const absl::FormatSpec<Args...> &format, const Args &...args) {
|
||||
std::string message = absl::StrFormat(format, args...);
|
||||
if (log_to_console) {
|
||||
std::cout << category << ": " << message << std::endl;
|
||||
}
|
||||
if (log_categories.contains(category)) {
|
||||
log_categories[category].push_back(message);
|
||||
} else {
|
||||
log_categories[category] = {message};
|
||||
}
|
||||
}
|
||||
|
||||
class Logger {
|
||||
public:
|
||||
public:
|
||||
static void log(std::string message) {
|
||||
static std::ofstream fout("log.txt", std::ios::out | std::ios::app);
|
||||
static std::ofstream fout(log_file_out, std::ios::out | std::ios::app);
|
||||
fout << message << std::endl;
|
||||
}
|
||||
|
||||
// log to console
|
||||
static void logc(std::string message) { logs.emplace_back(message); }
|
||||
|
||||
static std::vector<std::string> logs;
|
||||
};
|
||||
|
||||
std::string UppercaseHexByte(uint8_t byte, bool leading = false);
|
||||
std::string UppercaseHexWord(uint16_t word);
|
||||
std::string UppercaseHexLong(uint32_t dword);
|
||||
constexpr uint32_t kFastRomRegion = 0x808000;
|
||||
|
||||
uint32_t SnesToPc(uint32_t addr);
|
||||
uint32_t PcToSnes(uint32_t addr);
|
||||
inline uint32_t SnesToPc(uint32_t addr) noexcept {
|
||||
if (addr >= kFastRomRegion) {
|
||||
addr -= kFastRomRegion;
|
||||
}
|
||||
uint32_t temp = (addr & 0x7FFF) + ((addr / 2) & 0xFF8000);
|
||||
return (temp + 0x0);
|
||||
}
|
||||
|
||||
uint32_t MapBankToWordAddress(uint8_t bank, uint16_t addr);
|
||||
inline uint32_t PcToSnes(uint32_t addr) {
|
||||
uint8_t *b = reinterpret_cast<uint8_t *>(&addr);
|
||||
b[2] = static_cast<uint8_t>(b[2] * 2);
|
||||
|
||||
int AddressFromBytes(uint8_t addr1, uint8_t addr2, uint8_t addr3);
|
||||
int HexToDec(char *input, int length);
|
||||
if (b[1] >= 0x80) {
|
||||
b[2] += 1;
|
||||
} else {
|
||||
b[1] += 0x80;
|
||||
}
|
||||
|
||||
bool StringReplace(std::string &str, const std::string &from,
|
||||
const std::string &to);
|
||||
return addr;
|
||||
}
|
||||
|
||||
inline int AddressFromBytes(uint8_t bank, uint8_t high, uint8_t low) noexcept {
|
||||
return (bank << 16) | (high << 8) | low;
|
||||
}
|
||||
|
||||
inline uint32_t MapBankToWordAddress(uint8_t bank, uint16_t addr) noexcept {
|
||||
uint32_t result = 0;
|
||||
result = (bank << 16) | addr;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t Get24LocalFromPC(uint8_t *data, int addr, bool pc = true);
|
||||
|
||||
/**
|
||||
* @brief Store little endian 16-bit value using a byte pointer, offset by an
|
||||
* index before dereferencing
|
||||
*/
|
||||
void stle16b_i(uint8_t *const p_arr, size_t const p_index,
|
||||
uint16_t const p_val);
|
||||
uint16_t ldle16b_i(uint8_t const *const p_arr, size_t const p_index);
|
||||
|
||||
uint16_t ldle16b(uint8_t const *const p_arr);
|
||||
|
||||
void stle16b(uint8_t *const p_arr, uint16_t const p_val);
|
||||
|
||||
/**
|
||||
* @brief Load little endian halfword (16-bit) dereferenced from an arrays of
|
||||
* bytes. This version provides an index that will be multiplied by 2 and added
|
||||
* to the base address.
|
||||
*/
|
||||
uint16_t ldle16b_i(uint8_t const *const p_arr, size_t const p_index);
|
||||
|
||||
// Load little endian halfword (16-bit) dereferenced from
|
||||
uint16_t ldle16b(uint8_t const *const p_arr);
|
||||
|
||||
struct FolderItem {
|
||||
std::string name;
|
||||
std::vector<FolderItem> subfolders;
|
||||
@@ -225,10 +302,20 @@ struct FolderItem {
|
||||
|
||||
typedef struct FolderItem FolderItem;
|
||||
|
||||
uint32_t Get24LocalFromPC(uint8_t *data, int addr, bool pc = true);
|
||||
void CreateBpsPatch(const std::vector<uint8_t> &source,
|
||||
const std::vector<uint8_t> &target,
|
||||
std::vector<uint8_t> &patch);
|
||||
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
void ApplyBpsPatch(const std::vector<uint8_t> &source,
|
||||
const std::vector<uint8_t> &patch,
|
||||
std::vector<uint8_t> &target);
|
||||
|
||||
constexpr std::string_view kYazeVersion = "0.2.1";
|
||||
|
||||
absl::StatusOr<std::string> CheckVersion(const char *version);
|
||||
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
#ifndef YAZE_APP_CORE_CONSTANTS_H
|
||||
#define YAZE_APP_CORE_CONSTANTS_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
#define TAB_BAR(w) if (ImGui::BeginTabBar(w)) {
|
||||
#define END_TAB_BAR() \
|
||||
ImGui::EndTabBar(); \
|
||||
}
|
||||
|
||||
#define TAB_ITEM(w) if (ImGui::BeginTabItem(w)) {
|
||||
#define END_TAB_ITEM() \
|
||||
ImGui::EndTabItem(); \
|
||||
@@ -121,732 +112,5 @@
|
||||
using ushort = unsigned short;
|
||||
using uint = unsigned int;
|
||||
using uchar = unsigned char;
|
||||
using Bytes = std::vector<uint8_t>;
|
||||
|
||||
using OWBlockset = std::vector<std::vector<uint16_t>>;
|
||||
struct OWMapTiles {
|
||||
OWBlockset light_world; // 64 maps
|
||||
OWBlockset dark_world; // 64 maps
|
||||
OWBlockset special_world; // 32 maps
|
||||
};
|
||||
using OWMapTiles = struct OWMapTiles;
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
constexpr uint32_t kRedPen = 0xFF0000FF;
|
||||
constexpr float kYazeVersion = 0.2;
|
||||
|
||||
// ============================================================================
|
||||
// Magic numbers
|
||||
// ============================================================================
|
||||
|
||||
/// Bit set for object priority
|
||||
constexpr ushort TilePriorityBit = 0x2000;
|
||||
|
||||
/// Bit set for object hflip
|
||||
constexpr ushort TileHFlipBit = 0x4000;
|
||||
|
||||
/// Bit set for object vflip
|
||||
constexpr ushort TileVFlipBit = 0x8000;
|
||||
|
||||
/// Bits used for tile name
|
||||
constexpr ushort TileNameMask = 0x03FF;
|
||||
|
||||
constexpr int Uncompressed3BPPSize = 0x0600;
|
||||
constexpr int UncompressedSheetSize = 0x0800;
|
||||
|
||||
constexpr int NumberOfRooms = 296;
|
||||
|
||||
constexpr int NumberOfColors = 3143;
|
||||
|
||||
// ============================================================================
|
||||
// Game Graphics
|
||||
// ============================================================================
|
||||
|
||||
constexpr int tile_address = 0x1B52; // JP = Same
|
||||
constexpr int tile_address_floor = 0x1B5A; // JP = Same
|
||||
constexpr int subtype1_tiles = 0x8000; // JP = Same
|
||||
constexpr int subtype2_tiles = 0x83F0; // JP = Same
|
||||
constexpr int subtype3_tiles = 0x84F0; // JP = Same
|
||||
constexpr int gfx_animated_pointer = 0x10275; // JP 0x10624 //long pointer
|
||||
|
||||
constexpr int hud_palettes = 0xDD660;
|
||||
constexpr int maxGfx = 0xC3FB5;
|
||||
|
||||
constexpr int kTilesheetWidth = 128;
|
||||
constexpr int kTilesheetHeight = 32;
|
||||
constexpr int kTilesheetDepth = 8;
|
||||
|
||||
// TEXT EDITOR RELATED CONSTANTS
|
||||
constexpr int gfx_font = 0x70000; // 2bpp format
|
||||
constexpr int text_data = 0xE0000;
|
||||
constexpr int text_data2 = 0x75F40;
|
||||
constexpr int pointers_dictionaries = 0x74703;
|
||||
constexpr int characters_width = 0x74ADF;
|
||||
|
||||
constexpr int entrance_gfx_group = 0x5D97;
|
||||
|
||||
// ============================================================================
|
||||
// Gravestones related variables
|
||||
// ============================================================================
|
||||
|
||||
constexpr int GravesYTilePos = 0x49968; // short (0x0F entries)
|
||||
constexpr int GravesXTilePos = 0x49986; // short (0x0F entries)
|
||||
constexpr int GravesTilemapPos = 0x499A4; // short (0x0F entries)
|
||||
constexpr int GravesGFX = 0x499C2; // short (0x0F entries)
|
||||
|
||||
constexpr int GravesXPos = 0x4994A; // short (0x0F entries)
|
||||
constexpr int GravesYLine = 0x4993A; // short (0x08 entries)
|
||||
constexpr int GravesCountOnY = 0x499E0; // Byte 0x09 entries
|
||||
|
||||
constexpr int GraveLinkSpecialHole = 0x46DD9; // short
|
||||
constexpr int GraveLinkSpecialStairs = 0x46DE0; // short
|
||||
|
||||
// ============================================================================
|
||||
// Names
|
||||
// ============================================================================
|
||||
|
||||
static const std::string RoomEffect[] = {"Nothing",
|
||||
"Nothing",
|
||||
"Moving Floor",
|
||||
"Moving Water",
|
||||
"Trinexx Shell",
|
||||
"Red Flashes",
|
||||
"Light Torch to See Floor",
|
||||
"Ganon's Darkness"};
|
||||
|
||||
static const std::string RoomTag[] = {"Nothing",
|
||||
|
||||
"NW Kill Enemy to Open",
|
||||
"NE Kill Enemy to Open",
|
||||
"SW Kill Enemy to Open",
|
||||
"SE Kill Enemy to Open",
|
||||
"W Kill Enemy to Open",
|
||||
"E Kill Enemy to Open",
|
||||
"N Kill Enemy to Open",
|
||||
"S Kill Enemy to Open",
|
||||
"Clear Quadrant to Open",
|
||||
"Clear Full Tile to Open",
|
||||
|
||||
"NW Push Block to Open",
|
||||
"NE Push Block to Open",
|
||||
"SW Push Block to Open",
|
||||
"SE Push Block to Open",
|
||||
"W Push Block to Open",
|
||||
"E Push Block to Open",
|
||||
"N Push Block to Open",
|
||||
"S Push Block to Open",
|
||||
"Push Block to Open",
|
||||
"Pull Lever to Open",
|
||||
"Collect Prize to Open",
|
||||
|
||||
"Hold Switch Open Door",
|
||||
"Toggle Switch to Open Door",
|
||||
"Turn off Water",
|
||||
"Turn on Water",
|
||||
"Water Gate",
|
||||
"Water Twin",
|
||||
"Moving Wall Right",
|
||||
"Moving Wall Left",
|
||||
"Crash",
|
||||
"Crash",
|
||||
"Push Switch Exploding Wall",
|
||||
"Holes 0",
|
||||
"Open Chest (Holes 0)",
|
||||
"Holes 1",
|
||||
"Holes 2",
|
||||
"Defeat Boss for Dungeon Prize",
|
||||
|
||||
"SE Kill Enemy to Push Block",
|
||||
"Trigger Switch Chest",
|
||||
"Pull Lever Exploding Wall",
|
||||
"NW Kill Enemy for Chest",
|
||||
"NE Kill Enemy for Chest",
|
||||
"SW Kill Enemy for Chest",
|
||||
"SE Kill Enemy for Chest",
|
||||
"W Kill Enemy for Chest",
|
||||
"E Kill Enemy for Chest",
|
||||
"N Kill Enemy for Chest",
|
||||
"S Kill Enemy for Chest",
|
||||
"Clear Quadrant for Chest",
|
||||
"Clear Full Tile for Chest",
|
||||
|
||||
"Light Torches to Open",
|
||||
"Holes 3",
|
||||
"Holes 4",
|
||||
"Holes 5",
|
||||
"Holes 6",
|
||||
"Agahnim Room",
|
||||
"Holes 7",
|
||||
"Holes 8",
|
||||
"Open Chest for Holes 8",
|
||||
"Push Block for Chest",
|
||||
"Clear Room for Triforce Door",
|
||||
"Light Torches for Chest",
|
||||
"Kill Boss Again"};
|
||||
|
||||
static const std::string SecretItemNames[] = {
|
||||
"Nothing", "Green Rupee", "Rock hoarder", "Bee", "Health pack",
|
||||
"Bomb", "Heart ", "Blue Rupee",
|
||||
|
||||
"Key", "Arrow", "Bomb", "Heart", "Magic",
|
||||
"Full Magic", "Cucco", "Green Soldier", "Bush Stal", "Blue Soldier",
|
||||
|
||||
"Landmine", "Heart", "Fairy", "Heart",
|
||||
"Nothing ", // 22
|
||||
|
||||
"Hole", "Warp", "Staircase", "Bombable", "Switch"};
|
||||
|
||||
static const std::string TileTypeNames[] = {
|
||||
"$00 Nothing (standard floor)",
|
||||
"$01 Collision",
|
||||
"$02 Collision",
|
||||
"$03 Collision",
|
||||
"$04 Collision",
|
||||
"$05 Nothing (unused?)",
|
||||
"$06 Nothing (unused?)",
|
||||
"$07 Nothing (unused?)",
|
||||
"$08 Deep water",
|
||||
"$09 Shallow water",
|
||||
"$0A Unknown? Possibly unused",
|
||||
"$0B Collision (different in Overworld and unknown)",
|
||||
"$0C Overlay mask",
|
||||
"$0D Spike floor",
|
||||
"$0E GT ice",
|
||||
"$0F Ice palace ice",
|
||||
"$10 Slope ◤",
|
||||
"$11 Slope ◥",
|
||||
"$12 Slope ◣",
|
||||
"$13 Slope ◢",
|
||||
"$14 Nothing (unused?)",
|
||||
"$15 Nothing (unused?)",
|
||||
"$16 Nothing (unused?)",
|
||||
"$17 Nothing (unused?)",
|
||||
"$18 Slope ◤",
|
||||
"$19 Slope ◥",
|
||||
"$1A Slope ◣",
|
||||
"$1B Slope ◢",
|
||||
"$1C Layer 2 overlay",
|
||||
"$1D North single-layer auto stairs",
|
||||
"$1E North layer-swap auto stairs",
|
||||
"$1F North layer-swap auto stairs",
|
||||
"$20 Pit",
|
||||
"$21 Nothing (unused?)",
|
||||
"$22 Manual stairs",
|
||||
"$23 Pot switch",
|
||||
"$24 Pressure switch",
|
||||
"$25 Nothing (unused but referenced by somaria blocks)",
|
||||
"$26 Collision (near stairs?)",
|
||||
"$27 Brazier/Fence/Statue/Block/General hookable things",
|
||||
"$28 North ledge",
|
||||
"$29 South ledge",
|
||||
"$2A East ledge",
|
||||
"$2B West ledge",
|
||||
"$2C ◤ ledge",
|
||||
"$2D ◣ ledge",
|
||||
"$2E ◥ ledge",
|
||||
"$2F ◢ ledge",
|
||||
"$30 Straight inter-room stairs south/up 0",
|
||||
"$31 Straight inter-room stairs south/up 1",
|
||||
"$32 Straight inter-room stairs south/up 2",
|
||||
"$33 Straight inter-room stairs south/up 3",
|
||||
"$34 Straight inter-room stairs north/down 0",
|
||||
"$35 Straight inter-room stairs north/down 1",
|
||||
"$36 Straight inter-room stairs north/down 2",
|
||||
"$37 Straight inter-room stairs north/down 3",
|
||||
"$38 Straight inter-room stairs north/down edge",
|
||||
"$39 Straight inter-room stairs south/up edge",
|
||||
"$3A Star tile (inactive on load)",
|
||||
"$3B Star tile (active on load)",
|
||||
"$3C Nothing (unused?)",
|
||||
"$3D South single-layer auto stairs",
|
||||
"$3E South layer-swap auto stairs",
|
||||
"$3F South layer-swap auto stairs",
|
||||
"$40 Thick grass",
|
||||
"$41 Nothing (unused?)",
|
||||
"$42 Gravestone / Tower of hera ledge shadows??",
|
||||
"$43 Skull Woods entrance/Hera columns???",
|
||||
"$44 Spike",
|
||||
"$45 Nothing (unused?)",
|
||||
"$46 Desert Tablet",
|
||||
"$47 Nothing (unused?)",
|
||||
"$48 Diggable ground",
|
||||
"$49 Nothing (unused?)",
|
||||
"$4A Diggable ground",
|
||||
"$4B Warp tile",
|
||||
"$4C Nothing (unused?) | Something unknown in overworld",
|
||||
"$4D Nothing (unused?) | Something unknown in overworld",
|
||||
"$4E Square corners in EP overworld",
|
||||
"$4F Square corners in EP overworld",
|
||||
"$50 Green bush",
|
||||
"$51 Dark bush",
|
||||
"$52 Gray rock",
|
||||
"$53 Black rock",
|
||||
"$54 Hint tile/Sign",
|
||||
"$55 Big gray rock",
|
||||
"$56 Big black rock",
|
||||
"$57 Bonk rocks",
|
||||
"$58 Chest 0",
|
||||
"$59 Chest 1",
|
||||
"$5A Chest 2",
|
||||
"$5B Chest 3",
|
||||
"$5C Chest 4",
|
||||
"$5D Chest 5",
|
||||
"$5E Spiral stairs",
|
||||
"$5F Spiral stairs",
|
||||
"$60 Rupee tile",
|
||||
"$61 Nothing (unused?)",
|
||||
"$62 Bombable floor",
|
||||
"$63 Minigame chest",
|
||||
"$64 Nothing (unused?)",
|
||||
"$65 Nothing (unused?)",
|
||||
"$66 Crystal peg down",
|
||||
"$67 Crystal peg up",
|
||||
"$68 Upwards conveyor",
|
||||
"$69 Downwards conveyor",
|
||||
"$6A Leftwards conveyor",
|
||||
"$6B Rightwards conveyor",
|
||||
"$6C North vines",
|
||||
"$6D South vines",
|
||||
"$6E West vines",
|
||||
"$6F East vines",
|
||||
"$70 Pot/Hammer peg/Push block 00",
|
||||
"$71 Pot/Hammer peg/Push block 01",
|
||||
"$72 Pot/Hammer peg/Push block 02",
|
||||
"$73 Pot/Hammer peg/Push block 03",
|
||||
"$74 Pot/Hammer peg/Push block 04",
|
||||
"$75 Pot/Hammer peg/Push block 05",
|
||||
"$76 Pot/Hammer peg/Push block 06",
|
||||
"$77 Pot/Hammer peg/Push block 07",
|
||||
"$78 Pot/Hammer peg/Push block 08",
|
||||
"$79 Pot/Hammer peg/Push block 09",
|
||||
"$7A Pot/Hammer peg/Push block 0A",
|
||||
"$7B Pot/Hammer peg/Push block 0B",
|
||||
"$7C Pot/Hammer peg/Push block 0C",
|
||||
"$7D Pot/Hammer peg/Push block 0D",
|
||||
"$7E Pot/Hammer peg/Push block 0E",
|
||||
"$7F Pot/Hammer peg/Push block 0F",
|
||||
"$80 North/South door",
|
||||
"$81 East/West door",
|
||||
"$82 North/South shutter door",
|
||||
"$83 East/West shutter door",
|
||||
"$84 North/South layer 2 door",
|
||||
"$85 East/West layer 2 door",
|
||||
"$86 North/South layer 2 shutter door",
|
||||
"$87 East/West layer 2 shutter door",
|
||||
"$88 Some type of door (?)",
|
||||
"$89 East/West transport door",
|
||||
"$8A Some type of door (?)",
|
||||
"$8B Some type of door (?)",
|
||||
"$8C Some type of door (?)",
|
||||
"$8D Some type of door (?)",
|
||||
"$8E Entrance door",
|
||||
"$8F Entrance door",
|
||||
"$90 Layer toggle shutter door (?)",
|
||||
"$91 Layer toggle shutter door (?)",
|
||||
"$92 Layer toggle shutter door (?)",
|
||||
"$93 Layer toggle shutter door (?)",
|
||||
"$94 Layer toggle shutter door (?)",
|
||||
"$95 Layer toggle shutter door (?)",
|
||||
"$96 Layer toggle shutter door (?)",
|
||||
"$97 Layer toggle shutter door (?)",
|
||||
"$98 Layer+Dungeon toggle shutter door (?)",
|
||||
"$99 Layer+Dungeon toggle shutter door (?)",
|
||||
"$9A Layer+Dungeon toggle shutter door (?)",
|
||||
"$9B Layer+Dungeon toggle shutter door (?)",
|
||||
"$9C Layer+Dungeon toggle shutter door (?)",
|
||||
"$9D Layer+Dungeon toggle shutter door (?)",
|
||||
"$9E Layer+Dungeon toggle shutter door (?)",
|
||||
"$9F Layer+Dungeon toggle shutter door (?)",
|
||||
"$A0 North/South Dungeon swap door",
|
||||
"$A1 Dungeon toggle door (?)",
|
||||
"$A2 Dungeon toggle door (?)",
|
||||
"$A3 Dungeon toggle door (?)",
|
||||
"$A4 Dungeon toggle door (?)",
|
||||
"$A5 Dungeon toggle door (?)",
|
||||
"$A6 Nothing (unused?)",
|
||||
"$A7 Nothing (unused?)",
|
||||
"$A8 Layer+Dungeon toggle shutter door (?)",
|
||||
"$A9 Layer+Dungeon toggle shutter door (?)",
|
||||
"$AA Layer+Dungeon toggle shutter door (?)",
|
||||
"$AB Layer+Dungeon toggle shutter door (?)",
|
||||
"$AC Layer+Dungeon toggle shutter door (?)",
|
||||
"$AD Layer+Dungeon toggle shutter door (?)",
|
||||
"$AE Layer+Dungeon toggle shutter door (?)",
|
||||
"$AF Layer+Dungeon toggle shutter door (?)",
|
||||
"$B0 Somaria ─",
|
||||
"$B1 Somaria │",
|
||||
"$B2 Somaria ┌",
|
||||
"$B3 Somaria └",
|
||||
"$B4 Somaria ┐",
|
||||
"$B5 Somaria ┘",
|
||||
"$B6 Somaria ⍰ 1 way",
|
||||
"$B7 Somaria ┬",
|
||||
"$B8 Somaria ┴",
|
||||
"$B9 Somaria ├",
|
||||
"$BA Somaria ┤",
|
||||
"$BB Somaria ┼",
|
||||
"$BC Somaria ⍰ 2 way",
|
||||
"$BD Somaria ┼ crossover",
|
||||
"$BE Pipe entrance",
|
||||
"$BF Nothing (unused?)",
|
||||
"$C0 Torch 00",
|
||||
"$C1 Torch 01",
|
||||
"$C2 Torch 02",
|
||||
"$C3 Torch 03",
|
||||
"$C4 Torch 04",
|
||||
"$C5 Torch 05",
|
||||
"$C6 Torch 06",
|
||||
"$C7 Torch 07",
|
||||
"$C8 Torch 08",
|
||||
"$C9 Torch 09",
|
||||
"$CA Torch 0A",
|
||||
"$CB Torch 0B",
|
||||
"$CC Torch 0C",
|
||||
"$CD Torch 0D",
|
||||
"$CE Torch 0E",
|
||||
"$CF Torch 0F",
|
||||
"$D0 Nothing (unused?)",
|
||||
"$D1 Nothing (unused?)",
|
||||
"$D2 Nothing (unused?)",
|
||||
"$D3 Nothing (unused?)",
|
||||
"$D4 Nothing (unused?)",
|
||||
"$D5 Nothing (unused?)",
|
||||
"$D6 Nothing (unused?)",
|
||||
"$D7 Nothing (unused?)",
|
||||
"$D8 Nothing (unused?)",
|
||||
"$D9 Nothing (unused?)",
|
||||
"$DA Nothing (unused?)",
|
||||
"$DB Nothing (unused?)",
|
||||
"$DC Nothing (unused?)",
|
||||
"$DD Nothing (unused?)",
|
||||
"$DE Nothing (unused?)",
|
||||
"$DF Nothing (unused?)",
|
||||
"$E0 Nothing (unused?)",
|
||||
"$E1 Nothing (unused?)",
|
||||
"$E2 Nothing (unused?)",
|
||||
"$E3 Nothing (unused?)",
|
||||
"$E4 Nothing (unused?)",
|
||||
"$E5 Nothing (unused?)",
|
||||
"$E6 Nothing (unused?)",
|
||||
"$E7 Nothing (unused?)",
|
||||
"$E8 Nothing (unused?)",
|
||||
"$E9 Nothing (unused?)",
|
||||
"$EA Nothing (unused?)",
|
||||
"$EB Nothing (unused?)",
|
||||
"$EC Nothing (unused?)",
|
||||
"$ED Nothing (unused?)",
|
||||
"$EE Nothing (unused?)",
|
||||
"$EF Nothing (unused?)",
|
||||
"$F0 Door 0 bottom",
|
||||
"$F1 Door 1 bottom",
|
||||
"$F2 Door 2 bottom",
|
||||
"$F3 Door 3 bottom",
|
||||
"$F4 Door X bottom? (unused?)",
|
||||
"$F5 Door X bottom? (unused?)",
|
||||
"$F6 Door X bottom? (unused?)",
|
||||
"$F7 Door X bottom? (unused?)",
|
||||
"$F8 Door 0 top",
|
||||
"$F9 Door 1 top",
|
||||
"$FA Door 2 top",
|
||||
"$FB Door 3 top",
|
||||
"$FC Door X top? (unused?)",
|
||||
"$FD Door X top? (unused?)",
|
||||
"$FE Door X top? (unused?)",
|
||||
"$FF Door X top? (unused?)"};
|
||||
|
||||
static const std::string kSpriteDefaultNames[]{
|
||||
"00 Raven",
|
||||
"01 Vulture",
|
||||
"02 Flying Stalfos Head",
|
||||
"03 No Pointer (Empty",
|
||||
"04 Pull Switch (good",
|
||||
"05 Pull Switch (unused",
|
||||
"06 Pull Switch (bad",
|
||||
"07 Pull Switch (unused",
|
||||
"08 Octorock (one way",
|
||||
"09 Moldorm (Boss",
|
||||
"0A Octorock (four way",
|
||||
"0B Chicken",
|
||||
"0C Octorock (?",
|
||||
"0D Buzzblock",
|
||||
"0E Snapdragon",
|
||||
"0F Octoballoon",
|
||||
"10 Octoballon Hatchlings",
|
||||
"11 Hinox",
|
||||
"12 Moblin",
|
||||
"13 Mini Helmasaure",
|
||||
"14 Gargoyle's Domain Gate",
|
||||
"15 Antifairy",
|
||||
"16 Sahasrahla / Aginah",
|
||||
"17 Bush Hoarder",
|
||||
"18 Mini Moldorm",
|
||||
"19 Poe",
|
||||
"1A Dwarves",
|
||||
"1B Arrow in wall",
|
||||
"1C Statue",
|
||||
"1D Weathervane",
|
||||
"1E Crystal Switch",
|
||||
"1F Bug-Catching Kid",
|
||||
"20 Sluggula",
|
||||
"21 Push Switch",
|
||||
"22 Ropa",
|
||||
"23 Red Bari",
|
||||
"24 Blue Bari",
|
||||
"25 Talking Tree",
|
||||
"26 Hardhat Beetle",
|
||||
"27 Deadrock",
|
||||
"28 Storytellers",
|
||||
"29 Blind Hideout attendant",
|
||||
"2A Sweeping Lady",
|
||||
"2B Storytellers",
|
||||
"2C Lumberjacks",
|
||||
"2D Telepathic Stones",
|
||||
"2E Multipurpose Sprite",
|
||||
"2F Race Npc",
|
||||
"30 Person?",
|
||||
"31 Fortune Teller",
|
||||
"32 Angry Brothers",
|
||||
"33 Pull for items",
|
||||
"34 Scared Girl",
|
||||
"35 Innkeeper",
|
||||
"36 Witch",
|
||||
"37 Waterfall",
|
||||
"38 Arrow Target",
|
||||
"39 Average Middle",
|
||||
"3A Half Magic Bat",
|
||||
"3B Dash Item",
|
||||
"3C Village Kid",
|
||||
"3D Signs? Chicken lady also showed up / Scared ladies outside houses.",
|
||||
"3E Rock Hoarder",
|
||||
"3F Tutorial Soldier",
|
||||
"40 Lightning Lock",
|
||||
"41 Blue Sword Soldier / Used by guards to detect player",
|
||||
"42 Green Sword Soldier",
|
||||
"43 Red Spear Soldier",
|
||||
"44 Assault Sword Soldier",
|
||||
"45 Green Spear Soldier",
|
||||
"46 Blue Archer",
|
||||
"47 Green Archer",
|
||||
"48 Red Javelin Soldier",
|
||||
"49 Red Javelin Soldier 2",
|
||||
"4A Red Bomb Soldiers",
|
||||
"4B Green Soldier Recruits",
|
||||
"4C Geldman",
|
||||
"4D Rabbit",
|
||||
"4E Popo",
|
||||
"4F Popo 2",
|
||||
"50 Cannon Balls",
|
||||
"51 Armos",
|
||||
"52 Giant Zora",
|
||||
"53 Armos Knights (Boss",
|
||||
"54 Lanmolas (Boss",
|
||||
"55 Fireball Zora",
|
||||
"56 Walking Zora",
|
||||
"57 Desert Palace Barriers",
|
||||
"58 Crab",
|
||||
"59 Bird",
|
||||
"5A Squirrel",
|
||||
"5B Spark (Left to Right",
|
||||
"5C Spark (Right to Left",
|
||||
"5D Roller (vertical moving",
|
||||
"5E Roller (vertical moving",
|
||||
"5F Roller",
|
||||
"60 Roller (horizontal moving",
|
||||
"61 Beamos",
|
||||
"62 Master Sword",
|
||||
"63 Devalant (Non",
|
||||
"64 Devalant (Shooter",
|
||||
"65 Shooting Gallery Proprietor",
|
||||
"66 Moving Cannon Ball Shooters (Right",
|
||||
"67 Moving Cannon Ball Shooters (Left",
|
||||
"68 Moving Cannon Ball Shooters (Down",
|
||||
"69 Moving Cannon Ball Shooters (Up",
|
||||
"6A Ball N' Chain Trooper",
|
||||
"6B Cannon Soldier",
|
||||
"6C Mirror Portal",
|
||||
"6D Rat",
|
||||
"6E Rope",
|
||||
"6F Keese",
|
||||
"70 Helmasaur King Fireball",
|
||||
"71 Leever",
|
||||
"72 Activator for the ponds (where you throw in items",
|
||||
"73 Uncle / Priest",
|
||||
"74 Running Man",
|
||||
"75 Bottle Salesman",
|
||||
"76 Princess Zelda",
|
||||
"77 Antifairy (Alternate",
|
||||
"78 Village Elder",
|
||||
"79 Bee",
|
||||
"7A Agahnim",
|
||||
"7B Agahnim Energy Ball",
|
||||
"7C Hyu",
|
||||
"7D Big Spike Trap",
|
||||
"7E Guruguru Bar (Clockwise",
|
||||
"7F Guruguru Bar (Counter Clockwise",
|
||||
"80 Winder",
|
||||
"81 Water Tektite",
|
||||
"82 Antifairy Circle",
|
||||
"83 Green Eyegore",
|
||||
"84 Red Eyegore",
|
||||
"85 Yellow Stalfos",
|
||||
"86 Kodongos",
|
||||
"87 Flames",
|
||||
"88 Mothula (Boss",
|
||||
"89 Mothula's Beam",
|
||||
"8A Spike Trap",
|
||||
"8B Gibdo",
|
||||
"8C Arrghus (Boss",
|
||||
"8D Arrghus spawn",
|
||||
"8E Terrorpin",
|
||||
"8F Slime",
|
||||
"90 Wallmaster",
|
||||
"91 Stalfos Knight",
|
||||
"92 Helmasaur King",
|
||||
"93 Bumper",
|
||||
"94 Swimmers",
|
||||
"95 Eye Laser (Right",
|
||||
"96 Eye Laser (Left",
|
||||
"97 Eye Laser (Down",
|
||||
"98 Eye Laser (Up",
|
||||
"99 Pengator",
|
||||
"9A Kyameron",
|
||||
"9B Wizzrobe",
|
||||
"9C Tadpoles",
|
||||
"9D Tadpoles",
|
||||
"9E Ostrich (Haunted Grove",
|
||||
"9F Flute",
|
||||
"A0 Birds (Haunted Grove",
|
||||
"A1 Freezor",
|
||||
"A2 Kholdstare (Boss",
|
||||
"A3 Kholdstare's Shell",
|
||||
"A4 Falling Ice",
|
||||
"A5 Zazak Fireball",
|
||||
"A6 Red Zazak",
|
||||
"A7 Stalfos",
|
||||
"A8 Bomber Flying Creatures from Darkworld",
|
||||
"A9 Bomber Flying Creatures from Darkworld",
|
||||
"AA Pikit",
|
||||
"AB Maiden",
|
||||
"AC Apple",
|
||||
"AD Lost Old Man",
|
||||
"AE Down Pipe",
|
||||
"AF Up Pipe",
|
||||
"B0 Right Pip",
|
||||
"B1 Left Pipe",
|
||||
"B2 Good bee again?",
|
||||
"B3 Hylian Inscription",
|
||||
"B4 Thief?s chest (not the one that follows you",
|
||||
"B5 Bomb Salesman",
|
||||
"B6 Kiki",
|
||||
"B7 Maiden following you in Blind Dungeon",
|
||||
"B8 Monologue Testing Sprite",
|
||||
"B9 Feuding Friends on Death Mountain",
|
||||
"BA Whirlpool",
|
||||
"BB Salesman / chestgame guy / 300 rupee giver guy / Chest game thief",
|
||||
"BC Drunk in the inn",
|
||||
"BD Vitreous (Large Eyeball",
|
||||
"BE Vitreous (Small Eyeball",
|
||||
"BF Vitreous' Lightning",
|
||||
"C0 Monster in Lake of Ill Omen / Quake Medallion",
|
||||
"C1 Agahnim teleporting Zelda to dark world",
|
||||
"C2 Boulders",
|
||||
"C3 Gibo",
|
||||
"C4 Thief",
|
||||
"C5 Medusa",
|
||||
"C6 Four Way Fireball Spitters (spit when you use your sword",
|
||||
"C7 Hokku",
|
||||
"C8 Big Fairy who heals you",
|
||||
"C9 Tektite",
|
||||
"CA Chain Chomp",
|
||||
"CB Trinexx",
|
||||
"CC Another part of trinexx",
|
||||
"CD Yet another part of trinexx",
|
||||
"CE Blind The Thief (Boss)",
|
||||
"CF Swamola",
|
||||
"D0 Lynel",
|
||||
"D1 Bunny Beam",
|
||||
"D2 Flopping fish",
|
||||
"D3 Stal",
|
||||
"D4 Landmine",
|
||||
"D5 Digging Game Proprietor",
|
||||
"D6 Ganon",
|
||||
"D7 Copy of Ganon",
|
||||
"D8 Heart",
|
||||
"D9 Green Rupee",
|
||||
"DA Blue Rupee",
|
||||
"DB Red Rupee",
|
||||
"DC Bomb Refill (1)",
|
||||
"DD Bomb Refill (4)",
|
||||
"DE Bomb Refill (8)",
|
||||
"DF Small Magic Refill",
|
||||
"E0 Full Magic Refill",
|
||||
"E1 Arrow Refill (5)",
|
||||
"E2 Arrow Refill (10)",
|
||||
"E3 Fairy",
|
||||
"E4 Key",
|
||||
"E5 Big Key",
|
||||
"E6 Shield",
|
||||
"E7 Mushroom",
|
||||
"E8 Fake Master Sword",
|
||||
"E9 Magic Shop dude / His items",
|
||||
"EA Heart Container",
|
||||
"EB Heart Piece",
|
||||
"EC Bushes",
|
||||
"ED Cane Of Somaria Platform",
|
||||
"EE Mantle",
|
||||
"EF Cane of Somaria Platform (Unused)",
|
||||
"F0 Cane of Somaria Platform (Unused)",
|
||||
"F1 Cane of Somaria Platform (Unused)",
|
||||
"F2 Medallion Tablet",
|
||||
"F3",
|
||||
"F4 Falling Rocks",
|
||||
"F5",
|
||||
"F6",
|
||||
"F7",
|
||||
"F8",
|
||||
"F9",
|
||||
"FA",
|
||||
"FB",
|
||||
"FC",
|
||||
"FD",
|
||||
"FE",
|
||||
"FF",
|
||||
};
|
||||
|
||||
static const std::string overlordnames[] = {
|
||||
"Overlord_SpritePositionTarget",
|
||||
"Overlord_AllDirectionMetalBallFactory",
|
||||
"Overlord_CascadeMetalBallFactory",
|
||||
"Overlord_StalfosFactory",
|
||||
"Overlord_StalfosTrap",
|
||||
"Overlord_SnakeTrap",
|
||||
"Overlord_MovingFloor",
|
||||
"Overlord_ZolFactory",
|
||||
"Overlord_WallMasterFactory",
|
||||
"Overlord_CrumbleTilePath 1",
|
||||
"Overlord_CrumbleTilePath 2",
|
||||
"Overlord_CrumbleTilePath 3",
|
||||
"Overlord_CrumbleTilePath 4",
|
||||
"Overlord_CrumbleTilePath 5",
|
||||
"Overlord_CrumbleTilePath 6",
|
||||
"Overlord_PirogusuFactory 1",
|
||||
"Overlord_PirogusuFactory 2",
|
||||
"Overlord_PirogusuFactory 3",
|
||||
"Overlord_PirogusuFactory 4",
|
||||
"Overlord_FlyingTileFactory",
|
||||
"Overlord_WizzrobeFactory",
|
||||
"Overlord_ZoroFactory",
|
||||
"Overlord_StalfosTrapTriggerWindow",
|
||||
"Overlord_RedStalfosTrap",
|
||||
"Overlord_ArmosCoordinator",
|
||||
"Overlord_BombTrap",
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
@@ -2,30 +2,18 @@
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "imgui/backends/imgui_impl_sdl2.h"
|
||||
#include "imgui/backends/imgui_impl_sdlrenderer2.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/imgui_internal.h"
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#include <TargetConditionals.h>
|
||||
#if TARGET_IPHONE_SIMULATOR == 1
|
||||
#include "imgui/backends/imgui_impl_metal.h"
|
||||
#elif TARGET_OS_IPHONE == 1
|
||||
#include "imgui/backends/imgui_impl_metal.h"
|
||||
#elif TARGET_OS_MAC == 1
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/core/platform/font_loader.h"
|
||||
#include "app/editor/master_editor.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/editor/editor_manager.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "core/utils/file_util.h"
|
||||
#include "imgui/backends/imgui_impl_sdl2.h"
|
||||
#include "imgui/backends/imgui_impl_sdlrenderer2.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -38,7 +26,6 @@ constexpr ImGuiWindowFlags kMainEditorFlags =
|
||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar;
|
||||
|
||||
using ::ImVec2;
|
||||
using ImGui::Begin;
|
||||
using ImGui::End;
|
||||
using ImGui::GetIO;
|
||||
@@ -61,205 +48,66 @@ void NewMasterFrame() {
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeKeymap() {
|
||||
} // namespace
|
||||
|
||||
absl::Status Controller::OnEntry(std::string filename) {
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1
|
||||
platform_ = Platform::kiOS;
|
||||
#elif TARGET_OS_MAC == 1
|
||||
platform_ = Platform::kMacOS;
|
||||
#endif
|
||||
#elif defined(_WIN32)
|
||||
platform_ = Platform::kWindows;
|
||||
#elif defined(__linux__)
|
||||
platform_ = Platform::kLinux;
|
||||
#else
|
||||
platform_ = Platform::kUnknown;
|
||||
#endif
|
||||
RETURN_IF_ERROR(CreateWindow())
|
||||
RETURN_IF_ERROR(CreateRenderer())
|
||||
RETURN_IF_ERROR(CreateGuiContext())
|
||||
RETURN_IF_ERROR(LoadAudioDevice())
|
||||
editor_manager_.SetupScreen(filename);
|
||||
active_ = true;
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void Controller::OnInput() {
|
||||
int wheel = 0;
|
||||
SDL_Event event;
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
io.KeyMap[ImGuiKey_LeftSuper] = SDL_GetScancodeFromKey(SDLK_LGUI);
|
||||
io.KeyMap[ImGuiKey_Backspace] = SDL_GetScancodeFromKey(SDLK_BACKSPACE);
|
||||
io.KeyMap[ImGuiKey_LeftShift] = SDL_GetScancodeFromKey(SDLK_LSHIFT);
|
||||
io.KeyMap[ImGuiKey_Enter] = SDL_GetScancodeFromKey(SDLK_RETURN);
|
||||
io.KeyMap[ImGuiKey_UpArrow] = SDL_GetScancodeFromKey(SDLK_UP);
|
||||
io.KeyMap[ImGuiKey_DownArrow] = SDL_GetScancodeFromKey(SDLK_DOWN);
|
||||
io.KeyMap[ImGuiKey_LeftArrow] = SDL_GetScancodeFromKey(SDLK_LEFT);
|
||||
io.KeyMap[ImGuiKey_RightArrow] = SDL_GetScancodeFromKey(SDLK_RIGHT);
|
||||
io.KeyMap[ImGuiKey_Delete] = SDL_GetScancodeFromKey(SDLK_DELETE);
|
||||
io.KeyMap[ImGuiKey_Escape] = SDL_GetScancodeFromKey(SDLK_ESCAPE);
|
||||
io.KeyMap[ImGuiKey_Tab] = SDL_GetScancodeFromKey(SDLK_TAB);
|
||||
io.KeyMap[ImGuiKey_LeftCtrl] = SDL_GetScancodeFromKey(SDLK_LCTRL);
|
||||
io.KeyMap[ImGuiKey_PageUp] = SDL_GetScancodeFromKey(SDLK_PAGEUP);
|
||||
io.KeyMap[ImGuiKey_PageDown] = SDL_GetScancodeFromKey(SDLK_PAGEDOWN);
|
||||
io.KeyMap[ImGuiKey_Home] = SDL_GetScancodeFromKey(SDLK_HOME);
|
||||
io.KeyMap[ImGuiKey_Space] = SDL_GetScancodeFromKey(SDLK_SPACE);
|
||||
io.KeyMap[ImGuiKey_1] = SDL_GetScancodeFromKey(SDLK_1);
|
||||
io.KeyMap[ImGuiKey_2] = SDL_GetScancodeFromKey(SDLK_2);
|
||||
io.KeyMap[ImGuiKey_3] = SDL_GetScancodeFromKey(SDLK_3);
|
||||
io.KeyMap[ImGuiKey_4] = SDL_GetScancodeFromKey(SDLK_4);
|
||||
io.KeyMap[ImGuiKey_5] = SDL_GetScancodeFromKey(SDLK_5);
|
||||
io.KeyMap[ImGuiKey_6] = SDL_GetScancodeFromKey(SDLK_6);
|
||||
io.KeyMap[ImGuiKey_7] = SDL_GetScancodeFromKey(SDLK_7);
|
||||
io.KeyMap[ImGuiKey_8] = SDL_GetScancodeFromKey(SDLK_8);
|
||||
io.KeyMap[ImGuiKey_9] = SDL_GetScancodeFromKey(SDLK_9);
|
||||
io.KeyMap[ImGuiKey_0] = SDL_GetScancodeFromKey(SDLK_0);
|
||||
io.KeyMap[ImGuiKey_A] = SDL_GetScancodeFromKey(SDLK_a);
|
||||
io.KeyMap[ImGuiKey_B] = SDL_GetScancodeFromKey(SDLK_b);
|
||||
io.KeyMap[ImGuiKey_C] = SDL_GetScancodeFromKey(SDLK_c);
|
||||
io.KeyMap[ImGuiKey_D] = SDL_GetScancodeFromKey(SDLK_d);
|
||||
io.KeyMap[ImGuiKey_E] = SDL_GetScancodeFromKey(SDLK_e);
|
||||
io.KeyMap[ImGuiKey_F] = SDL_GetScancodeFromKey(SDLK_f);
|
||||
io.KeyMap[ImGuiKey_G] = SDL_GetScancodeFromKey(SDLK_g);
|
||||
io.KeyMap[ImGuiKey_H] = SDL_GetScancodeFromKey(SDLK_h);
|
||||
io.KeyMap[ImGuiKey_I] = SDL_GetScancodeFromKey(SDLK_i);
|
||||
io.KeyMap[ImGuiKey_J] = SDL_GetScancodeFromKey(SDLK_j);
|
||||
io.KeyMap[ImGuiKey_K] = SDL_GetScancodeFromKey(SDLK_k);
|
||||
io.KeyMap[ImGuiKey_L] = SDL_GetScancodeFromKey(SDLK_l);
|
||||
io.KeyMap[ImGuiKey_M] = SDL_GetScancodeFromKey(SDLK_m);
|
||||
io.KeyMap[ImGuiKey_N] = SDL_GetScancodeFromKey(SDLK_n);
|
||||
io.KeyMap[ImGuiKey_O] = SDL_GetScancodeFromKey(SDLK_o);
|
||||
io.KeyMap[ImGuiKey_P] = SDL_GetScancodeFromKey(SDLK_p);
|
||||
io.KeyMap[ImGuiKey_Q] = SDL_GetScancodeFromKey(SDLK_q);
|
||||
io.KeyMap[ImGuiKey_R] = SDL_GetScancodeFromKey(SDLK_r);
|
||||
io.KeyMap[ImGuiKey_S] = SDL_GetScancodeFromKey(SDLK_s);
|
||||
io.KeyMap[ImGuiKey_T] = SDL_GetScancodeFromKey(SDLK_t);
|
||||
io.KeyMap[ImGuiKey_U] = SDL_GetScancodeFromKey(SDLK_u);
|
||||
io.KeyMap[ImGuiKey_V] = SDL_GetScancodeFromKey(SDLK_v);
|
||||
io.KeyMap[ImGuiKey_W] = SDL_GetScancodeFromKey(SDLK_w);
|
||||
io.KeyMap[ImGuiKey_X] = SDL_GetScancodeFromKey(SDLK_x);
|
||||
io.KeyMap[ImGuiKey_Y] = SDL_GetScancodeFromKey(SDLK_y);
|
||||
io.KeyMap[ImGuiKey_Z] = SDL_GetScancodeFromKey(SDLK_z);
|
||||
io.KeyMap[ImGuiKey_F1] = SDL_GetScancodeFromKey(SDLK_F1);
|
||||
io.KeyMap[ImGuiKey_F2] = SDL_GetScancodeFromKey(SDLK_F2);
|
||||
io.KeyMap[ImGuiKey_F3] = SDL_GetScancodeFromKey(SDLK_F3);
|
||||
io.KeyMap[ImGuiKey_F4] = SDL_GetScancodeFromKey(SDLK_F4);
|
||||
io.KeyMap[ImGuiKey_F5] = SDL_GetScancodeFromKey(SDLK_F5);
|
||||
io.KeyMap[ImGuiKey_F6] = SDL_GetScancodeFromKey(SDLK_F6);
|
||||
io.KeyMap[ImGuiKey_F7] = SDL_GetScancodeFromKey(SDLK_F7);
|
||||
io.KeyMap[ImGuiKey_F8] = SDL_GetScancodeFromKey(SDLK_F8);
|
||||
io.KeyMap[ImGuiKey_F9] = SDL_GetScancodeFromKey(SDLK_F9);
|
||||
io.KeyMap[ImGuiKey_F10] = SDL_GetScancodeFromKey(SDLK_F10);
|
||||
io.KeyMap[ImGuiKey_F11] = SDL_GetScancodeFromKey(SDLK_F11);
|
||||
io.KeyMap[ImGuiKey_F12] = SDL_GetScancodeFromKey(SDLK_F12);
|
||||
}
|
||||
|
||||
void ImGui_ImplSDL2_SetClipboardText(void *user_data, const char *text) {
|
||||
SDL_SetClipboardText(text);
|
||||
}
|
||||
|
||||
const char *ImGui_ImplSDL2_GetClipboardText(void *user_data) {
|
||||
return SDL_GetClipboardText();
|
||||
}
|
||||
|
||||
void InitializeClipboard() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;
|
||||
io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText;
|
||||
io.ClipboardUserData = nullptr;
|
||||
}
|
||||
|
||||
void HandleKeyDown(SDL_Event &event, editor::MasterEditor &editor) {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
io.KeysDown[event.key.keysym.scancode] = (event.type == SDL_KEYDOWN);
|
||||
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
|
||||
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
|
||||
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
|
||||
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
|
||||
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_BACKSPACE:
|
||||
case SDLK_LSHIFT:
|
||||
case SDLK_LCTRL:
|
||||
case SDLK_TAB:
|
||||
io.KeysDown[event.key.keysym.scancode] = (event.type == SDL_KEYDOWN);
|
||||
break;
|
||||
case SDLK_z:
|
||||
editor.emulator().snes().SetButtonState(1, 0, true);
|
||||
break;
|
||||
case SDLK_a:
|
||||
editor.emulator().snes().SetButtonState(1, 1, true);
|
||||
break;
|
||||
case SDLK_RSHIFT:
|
||||
editor.emulator().snes().SetButtonState(1, 2, true);
|
||||
break;
|
||||
case SDLK_RETURN:
|
||||
editor.emulator().snes().SetButtonState(1, 3, true);
|
||||
break;
|
||||
case SDLK_UP:
|
||||
editor.emulator().snes().SetButtonState(1, 4, true);
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
editor.emulator().snes().SetButtonState(1, 5, true);
|
||||
break;
|
||||
case SDLK_LEFT:
|
||||
editor.emulator().snes().SetButtonState(1, 6, true);
|
||||
break;
|
||||
case SDLK_RIGHT:
|
||||
editor.emulator().snes().SetButtonState(1, 7, true);
|
||||
break;
|
||||
case SDLK_x:
|
||||
editor.emulator().snes().SetButtonState(1, 8, true);
|
||||
break;
|
||||
case SDLK_s:
|
||||
editor.emulator().snes().SetButtonState(1, 9, true);
|
||||
break;
|
||||
case SDLK_d:
|
||||
editor.emulator().snes().SetButtonState(1, 10, true);
|
||||
break;
|
||||
case SDLK_c:
|
||||
editor.emulator().snes().SetButtonState(1, 11, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||
switch (event.type) {
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP: {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
|
||||
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
|
||||
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
|
||||
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
|
||||
break;
|
||||
}
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_CLOSE:
|
||||
active_ = false;
|
||||
break;
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
io.DisplaySize.x = static_cast<float>(event.window.data1);
|
||||
io.DisplaySize.y = static_cast<float>(event.window.data2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleKeyUp(SDL_Event &event, editor::MasterEditor &editor) {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
int key = event.key.keysym.scancode;
|
||||
IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown));
|
||||
io.KeysDown[key] = (event.type == SDL_KEYDOWN);
|
||||
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
|
||||
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
|
||||
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
|
||||
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
|
||||
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_z:
|
||||
editor.emulator().snes().SetButtonState(1, 0, false);
|
||||
break;
|
||||
case SDLK_a:
|
||||
editor.emulator().snes().SetButtonState(1, 1, false);
|
||||
break;
|
||||
case SDLK_RSHIFT:
|
||||
editor.emulator().snes().SetButtonState(1, 2, false);
|
||||
break;
|
||||
case SDLK_RETURN:
|
||||
editor.emulator().snes().SetButtonState(1, 3, false);
|
||||
break;
|
||||
case SDLK_UP:
|
||||
editor.emulator().snes().SetButtonState(1, 4, false);
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
editor.emulator().snes().SetButtonState(1, 5, false);
|
||||
break;
|
||||
case SDLK_LEFT:
|
||||
editor.emulator().snes().SetButtonState(1, 6, false);
|
||||
break;
|
||||
case SDLK_RIGHT:
|
||||
editor.emulator().snes().SetButtonState(1, 7, false);
|
||||
break;
|
||||
case SDLK_x:
|
||||
editor.emulator().snes().SetButtonState(1, 8, false);
|
||||
break;
|
||||
case SDLK_s:
|
||||
editor.emulator().snes().SetButtonState(1, 9, false);
|
||||
break;
|
||||
case SDLK_d:
|
||||
editor.emulator().snes().SetButtonState(1, 10, false);
|
||||
break;
|
||||
case SDLK_c:
|
||||
editor.emulator().snes().SetButtonState(1, 11, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeWindowSizeEvent(SDL_Event &event) {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
io.DisplaySize.x = static_cast<float>(event.window.data1);
|
||||
io.DisplaySize.y = static_cast<float>(event.window.data2);
|
||||
}
|
||||
|
||||
void HandleMouseMovement(int &wheel) {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
int mouseX;
|
||||
int mouseY;
|
||||
const int buttons = SDL_GetMouseState(&mouseX, &mouseY);
|
||||
@@ -272,169 +120,80 @@ void HandleMouseMovement(int &wheel) {
|
||||
io.MouseWheel = static_cast<float>(wheel);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
absl::Status Controller::OnEntry(std::string filename) {
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#if TARGET_IPHONE_SIMULATOR == 1
|
||||
/* iOS in Xcode simulator */
|
||||
platform_ = Platform::kiOS;
|
||||
#elif TARGET_OS_IPHONE == 1
|
||||
/* iOS */
|
||||
platform_ = Platform::kiOS;
|
||||
#elif TARGET_OS_MAC == 1
|
||||
/* macOS */
|
||||
platform_ = Platform::kMacOS;
|
||||
#endif
|
||||
#elif defined(_WIN32)
|
||||
platform_ = Platform::kWindows;
|
||||
#elif defined(__linux__)
|
||||
platform_ = Platform::kLinux;
|
||||
#else
|
||||
platform_ = Platform::kUnknown;
|
||||
#endif
|
||||
|
||||
RETURN_IF_ERROR(CreateSDL_Window())
|
||||
RETURN_IF_ERROR(CreateRenderer())
|
||||
RETURN_IF_ERROR(CreateGuiContext())
|
||||
if (flags()->kLoadAudioDevice) {
|
||||
RETURN_IF_ERROR(LoadAudioDevice())
|
||||
master_editor_.emulator().set_audio_buffer(audio_buffer_);
|
||||
master_editor_.emulator().set_audio_device_id(audio_device_);
|
||||
absl::Status Controller::OnLoad() {
|
||||
if (editor_manager_.quit()) {
|
||||
active_ = false;
|
||||
}
|
||||
InitializeKeymap();
|
||||
master_editor_.SetupScreen(renderer_, filename);
|
||||
active_ = true;
|
||||
#if TARGET_OS_IPHONE != 1
|
||||
if (platform_ != Platform::kiOS) {
|
||||
NewMasterFrame();
|
||||
}
|
||||
#endif
|
||||
RETURN_IF_ERROR(editor_manager_.Update());
|
||||
#if TARGET_OS_IPHONE != 1
|
||||
if (platform_ != Platform::kiOS) {
|
||||
End();
|
||||
}
|
||||
#endif
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void Controller::OnInput() {
|
||||
int wheel = 0;
|
||||
SDL_Event event;
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_KEYDOWN:
|
||||
HandleKeyDown(event, master_editor_);
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
HandleKeyUp(event, master_editor_);
|
||||
break;
|
||||
case SDL_TEXTINPUT:
|
||||
io.AddInputCharactersUTF8(event.text.text);
|
||||
break;
|
||||
case SDL_MOUSEWHEEL:
|
||||
wheel = event.wheel.y;
|
||||
break;
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_CLOSE:
|
||||
CloseWindow();
|
||||
break;
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
ChangeWindowSizeEvent(event);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
HandleMouseMovement(wheel);
|
||||
}
|
||||
|
||||
absl::Status Controller::OnLoad() {
|
||||
if (master_editor_.quit()) {
|
||||
active_ = false;
|
||||
}
|
||||
NewMasterFrame();
|
||||
RETURN_IF_ERROR(master_editor_.Update());
|
||||
absl::Status Controller::OnTestLoad() {
|
||||
RETURN_IF_ERROR(test_editor_->Update());
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void Controller::DoRender() const {
|
||||
ImGui::Render();
|
||||
SDL_RenderClear(renderer_.get());
|
||||
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), renderer_.get());
|
||||
SDL_RenderPresent(renderer_.get());
|
||||
SDL_RenderClear(Renderer::GetInstance().renderer());
|
||||
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(),
|
||||
Renderer::GetInstance().renderer());
|
||||
SDL_RenderPresent(Renderer::GetInstance().renderer());
|
||||
}
|
||||
|
||||
void Controller::OnExit() {
|
||||
ImGui::DestroyContext();
|
||||
if (flags()->kLoadAudioDevice) {
|
||||
SDL_PauseAudioDevice(audio_device_, 1);
|
||||
SDL_CloseAudioDevice(audio_device_);
|
||||
delete audio_buffer_;
|
||||
}
|
||||
switch (platform_) {
|
||||
case Platform::kMacOS:
|
||||
case Platform::kWindows:
|
||||
case Platform::kLinux:
|
||||
ImGui_ImplSDLRenderer2_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
break;
|
||||
case Platform::kiOS:
|
||||
// Deferred
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
SDL_PauseAudioDevice(audio_device_, 1);
|
||||
SDL_CloseAudioDevice(audio_device_);
|
||||
ImGui_ImplSDLRenderer2_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
absl::Status Controller::CreateSDL_Window() {
|
||||
auto sdl_flags = SDL_INIT_VIDEO | SDL_INIT_TIMER;
|
||||
absl::Status Controller::CreateWindow() {
|
||||
auto sdl_flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
|
||||
if (flags()->kUseNewImGuiInput) {
|
||||
sdl_flags |= SDL_INIT_GAMECONTROLLER;
|
||||
}
|
||||
|
||||
if (flags()->kLoadAudioDevice) {
|
||||
sdl_flags |= SDL_INIT_AUDIO;
|
||||
}
|
||||
|
||||
if (SDL_Init(sdl_flags) != 0) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("SDL_Init: %s\n", SDL_GetError()));
|
||||
} else {
|
||||
SDL_DisplayMode displayMode;
|
||||
SDL_GetCurrentDisplayMode(0, &displayMode);
|
||||
int screenWidth = displayMode.w * 0.8;
|
||||
int screenHeight = displayMode.h * 0.8;
|
||||
|
||||
window_ = std::unique_ptr<SDL_Window, sdl_deleter>(
|
||||
SDL_CreateWindow("Yet Another Zelda3 Editor", // window title
|
||||
SDL_WINDOWPOS_UNDEFINED, // initial x position
|
||||
SDL_WINDOWPOS_UNDEFINED, // initial y position
|
||||
screenWidth, // width, in pixels
|
||||
screenHeight, // height, in pixels
|
||||
SDL_WINDOW_RESIZABLE),
|
||||
sdl_deleter());
|
||||
if (window_ == nullptr) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("SDL_CreateWindow: %s\n", SDL_GetError()));
|
||||
}
|
||||
}
|
||||
|
||||
SDL_DisplayMode display_mode;
|
||||
SDL_GetCurrentDisplayMode(0, &display_mode);
|
||||
int screen_width = display_mode.w * 0.8;
|
||||
int screen_height = display_mode.h * 0.8;
|
||||
|
||||
window_ = std::unique_ptr<SDL_Window, core::SDL_Deleter>(
|
||||
SDL_CreateWindow("Yet Another Zelda3 Editor", // window title
|
||||
SDL_WINDOWPOS_UNDEFINED, // initial x position
|
||||
SDL_WINDOWPOS_UNDEFINED, // initial y position
|
||||
screen_width, // width, in pixels
|
||||
screen_height, // height, in pixels
|
||||
SDL_WINDOW_RESIZABLE),
|
||||
core::SDL_Deleter());
|
||||
if (window_ == nullptr) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("SDL_CreateWindow: %s\n", SDL_GetError()));
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Controller::CreateRenderer() {
|
||||
renderer_ = std::unique_ptr<SDL_Renderer, sdl_deleter>(
|
||||
SDL_CreateRenderer(window_.get(), -1,
|
||||
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC),
|
||||
sdl_deleter());
|
||||
if (renderer_ == nullptr) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("SDL_CreateRenderer: %s\n", SDL_GetError()));
|
||||
} else {
|
||||
SDL_SetRenderDrawBlendMode(renderer_.get(), SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(renderer_.get(), 0x00, 0x00, 0x00, 0x00);
|
||||
}
|
||||
return absl::OkStatus();
|
||||
return Renderer::GetInstance().CreateRenderer(window_.get());
|
||||
}
|
||||
|
||||
absl::Status Controller::CreateGuiContext() {
|
||||
@@ -443,20 +202,16 @@ absl::Status Controller::CreateGuiContext() {
|
||||
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
|
||||
if (flags()->kUseNewImGuiInput) {
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
||||
}
|
||||
|
||||
// Initialize ImGui for SDL
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(window_.get(), renderer_.get());
|
||||
ImGui_ImplSDLRenderer2_Init(renderer_.get());
|
||||
// Initialize ImGui based on the backend
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(window_.get(),
|
||||
Renderer::GetInstance().renderer());
|
||||
ImGui_ImplSDLRenderer2_Init(Renderer::GetInstance().renderer());
|
||||
|
||||
if (flags()->kLoadSystemFonts) {
|
||||
LoadSystemFonts();
|
||||
} else {
|
||||
RETURN_IF_ERROR(LoadFontFamilies());
|
||||
}
|
||||
RETURN_IF_ERROR(LoadFontFamilies());
|
||||
|
||||
// Set the default style
|
||||
gui::ColorsYaze();
|
||||
@@ -469,94 +224,8 @@ absl::Status Controller::CreateGuiContext() {
|
||||
}
|
||||
|
||||
absl::Status Controller::LoadFontFamilies() const {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
|
||||
const char *font_path = "assets/font/";
|
||||
static const char *KARLA_REGULAR = "Karla-Regular.ttf";
|
||||
static const char *ROBOTO_MEDIUM = "Roboto-Medium.ttf";
|
||||
static const char *COUSINE_REGULAR = "Cousine-Regular.ttf";
|
||||
static const char *DROID_SANS = "DroidSans.ttf";
|
||||
static const char *NOTO_SANS_JP = "NotoSansJP.ttf";
|
||||
static const char *IBM_PLEX_JP = "IBMPlexSansJP-Bold.ttf";
|
||||
static const float FONT_SIZE_DEFAULT = 14.0f;
|
||||
static const float FONT_SIZE_DROID_SANS = 16.0f;
|
||||
static const float ICON_FONT_SIZE = 18.0f;
|
||||
|
||||
// Icon configuration
|
||||
static const ImWchar icons_ranges[] = {ICON_MIN_MD, 0xf900, 0};
|
||||
ImFontConfig icons_config;
|
||||
icons_config.MergeMode = true;
|
||||
icons_config.GlyphOffset.y = 5.0f;
|
||||
icons_config.GlyphMinAdvanceX = 13.0f;
|
||||
icons_config.PixelSnapH = true;
|
||||
|
||||
// Japanese font configuration
|
||||
ImFontConfig japanese_font_config;
|
||||
japanese_font_config.MergeMode = true;
|
||||
icons_config.GlyphOffset.y = 5.0f;
|
||||
icons_config.GlyphMinAdvanceX = 13.0f;
|
||||
icons_config.PixelSnapH = true;
|
||||
|
||||
// List of fonts to be loaded
|
||||
std::vector<const char *> font_paths = {KARLA_REGULAR, ROBOTO_MEDIUM,
|
||||
COUSINE_REGULAR, IBM_PLEX_JP};
|
||||
|
||||
// Load fonts with associated icon and Japanese merges
|
||||
for (const auto &font_path : font_paths) {
|
||||
float font_size =
|
||||
(font_path == DROID_SANS) ? FONT_SIZE_DROID_SANS : FONT_SIZE_DEFAULT;
|
||||
|
||||
std::string actual_font_path;
|
||||
#ifdef __APPLE__
|
||||
#if TARGET_OS_IOS == 1
|
||||
const std::string kBundlePath = GetBundleResourcePath();
|
||||
actual_font_path = kBundlePath + font_path;
|
||||
#else
|
||||
actual_font_path = std::filesystem::absolute(font_path).string();
|
||||
#endif
|
||||
#else
|
||||
actual_font_path = font_path;
|
||||
#endif
|
||||
|
||||
if (!io.Fonts->AddFontFromFileTTF(actual_font_path.data(), font_size)) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Failed to load font from %s", actual_font_path));
|
||||
}
|
||||
|
||||
// Merge icon set
|
||||
std::string actual_icon_font_path = "";
|
||||
const char *icon_font_path = FONT_ICON_FILE_NAME_MD;
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#if TARGET_OS_IOS == 1
|
||||
const std::string kIconBundlePath = GetBundleResourcePath();
|
||||
actual_icon_font_path = kIconBundlePath + "MaterialIcons-Regular.ttf";
|
||||
#else
|
||||
actual_icon_font_path = std::filesystem::absolute(icon_font_path).string();
|
||||
#endif
|
||||
#else
|
||||
#endif
|
||||
io.Fonts->AddFontFromFileTTF(actual_icon_font_path.data(), ICON_FONT_SIZE,
|
||||
&icons_config, icons_ranges);
|
||||
|
||||
// Merge Japanese font
|
||||
std::string actual_japanese_font_path = "";
|
||||
const char *japanese_font_path = NOTO_SANS_JP;
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#if TARGET_OS_IOS == 1
|
||||
const std::string kJapaneseBundlePath = GetBundleResourcePath();
|
||||
actual_japanese_font_path = kJapaneseBundlePath + japanese_font_path;
|
||||
#else
|
||||
actual_japanese_font_path =
|
||||
std::filesystem::absolute(japanese_font_path).string();
|
||||
#endif
|
||||
#else
|
||||
#endif
|
||||
io.Fonts->AddFontFromFileTTF(actual_japanese_font_path.data(), 18.0f,
|
||||
&japanese_font_config,
|
||||
io.Fonts->GetGlyphRangesJapanese());
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
// LoadSystemFonts();
|
||||
return LoadPackageFonts();
|
||||
}
|
||||
|
||||
absl::Status Controller::LoadAudioDevice() {
|
||||
@@ -572,9 +241,39 @@ absl::Status Controller::LoadAudioDevice() {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Failed to open audio: %s\n", SDL_GetError()));
|
||||
}
|
||||
audio_buffer_ = new int16_t[audio_frequency_ / 50 * 4];
|
||||
master_editor_.emulator().set_audio_buffer(audio_buffer_);
|
||||
// audio_buffer_ = new int16_t[audio_frequency_ / 50 * 4];
|
||||
audio_buffer_ = std::make_shared<int16_t>(audio_frequency_ / 50 * 4);
|
||||
SDL_PauseAudioDevice(audio_device_, 0);
|
||||
editor_manager_.emulator().set_audio_buffer(audio_buffer_.get());
|
||||
editor_manager_.emulator().set_audio_device_id(audio_device_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Controller::LoadConfigFiles() {
|
||||
// Create and load a dotfile for the application
|
||||
// This will store the user's preferences and settings
|
||||
std::string config_directory = GetConfigDirectory(platform_);
|
||||
|
||||
// Create the directory if it doesn't exist
|
||||
if (!std::filesystem::exists(config_directory)) {
|
||||
if (!std::filesystem::create_directory(config_directory)) {
|
||||
return absl::InternalError(absl::StrFormat(
|
||||
"Failed to create config directory %s", config_directory));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the config file exists
|
||||
std::string config_file = config_directory + "yaze.cfg";
|
||||
if (!std::filesystem::exists(config_file)) {
|
||||
// Create the file if it doesn't exist
|
||||
std::ofstream file(config_file);
|
||||
if (!file.is_open()) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Failed to create config file %s", config_file));
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,20 +2,18 @@
|
||||
#define YAZE_APP_CORE_CONTROLLER_H
|
||||
|
||||
#include <SDL.h>
|
||||
#include "imgui/backends/imgui_impl_sdl2.h"
|
||||
#include "imgui/backends/imgui_impl_sdlrenderer2.h"
|
||||
#include "imgui/imconfig.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/imgui_internal.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/common.h"
|
||||
#include "app/editor/master_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/core/platform/renderer.h"
|
||||
#include "app/core/utils/file_util.h"
|
||||
#include "app/editor/editor_manager.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "imgui/backends/imgui_impl_sdl2.h"
|
||||
#include "imgui/backends/imgui_impl_sdlrenderer2.h"
|
||||
#include "imgui/imconfig.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
int main(int argc, char **argv);
|
||||
|
||||
@@ -23,8 +21,6 @@ namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
enum class Platform { kUnknown, kMacOS, kiOS, kWindows, kLinux };
|
||||
|
||||
/**
|
||||
* @brief Main controller for the application.
|
||||
*
|
||||
@@ -37,45 +33,40 @@ class Controller : public ExperimentFlags {
|
||||
absl::Status OnEntry(std::string filename = "");
|
||||
void OnInput();
|
||||
absl::Status OnLoad();
|
||||
absl::Status OnTestLoad();
|
||||
void DoRender() const;
|
||||
void OnExit();
|
||||
|
||||
absl::Status CreateSDL_Window();
|
||||
absl::Status CreateWindow();
|
||||
absl::Status CreateRenderer();
|
||||
absl::Status CreateGuiContext();
|
||||
absl::Status LoadFontFamilies() const;
|
||||
absl::Status LoadAudioDevice();
|
||||
absl::Status LoadConfigFiles();
|
||||
|
||||
auto master_editor() -> editor::MasterEditor & { return master_editor_; }
|
||||
void SetupScreen(std::string filename = "") {
|
||||
editor_manager_.SetupScreen(filename);
|
||||
}
|
||||
auto editor_manager() -> editor::EditorManager & { return editor_manager_; }
|
||||
auto renderer() -> SDL_Renderer * {
|
||||
return Renderer::GetInstance().renderer();
|
||||
}
|
||||
auto window() -> SDL_Window * { return window_.get(); }
|
||||
void init_test_editor(editor::Editor *editor) { test_editor_ = editor; }
|
||||
void set_active(bool active) { active_ = active; }
|
||||
|
||||
private:
|
||||
struct sdl_deleter {
|
||||
void operator()(SDL_Window *p) const {
|
||||
if (p) {
|
||||
SDL_DestroyWindow(p);
|
||||
}
|
||||
}
|
||||
void operator()(SDL_Renderer *p) const {
|
||||
if (p) {
|
||||
SDL_DestroyRenderer(p);
|
||||
}
|
||||
}
|
||||
void operator()(SDL_Texture *p) const { SDL_DestroyTexture(p); }
|
||||
};
|
||||
|
||||
void CloseWindow() { active_ = false; }
|
||||
|
||||
friend int ::main(int argc, char **argv);
|
||||
|
||||
bool active_;
|
||||
Platform platform_;
|
||||
editor::MasterEditor master_editor_;
|
||||
editor::Editor *test_editor_;
|
||||
editor::EditorManager editor_manager_;
|
||||
|
||||
int audio_frequency_ = 48000;
|
||||
int16_t *audio_buffer_;
|
||||
SDL_AudioDeviceID audio_device_;
|
||||
std::shared_ptr<int16_t> audio_buffer_;
|
||||
std::shared_ptr<SDL_Window> window_;
|
||||
std::shared_ptr<SDL_Renderer> renderer_;
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
|
||||
33
src/app/core/core.cmake
Normal file
33
src/app/core/core.cmake
Normal file
@@ -0,0 +1,33 @@
|
||||
set(
|
||||
YAZE_APP_CORE_SRC
|
||||
app/core/common.cc
|
||||
app/core/controller.cc
|
||||
app/emu/emulator.cc
|
||||
app/core/project.cc
|
||||
app/core/utils/file_util.cc
|
||||
)
|
||||
|
||||
if (WIN32 OR MINGW OR UNIX AND NOT APPLE)
|
||||
list(APPEND YAZE_APP_CORE_SRC
|
||||
app/core/platform/font_loader.cc
|
||||
app/core/platform/clipboard.cc
|
||||
app/core/platform/file_dialog.cc
|
||||
)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND YAZE_APP_CORE_SRC
|
||||
app/core/platform/file_dialog.mm
|
||||
app/core/platform/app_delegate.mm
|
||||
app/core/platform/font_loader.cc
|
||||
app/core/platform/font_loader.mm
|
||||
app/core/platform/clipboard.mm
|
||||
app/core/platform/file_path.mm
|
||||
)
|
||||
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
if(NOT COCOA_LIBRARY)
|
||||
message(FATAL_ERROR "Cocoa not found")
|
||||
endif()
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-framework ServiceManagement -framework Foundation -framework Cocoa")
|
||||
endif()
|
||||
@@ -1,53 +0,0 @@
|
||||
#ifndef YAZE_APP_CORE_LABELING_H_
|
||||
#define YAZE_APP_CORE_LABELING_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/core/common.h"
|
||||
#include "app/core/constants.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
// Default types
|
||||
static constexpr absl::string_view kDefaultTypes[] = {
|
||||
"Dungeon Names", "Dungeon Room Names", "Overworld Map Names"};
|
||||
|
||||
struct ResourceLabelManager {
|
||||
bool LoadLabels(const std::string& filename);
|
||||
bool SaveLabels();
|
||||
void DisplayLabels(bool* p_open);
|
||||
void EditLabel(const std::string& type, const std::string& key,
|
||||
const std::string& newValue);
|
||||
void SelectableLabelWithNameEdit(bool selected, const std::string& type,
|
||||
const std::string& key,
|
||||
const std::string& defaultValue);
|
||||
std::string CreateOrGetLabel(const std::string& type, const std::string& key,
|
||||
const std::string& defaultValue);
|
||||
std::string CreateOrGetLabel(const std::string& type, const std::string& key,
|
||||
const absl::string_view& defaultValue);
|
||||
|
||||
bool labels_loaded_ = false;
|
||||
std::string filename_;
|
||||
struct ResourceType {
|
||||
std::string key_name;
|
||||
std::string display_description;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, std::string>>
|
||||
labels_;
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_CORE_LABELING_H_
|
||||
@@ -1,13 +1,57 @@
|
||||
#ifndef YAZE_APP_CORE_PLATFORM_APP_DELEGATE_H
|
||||
#define YAZE_APP_CORE_PLATFORM_APP_DELEGATE_H
|
||||
|
||||
#ifdef TARGET_OS_MAC
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
/* Apple OSX and iOS (Darwin). */
|
||||
#import <CoreText/CoreText.h>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1
|
||||
/* iOS in Xcode simulator */
|
||||
#import <PencilKit/PencilKit.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate,
|
||||
UIDocumentPickerDelegate,
|
||||
UITabBarControllerDelegate,
|
||||
PKCanvasViewDelegate>
|
||||
@property(strong, nonatomic) UIWindow *window;
|
||||
|
||||
@property UIDocumentPickerViewController *documentPicker;
|
||||
@property(nonatomic, copy) void (^completionHandler)(NSString *selectedFile);
|
||||
- (void)PresentDocumentPickerWithCompletionHandler:
|
||||
(void (^)(NSString *selectedFile))completionHandler;
|
||||
|
||||
// TODO: Setup a tab bar controller for multiple yaze instances
|
||||
@property(nonatomic) UITabBarController *tabBarController;
|
||||
|
||||
// TODO: Setup a font picker for the text editor and display settings
|
||||
@property(nonatomic) UIFontPickerViewController *fontPicker;
|
||||
|
||||
// TODO: Setup the pencil kit for drawing
|
||||
@property PKToolPicker *toolPicker;
|
||||
@property PKCanvasView *canvasView;
|
||||
|
||||
// TODO: Setup the file manager for file operations
|
||||
@property NSFileManager *fileManager;
|
||||
|
||||
@end
|
||||
|
||||
#elif TARGET_OS_MAC == 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void InitializeCocoa();
|
||||
/**
|
||||
* @brief Initialize the Cocoa application.
|
||||
*/
|
||||
void yaze_initialize_cocoa();
|
||||
|
||||
/**
|
||||
* @brief Run the Cocoa application delegate.
|
||||
*/
|
||||
void yaze_run_cocoa_app_delegate(const char *filename);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
@@ -15,4 +59,6 @@ void InitializeCocoa();
|
||||
|
||||
#endif // TARGET_OS_MAC
|
||||
|
||||
#endif // defined(__APPLE__) && defined(__MACH__)
|
||||
|
||||
#endif // YAZE_APP_CORE_PLATFORM_APP_DELEGATE_H
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#import "app/core/platform/app_delegate.h"
|
||||
#import "app/core/controller.h"
|
||||
#import "app/core/platform/file_dialog.h"
|
||||
#import "app/editor/utils/editor.h"
|
||||
#import "app/editor/editor.h"
|
||||
#import "app/rom.h"
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
@@ -11,12 +11,9 @@
|
||||
|
||||
#import <CoreText/CoreText.h>
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR == 1
|
||||
#if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1
|
||||
/* iOS in Xcode simulator */
|
||||
|
||||
#elif TARGET_OS_IPHONE == 1
|
||||
/* iOS */
|
||||
|
||||
#elif TARGET_OS_MAC == 1
|
||||
/* macOS */
|
||||
#import <Cocoa/Cocoa.h>
|
||||
@@ -209,7 +206,8 @@
|
||||
}
|
||||
|
||||
- (void)openFileAction:(id)sender {
|
||||
if (!yaze::app::SharedRom::shared_rom_->LoadFromFile(FileDialogWrapper::ShowOpenFileDialog())
|
||||
if (!yaze::app::SharedRom::shared_rom_
|
||||
->LoadFromFile(yaze::app::core::FileDialogWrapper::ShowOpenFileDialog())
|
||||
.ok()) {
|
||||
NSAlert *alert = [[NSAlert alloc] init];
|
||||
[alert setMessageText:@"Error"];
|
||||
@@ -227,7 +225,9 @@
|
||||
NSLog(@"Open Recent File action triggered");
|
||||
}
|
||||
|
||||
extern "C" void InitializeCocoa() {
|
||||
@end
|
||||
|
||||
extern "C" void yaze_initialize_cococa() {
|
||||
@autoreleasepool {
|
||||
AppDelegate *delegate = [[AppDelegate alloc] init];
|
||||
[NSApplication sharedApplication];
|
||||
@@ -236,7 +236,21 @@ extern "C" void InitializeCocoa() {
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
extern "C" void yaze_run_cocoa_app_delegate(const char *filename) {
|
||||
yaze_initialize_cococa();
|
||||
yaze::app::core::Controller controller;
|
||||
RETURN_VOID_IF_ERROR(controller.OnEntry(filename));
|
||||
while (controller.IsActive()) {
|
||||
@autoreleasepool {
|
||||
controller.OnInput();
|
||||
if (auto status = controller.OnLoad(); !status.ok()) {
|
||||
break;
|
||||
}
|
||||
controller.DoRender();
|
||||
}
|
||||
}
|
||||
controller.OnExit();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
void CopyImageToClipboard(const std::vector<uint8_t>& data) {}
|
||||
void GetImageFromClipboard(std::vector<uint8_t>& data, int& width,
|
||||
int& height) {}
|
||||
int& height) {}
|
||||
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -1,27 +1,18 @@
|
||||
#ifndef YAZE_APP_CORE_PLATFORM_CLIPBOARD_H
|
||||
#define YAZE_APP_CORE_PLATFORM_CLIPBOARD_H
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
void CopyImageToClipboard(const std::vector<uint8_t>& data);
|
||||
void GetImageFromClipboard(std::vector<uint8_t>& data, int& width, int& height);
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
void CopyImageToClipboard(const std::vector<uint8_t>& data);
|
||||
void GetImageFromClipboard(std::vector<uint8_t>& data, int& width, int& height);
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
#elif defined(__linux__)
|
||||
void CopyImageToClipboard(const std::vector<uint8_t> &data);
|
||||
void GetImageFromClipboard(std::vector<uint8_t> &data, int &width, int &height);
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
void CopyImageToClipboard(const std::vector<uint8_t>& data);
|
||||
void GetImageFromClipboard(std::vector<uint8_t>& data, int& width, int& height);
|
||||
|
||||
#endif
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_CORE_PLATFORM_CLIPBOARD_H
|
||||
@@ -6,7 +6,7 @@
|
||||
#ifdef TARGET_OS_MAC
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
void CopyImageToClipboard(const std::vector<uint8_t>& pngData) {
|
||||
void yaze::app::core::CopyImageToClipboard(const std::vector<uint8_t>& pngData) {
|
||||
NSData* data = [NSData dataWithBytes:pngData.data() length:pngData.size()];
|
||||
NSImage* image = [[NSImage alloc] initWithData:data];
|
||||
|
||||
@@ -15,7 +15,7 @@ void CopyImageToClipboard(const std::vector<uint8_t>& pngData) {
|
||||
[pasteboard writeObjects:@[ image ]];
|
||||
}
|
||||
|
||||
void GetImageFromClipboard(std::vector<uint8_t>& pixel_data, int& width, int& height) {
|
||||
void yaze::app::core::GetImageFromClipboard(std::vector<uint8_t>& pixel_data, int& width, int& height) {
|
||||
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
|
||||
NSArray* classArray = [NSArray arrayWithObject:[NSImage class]];
|
||||
NSDictionary* options = [NSDictionary dictionary];
|
||||
|
||||
145
src/app/core/platform/file_dialog.cc
Normal file
145
src/app/core/platform/file_dialog.cc
Normal file
@@ -0,0 +1,145 @@
|
||||
#include "file_dialog.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
// Include Windows-specific headers
|
||||
#include <shobjidl.h>
|
||||
#include <windows.h>
|
||||
#endif // _WIN32
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
std::string FileDialogWrapper::ShowOpenFileDialog() {
|
||||
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
||||
|
||||
IFileDialog *pfd = NULL;
|
||||
HRESULT hr =
|
||||
CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, IID_IFileDialog,
|
||||
reinterpret_cast<void **>(&pfd));
|
||||
std::string file_path_windows;
|
||||
if (SUCCEEDED(hr)) {
|
||||
// Show the dialog
|
||||
hr = pfd->Show(NULL);
|
||||
if (SUCCEEDED(hr)) {
|
||||
IShellItem *psiResult;
|
||||
hr = pfd->GetResult(&psiResult);
|
||||
if (SUCCEEDED(hr)) {
|
||||
// Get the file path
|
||||
PWSTR pszFilePath;
|
||||
psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
|
||||
char str[128];
|
||||
wcstombs(str, pszFilePath, 128);
|
||||
file_path_windows = str;
|
||||
psiResult->Release();
|
||||
CoTaskMemFree(pszFilePath);
|
||||
}
|
||||
}
|
||||
pfd->Release();
|
||||
}
|
||||
|
||||
CoUninitialize();
|
||||
return file_path_windows;
|
||||
}
|
||||
|
||||
std::string FileDialogWrapper::ShowOpenFolderDialog() {
|
||||
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
||||
|
||||
IFileDialog *pfd = NULL;
|
||||
HRESULT hr =
|
||||
CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, IID_IFileDialog,
|
||||
reinterpret_cast<void **>(&pfd));
|
||||
std::string folder_path_windows;
|
||||
if (SUCCEEDED(hr)) {
|
||||
// Show the dialog
|
||||
DWORD dwOptions;
|
||||
hr = pfd->GetOptions(&dwOptions);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = pfd->SetOptions(dwOptions | FOS_PICKFOLDERS);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = pfd->Show(NULL);
|
||||
if (SUCCEEDED(hr)) {
|
||||
IShellItem *psiResult;
|
||||
hr = pfd->GetResult(&psiResult);
|
||||
if (SUCCEEDED(hr)) {
|
||||
// Get the folder path
|
||||
PWSTR pszFolderPath;
|
||||
psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszFolderPath);
|
||||
char str[128];
|
||||
wcstombs(str, pszFolderPath, 128);
|
||||
folder_path_windows = str;
|
||||
psiResult->Release();
|
||||
CoTaskMemFree(pszFolderPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pfd->Release();
|
||||
}
|
||||
|
||||
CoUninitialize();
|
||||
return folder_path_windows;
|
||||
}
|
||||
|
||||
std::vector<std::string> FileDialogWrapper::GetSubdirectoriesInFolder(
|
||||
const std::string &folder_path) {
|
||||
std::vector<std::string> subdirectories;
|
||||
WIN32_FIND_DATA findFileData;
|
||||
HANDLE hFind = FindFirstFile((folder_path + "\\*").c_str(), &findFileData);
|
||||
if (hFind != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if (strcmp(findFileData.cFileName, ".") != 0 &&
|
||||
strcmp(findFileData.cFileName, "..") != 0) {
|
||||
subdirectories.push_back(findFileData.cFileName);
|
||||
}
|
||||
}
|
||||
} while (FindNextFile(hFind, &findFileData) != 0);
|
||||
FindClose(hFind);
|
||||
}
|
||||
return subdirectories;
|
||||
}
|
||||
|
||||
std::vector<std::string> FileDialogWrapper::GetFilesInFolder(
|
||||
const std::string &folder_path) {
|
||||
std::vector<std::string> files;
|
||||
WIN32_FIND_DATA findFileData;
|
||||
HANDLE hFind = FindFirstFile((folder_path + "\\*").c_str(), &findFileData);
|
||||
if (hFind != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if (!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
files.push_back(findFileData.cFileName);
|
||||
}
|
||||
} while (FindNextFile(hFind, &findFileData) != 0);
|
||||
FindClose(hFind);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
#elif defined(__linux__)
|
||||
|
||||
std::string FileDialogWrapper::ShowOpenFileDialog() {
|
||||
return "Linux: Open file dialog";
|
||||
}
|
||||
|
||||
std::string FileDialogWrapper::ShowOpenFolderDialog() {
|
||||
return "Linux: Open folder dialog";
|
||||
}
|
||||
|
||||
std::vector<std::string> FileDialogWrapper::GetSubdirectoriesInFolder(
|
||||
const std::string& folder_path) {
|
||||
return {"Linux: Subdirectories in folder"};
|
||||
}
|
||||
|
||||
std::vector<std::string> FileDialogWrapper::GetFilesInFolder(
|
||||
const std::string& folder_path) {
|
||||
return {"Linux: Files in folder"};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -1,61 +1,25 @@
|
||||
#ifndef YAZE_APP_CORE_PLATFORM_FILE_DIALOG_H
|
||||
#define YAZE_APP_CORE_PLATFORM_FILE_DIALOG_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
// Include Windows-specific headers
|
||||
#include <shobjidl.h>
|
||||
#include <windows.h>
|
||||
|
||||
class FileDialogWrapper {
|
||||
public:
|
||||
static std::string ShowOpenFileDialog() {
|
||||
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
||||
|
||||
IFileDialog *pfd = NULL;
|
||||
HRESULT hr =
|
||||
CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
|
||||
IID_IFileDialog, reinterpret_cast<void **>(&pfd));
|
||||
std::string file_path_windows;
|
||||
if (SUCCEEDED(hr)) {
|
||||
// Show the dialog
|
||||
hr = pfd->Show(NULL);
|
||||
if (SUCCEEDED(hr)) {
|
||||
IShellItem *psiResult;
|
||||
hr = pfd->GetResult(&psiResult);
|
||||
if (SUCCEEDED(hr)) {
|
||||
// Get the file path
|
||||
PWSTR pszFilePath;
|
||||
psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
|
||||
char str[128];
|
||||
wcstombs(str, pszFilePath, 128);
|
||||
file_path_windows = str;
|
||||
psiResult->Release();
|
||||
CoTaskMemFree(pszFilePath);
|
||||
}
|
||||
}
|
||||
pfd->Release();
|
||||
}
|
||||
|
||||
CoUninitialize();
|
||||
return file_path_windows;
|
||||
}
|
||||
};
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
#include "TargetConditionals.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef TARGET_OS_MAC
|
||||
// Other kinds of Mac OS
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
class FileDialogWrapper {
|
||||
public:
|
||||
/**
|
||||
* @brief ShowOpenFileDialog opens a file dialog and returns the selected
|
||||
* filepath.
|
||||
*/
|
||||
static std::string ShowOpenFileDialog();
|
||||
|
||||
/**
|
||||
* @brief ShowOpenFolderDialog opens a file dialog and returns the selected
|
||||
* folder path.
|
||||
*/
|
||||
static std::string ShowOpenFolderDialog();
|
||||
static std::vector<std::string> GetSubdirectoriesInFolder(
|
||||
const std::string& folder_path);
|
||||
@@ -63,34 +27,8 @@ class FileDialogWrapper {
|
||||
const std::string& folder_path);
|
||||
};
|
||||
|
||||
#elif TARGET_OS_IPHONE
|
||||
|
||||
// iOS
|
||||
class FileDialogWrapper {
|
||||
public:
|
||||
static std::string ShowOpenFileDialog();
|
||||
static std::string ShowOpenFolderDialog();
|
||||
static std::vector<std::string> GetSubdirectoriesInFolder(
|
||||
const std::string& folder_path);
|
||||
static std::vector<std::string> GetFilesInFolder(
|
||||
const std::string& folder_path);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#elif defined(__linux__)
|
||||
|
||||
class FileDialogWrapper {
|
||||
public:
|
||||
static std::string ShowOpenFileDialog() {
|
||||
// Linux-specific file dialog implementation using GTK
|
||||
// ...
|
||||
return "file_path_linux";
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
#error "Unsupported platform."
|
||||
#endif
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_CORE_PLATFORM_FILE_DIALOG_H
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "app/core/platform/file_dialog.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "app/core/platform/file_dialog.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
/* Apple OSX and iOS (Darwin). */
|
||||
@@ -10,32 +10,48 @@
|
||||
|
||||
#import <CoreText/CoreText.h>
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR == 1
|
||||
#if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1
|
||||
/* iOS in Xcode simulator */
|
||||
std::string FileDialogWrapper::ShowOpenFileDialog() { return ""; }
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||
|
||||
std::string FileDialogWrapper::ShowOpenFolderDialog() { return ""; }
|
||||
#include "app/core/platform/app_delegate.h"
|
||||
|
||||
std::vector<std::string> FileDialogWrapper::GetFilesInFolder(const std::string& folder) {
|
||||
namespace {
|
||||
static std::string selectedFile;
|
||||
|
||||
void ShowOpenFileDialogImpl(void (^completionHandler)(std::string)) {
|
||||
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
|
||||
[appDelegate PresentDocumentPickerWithCompletionHandler:^(NSString *filePath) {
|
||||
selectedFile = std::string([filePath UTF8String]);
|
||||
completionHandler(selectedFile);
|
||||
}];
|
||||
}
|
||||
|
||||
std::string ShowOpenFileDialogSync() {
|
||||
__block std::string result;
|
||||
|
||||
ShowOpenFileDialogImpl(^(std::string filePath) {
|
||||
result = filePath;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
std::string yaze::app::core::FileDialogWrapper::ShowOpenFileDialog() {
|
||||
return ShowOpenFileDialogSync();
|
||||
}
|
||||
|
||||
std::string yaze::app::core::FileDialogWrapper::ShowOpenFolderDialog() { return ""; }
|
||||
|
||||
std::vector<std::string> yaze::app::core::FileDialogWrapper::GetFilesInFolder(
|
||||
const std::string &folder) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> FileDialogWrapper::GetSubdirectoriesInFolder(const std::string& folder) {
|
||||
return {};
|
||||
}
|
||||
|
||||
#elif TARGET_OS_IPHONE == 1
|
||||
/* iOS */
|
||||
|
||||
std::string FileDialogWrapper::ShowOpenFileDialog() { return ""; }
|
||||
|
||||
std::string FileDialogWrapper::ShowOpenFolderDialog() { return ""; }
|
||||
|
||||
std::vector<std::string> FileDialogWrapper::GetFilesInFolder(const std::string& folder) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> FileDialogWrapper::GetSubdirectoriesInFolder(const std::string& folder) {
|
||||
std::vector<std::string> yaze::app::core::FileDialogWrapper::GetSubdirectoriesInFolder(
|
||||
const std::string &folder) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -44,7 +60,7 @@ std::vector<std::string> FileDialogWrapper::GetSubdirectoriesInFolder(const std:
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
std::string FileDialogWrapper::ShowOpenFileDialog() {
|
||||
std::string yaze::app::core::FileDialogWrapper::ShowOpenFileDialog() {
|
||||
NSOpenPanel* openPanel = [NSOpenPanel openPanel];
|
||||
[openPanel setCanChooseFiles:YES];
|
||||
[openPanel setCanChooseDirectories:NO];
|
||||
@@ -59,7 +75,7 @@ std::string FileDialogWrapper::ShowOpenFileDialog() {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string FileDialogWrapper::ShowOpenFolderDialog() {
|
||||
std::string yaze::app::core::FileDialogWrapper::ShowOpenFolderDialog() {
|
||||
NSOpenPanel* openPanel = [NSOpenPanel openPanel];
|
||||
[openPanel setCanChooseFiles:NO];
|
||||
[openPanel setCanChooseDirectories:YES];
|
||||
@@ -74,7 +90,8 @@ std::string FileDialogWrapper::ShowOpenFolderDialog() {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::vector<std::string> FileDialogWrapper::GetFilesInFolder(const std::string& folder) {
|
||||
std::vector<std::string> yaze::app::core::FileDialogWrapper::GetFilesInFolder(
|
||||
const std::string& folder) {
|
||||
std::vector<std::string> filenames;
|
||||
NSFileManager* fileManager = [NSFileManager defaultManager];
|
||||
NSDirectoryEnumerator* enumerator =
|
||||
@@ -89,7 +106,8 @@ std::vector<std::string> FileDialogWrapper::GetFilesInFolder(const std::string&
|
||||
return filenames;
|
||||
}
|
||||
|
||||
std::vector<std::string> FileDialogWrapper::GetSubdirectoriesInFolder(const std::string& folder) {
|
||||
std::vector<std::string> yaze::app::core::FileDialogWrapper::GetSubdirectoriesInFolder(
|
||||
const std::string& folder) {
|
||||
std::vector<std::string> subdirectories;
|
||||
NSFileManager* fileManager = [NSFileManager defaultManager];
|
||||
NSDirectoryEnumerator* enumerator =
|
||||
@@ -113,4 +131,4 @@ std::vector<std::string> FileDialogWrapper::GetSubdirectoriesInFolder(const std:
|
||||
// Unsupported platform
|
||||
#endif // TARGET_OS_MAC
|
||||
|
||||
#endif // __APPLE__ && __MACH__
|
||||
#endif // __APPLE__ && __MACH__
|
||||
@@ -1,6 +1,20 @@
|
||||
#ifndef YAZE_APP_CORE_PLATFORM_FILE_PATH_H
|
||||
#define YAZE_APP_CORE_PLATFORM_FILE_PATH_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
/**
|
||||
* @brief GetBundleResourcePath returns the path to the bundle resource
|
||||
* directory. Specific to MacOS.
|
||||
*/
|
||||
std::string GetBundleResourcePath();
|
||||
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_CORE_PLATFORM_FILE_PATH_H
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "file_path.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
@@ -5,17 +7,15 @@
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR == 1
|
||||
std::string GetBundleResourcePath() {}
|
||||
#elif TARGET_OS_IPHONE == 1
|
||||
std::string GetBundleResourcePath() {
|
||||
#if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1
|
||||
std::string yaze::app::core::GetBundleResourcePath() {
|
||||
NSBundle* bundle = [NSBundle mainBundle];
|
||||
NSString* resourceDirectoryPath = [bundle bundlePath];
|
||||
NSString* path = [resourceDirectoryPath stringByAppendingString:@"/"];
|
||||
return [path UTF8String];
|
||||
}
|
||||
#elif TARGET_OS_MAC == 1
|
||||
std::string GetBundleResourcePath() {
|
||||
std::string yaze::app::core::GetBundleResourcePath() {
|
||||
NSBundle* bundle = [NSBundle mainBundle];
|
||||
NSString* resourceDirectoryPath = [bundle bundlePath];
|
||||
NSString* path = [resourceDirectoryPath stringByAppendingString:@"/"];
|
||||
|
||||
@@ -1,17 +1,124 @@
|
||||
#include "app/core/platform/font_loader.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/core/platform/file_path.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
absl::Status LoadPackageFonts() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
|
||||
static const char *KARLA_REGULAR = "Karla-Regular.ttf";
|
||||
static const char *ROBOTO_MEDIUM = "Roboto-Medium.ttf";
|
||||
static const char *COUSINE_REGULAR = "Cousine-Regular.ttf";
|
||||
static const char *DROID_SANS = "DroidSans.ttf";
|
||||
static const char *NOTO_SANS_JP = "NotoSansJP.ttf";
|
||||
static const char *IBM_PLEX_JP = "IBMPlexSansJP-Bold.ttf";
|
||||
static const float FONT_SIZE_DEFAULT = 16.0f;
|
||||
static const float FONT_SIZE_DROID_SANS = 18.0f;
|
||||
static const float ICON_FONT_SIZE = 18.0f;
|
||||
|
||||
// Icon configuration
|
||||
static const ImWchar icons_ranges[] = {ICON_MIN_MD, 0xf900, 0};
|
||||
ImFontConfig icons_config;
|
||||
icons_config.MergeMode = true;
|
||||
icons_config.GlyphOffset.y = 5.0f;
|
||||
icons_config.GlyphMinAdvanceX = 13.0f;
|
||||
icons_config.PixelSnapH = true;
|
||||
|
||||
// Japanese font configuration
|
||||
ImFontConfig japanese_font_config;
|
||||
japanese_font_config.MergeMode = true;
|
||||
icons_config.GlyphOffset.y = 5.0f;
|
||||
icons_config.GlyphMinAdvanceX = 13.0f;
|
||||
icons_config.PixelSnapH = true;
|
||||
|
||||
// List of fonts to be loaded
|
||||
std::vector<const char *> font_paths = {
|
||||
KARLA_REGULAR, ROBOTO_MEDIUM, COUSINE_REGULAR, IBM_PLEX_JP, DROID_SANS};
|
||||
|
||||
// Load fonts with associated icon and Japanese merges
|
||||
for (const auto &font_path : font_paths) {
|
||||
float font_size =
|
||||
(font_path == DROID_SANS) ? FONT_SIZE_DROID_SANS : FONT_SIZE_DEFAULT;
|
||||
|
||||
std::string actual_font_path;
|
||||
#ifdef __APPLE__
|
||||
#if TARGET_OS_IOS == 1
|
||||
const std::string kBundlePath = GetBundleResourcePath();
|
||||
actual_font_path = kBundlePath + font_path;
|
||||
#else
|
||||
actual_font_path = absl::StrCat(GetBundleResourcePath(),
|
||||
"Contents/Resources/font/", font_path);
|
||||
#endif
|
||||
#else
|
||||
actual_font_path = std::filesystem::absolute(font_path).string();
|
||||
#endif
|
||||
|
||||
if (!io.Fonts->AddFontFromFileTTF(actual_font_path.data(), font_size)) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Failed to load font from %s", actual_font_path));
|
||||
}
|
||||
|
||||
// Merge icon set
|
||||
std::string actual_icon_font_path = "";
|
||||
const char *icon_font_path = FONT_ICON_FILE_NAME_MD;
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#if TARGET_OS_IOS == 1
|
||||
const std::string kIconBundlePath = GetBundleResourcePath();
|
||||
actual_icon_font_path = kIconBundlePath + "MaterialIcons-Regular.ttf";
|
||||
#else
|
||||
actual_icon_font_path =
|
||||
absl::StrCat(GetBundleResourcePath(),
|
||||
"Contents/Resources/font/MaterialIcons-Regular.ttf");
|
||||
#endif
|
||||
#else
|
||||
actual_icon_font_path = std::filesystem::absolute(icon_font_path).string();
|
||||
#endif
|
||||
io.Fonts->AddFontFromFileTTF(actual_icon_font_path.data(), ICON_FONT_SIZE,
|
||||
&icons_config, icons_ranges);
|
||||
|
||||
// Merge Japanese font
|
||||
std::string actual_japanese_font_path = "";
|
||||
const char *japanese_font_path = NOTO_SANS_JP;
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#if TARGET_OS_IOS == 1
|
||||
const std::string kJapaneseBundlePath = GetBundleResourcePath();
|
||||
actual_japanese_font_path = kJapaneseBundlePath + japanese_font_path;
|
||||
#else
|
||||
actual_japanese_font_path =
|
||||
absl::StrCat(GetBundleResourcePath(), "Contents/Resources/font/",
|
||||
japanese_font_path);
|
||||
#endif
|
||||
#else
|
||||
actual_japanese_font_path =
|
||||
std::filesystem::absolute(japanese_font_path).string();
|
||||
#endif
|
||||
io.Fonts->AddFontFromFileTTF(actual_japanese_font_path.data(), 18.0f,
|
||||
&japanese_font_config,
|
||||
io.Fonts->GetGlyphRangesJapanese());
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
|
||||
int CALLBACK EnumFontFamExProc(const LOGFONT* lpelfe, const TEXTMETRIC* lpntme,
|
||||
int CALLBACK EnumFontFamExProc(const LOGFONT *lpelfe, const TEXTMETRIC *lpntme,
|
||||
DWORD FontType, LPARAM lParam) {
|
||||
// Step 3: Load the font into ImGui
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
io.Fonts->AddFontFromFileTTF(lpelfe->lfFaceName, 16.0f);
|
||||
|
||||
return 1;
|
||||
@@ -34,8 +141,8 @@ void LoadSystemFonts() {
|
||||
RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &valueCount,
|
||||
&maxValueNameSize, &maxValueDataSize, NULL, NULL);
|
||||
|
||||
char* valueName = new char[maxValueNameSize + 1]; // +1 for null terminator
|
||||
BYTE* valueData = new BYTE[maxValueDataSize + 1]; // +1 for null terminator
|
||||
char *valueName = new char[maxValueNameSize + 1]; // +1 for null terminator
|
||||
BYTE *valueData = new BYTE[maxValueDataSize + 1]; // +1 for null terminator
|
||||
|
||||
// Enumerate all font entries
|
||||
for (DWORD i = 0; i < valueCount; i++) {
|
||||
@@ -52,7 +159,9 @@ void LoadSystemFonts() {
|
||||
valueData, &valueDataSize) == ERROR_SUCCESS) {
|
||||
if (valueType == REG_SZ) {
|
||||
// Add the font file path to the vector
|
||||
std::string fontPath((char*)valueData);
|
||||
std::string fontPath(reinterpret_cast<char *>(valueData),
|
||||
valueDataSize);
|
||||
|
||||
fontPaths.push_back(fontPath);
|
||||
}
|
||||
}
|
||||
@@ -64,10 +173,57 @@ void LoadSystemFonts() {
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
|
||||
for (const auto& fontPath : fontPaths) {
|
||||
io.Fonts->AddFontFromFileTTF(fontPath.c_str(), 16.0f);
|
||||
// List of common font face names
|
||||
static const std::unordered_set<std::string> commonFontFaceNames = {
|
||||
"arial",
|
||||
"times",
|
||||
"cour",
|
||||
"verdana",
|
||||
"tahoma",
|
||||
"comic",
|
||||
"Impact",
|
||||
"ariblk",
|
||||
"Trebuchet MS",
|
||||
"Georgia",
|
||||
"Palatino Linotype",
|
||||
"Lucida Sans Unicode",
|
||||
"Tahoma",
|
||||
"Lucida Console"};
|
||||
|
||||
for (auto &fontPath : fontPaths) {
|
||||
// Check if the font path has a "C:\" prefix
|
||||
if (fontPath.substr(0, 2) != "C:") {
|
||||
// Add "C:\Windows\Fonts\" prefix to the font path
|
||||
fontPath = absl::StrFormat("C:\\Windows\\Fonts\\%s", fontPath.c_str());
|
||||
}
|
||||
|
||||
// Check if the font file has a .ttf or .TTF extension
|
||||
std::string extension = fontPath.substr(fontPath.find_last_of(".") + 1);
|
||||
if (extension == "ttf" || extension == "TTF") {
|
||||
// Get the font face name from the font path
|
||||
std::string fontFaceName =
|
||||
fontPath.substr(fontPath.find_last_of("\\/") + 1);
|
||||
fontFaceName = fontFaceName.substr(0, fontFaceName.find_last_of("."));
|
||||
|
||||
// Check if the font face name is in the common font face names list
|
||||
if (commonFontFaceNames.find(fontFaceName) != commonFontFaceNames.end()) {
|
||||
io.Fonts->AddFontFromFileTTF(fontPath.c_str(), 16.0f);
|
||||
|
||||
// Merge icon set
|
||||
// Icon configuration
|
||||
static const ImWchar icons_ranges[] = {ICON_MIN_MD, 0xf900, 0};
|
||||
ImFontConfig icons_config;
|
||||
static const float ICON_FONT_SIZE = 18.0f;
|
||||
icons_config.MergeMode = true;
|
||||
icons_config.GlyphOffset.y = 5.0f;
|
||||
icons_config.GlyphMinAdvanceX = 13.0f;
|
||||
icons_config.PixelSnapH = true;
|
||||
io.Fonts->AddFontFromFileTTF(FONT_ICON_FILE_NAME_MD, ICON_FONT_SIZE,
|
||||
&icons_config, icons_ranges);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,4 +234,8 @@ void LoadSystemFonts() {
|
||||
// ...
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -1,26 +1,17 @@
|
||||
// FontLoader.h
|
||||
#ifndef FONTLOADER_H
|
||||
#define FONTLOADER_H
|
||||
#ifndef YAZE_APP_CORE_PLATFORM_FONTLOADER_H
|
||||
#define YAZE_APP_CORE_PLATFORM_FONTLOADER_H
|
||||
|
||||
#include "TargetConditionals.h"
|
||||
#include "absl/status/status.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
// Windows specific function declaration for loading system fonts into ImGui
|
||||
int CALLBACK EnumFontFamExProc(const LOGFONT* lpelfe, const TEXTMETRIC* lpntme,
|
||||
DWORD FontType, LPARAM lParam);
|
||||
#elif __APPLE__
|
||||
|
||||
#ifdef TARGET_OS_MAC
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
void LoadSystemFonts();
|
||||
absl::Status LoadPackageFonts();
|
||||
|
||||
#elif TARGET_OS_IPHONE
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
void LoadSystemFonts();
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif // FONTLOADER_H
|
||||
#endif // YAZE_APP_CORE_PLATFORM_FONTLOADER_H
|
||||
|
||||
@@ -1,32 +1,20 @@
|
||||
// FontLoader.mm
|
||||
#include "app/core/platform/font_loader.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "app/gui/icons.h"
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
/* Apple OSX and iOS (Darwin). */
|
||||
#import <CoreText/CoreText.h>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#import <CoreText/CoreText.h>
|
||||
#include "app/gui/icons.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR == 1
|
||||
/* iOS in Xcode simulator */
|
||||
void LoadSystemFonts() {}
|
||||
|
||||
#elif TARGET_OS_IPHONE == 1
|
||||
#if TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1
|
||||
/* iOS */
|
||||
void LoadSystemFonts() {}
|
||||
void yaze::app::core::LoadSystemFonts() {}
|
||||
|
||||
#elif TARGET_OS_MAC == 1
|
||||
/* macOS */
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
// MacOS Implementation
|
||||
void LoadSystemFonts() {
|
||||
// List of common macOS system fonts
|
||||
void yaze::app::core::LoadSystemFonts() {
|
||||
NSArray *fontNames = @[ @"Helvetica", @"Times New Roman", @"Courier", @"Arial", @"Verdana" ];
|
||||
|
||||
for (NSString *fontName in fontNames) {
|
||||
@@ -67,8 +55,5 @@ void LoadSystemFonts() {
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Unsupported platform
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
83
src/app/core/platform/renderer.h
Normal file
83
src/app/core/platform/renderer.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef YAZE_APP_CORE_PLATFORM_RENDERER_H
|
||||
#define YAZE_APP_CORE_PLATFORM_RENDERER_H
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/core/utils/sdl_deleter.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
/**
|
||||
* @class Renderer
|
||||
* @brief The Renderer class represents the renderer for the Yaze application.
|
||||
*
|
||||
* This class is a singleton that provides functionality for creating and
|
||||
* rendering bitmaps to the screen. It also includes methods for updating
|
||||
* bitmaps on the screen.
|
||||
*/
|
||||
class Renderer {
|
||||
public:
|
||||
static Renderer &GetInstance() {
|
||||
static Renderer instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
absl::Status CreateRenderer(SDL_Window *window) {
|
||||
renderer_ = std::unique_ptr<SDL_Renderer, SDL_Deleter>(SDL_CreateRenderer(
|
||||
window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED));
|
||||
if (renderer_ == nullptr) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("SDL_CreateRenderer: %s\n", SDL_GetError()));
|
||||
}
|
||||
SDL_SetRenderDrawBlendMode(renderer_.get(), SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(renderer_.get(), 0x00, 0x00, 0x00, 0x00);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
auto renderer() -> SDL_Renderer * { return renderer_.get(); }
|
||||
|
||||
/**
|
||||
* @brief Used to render a bitmap to the screen.
|
||||
*/
|
||||
void RenderBitmap(gfx::Bitmap *bitmap) {
|
||||
bitmap->CreateTexture(renderer_.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Used to update a bitmap on the screen.
|
||||
*/
|
||||
void UpdateBitmap(gfx::Bitmap *bitmap) {
|
||||
bitmap->UpdateTexture(renderer_.get());
|
||||
}
|
||||
|
||||
absl::Status CreateAndRenderBitmap(int width, int height, int depth,
|
||||
const std::vector<uint8_t> &data,
|
||||
gfx::Bitmap &bitmap,
|
||||
gfx::SnesPalette &palette) {
|
||||
bitmap.Create(width, height, depth, data);
|
||||
RETURN_IF_ERROR(bitmap.ApplyPalette(palette));
|
||||
RenderBitmap(&bitmap);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
private:
|
||||
Renderer() = default;
|
||||
|
||||
std::unique_ptr<SDL_Renderer, SDL_Deleter> renderer_;
|
||||
|
||||
Renderer(const Renderer &) = delete;
|
||||
Renderer &operator=(const Renderer &) = delete;
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
29
src/app/core/platform/view_controller.h
Normal file
29
src/app/core/platform/view_controller.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef YAZE_APP_CORE_PLATFORM_VIEW_CONTROLLER_H
|
||||
#define YAZE_APP_CORE_PLATFORM_VIEW_CONTROLLER_H
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
#include "imgui_impl_osx.h"
|
||||
@interface AppViewController : NSViewController <NSWindowDelegate>
|
||||
@end
|
||||
#else
|
||||
@interface AppViewController : UIViewController <MTKViewDelegate>
|
||||
@property(nonatomic) yaze::app::core::Controller *controller;
|
||||
@property(nonatomic) UIHoverGestureRecognizer *hoverGestureRecognizer;
|
||||
@property(nonatomic) UIPinchGestureRecognizer *pinchRecognizer;
|
||||
@property(nonatomic) UISwipeGestureRecognizer *swipeRecognizer;
|
||||
@property(nonatomic) UILongPressGestureRecognizer *longPressRecognizer;
|
||||
@end
|
||||
#endif
|
||||
|
||||
@interface AppViewController () <MTKViewDelegate>
|
||||
@property(nonatomic, readonly) MTKView *mtkView;
|
||||
@property(nonatomic, strong) id<MTLDevice> device;
|
||||
@property(nonatomic, strong) id<MTLCommandQueue> commandQueue;
|
||||
@end
|
||||
|
||||
#endif // __APPLE__
|
||||
|
||||
#endif // YAZE_APP_CORE_PLATFORM_APP_VIEW_CONTROLLER_H
|
||||
@@ -1,22 +1,66 @@
|
||||
#include "app/core/labeling.h"
|
||||
#include "project.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "app/core/constants.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/misc/cpp/imgui_stdlib.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "app/core/common.h"
|
||||
#include "app/core/constants.h"
|
||||
#include "app/gui/icons.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
absl::Status Project::Open(const std::string& project_path) {
|
||||
filepath = project_path;
|
||||
name = project_path.substr(project_path.find_last_of("/") + 1);
|
||||
|
||||
std::ifstream in(project_path);
|
||||
|
||||
if (!in.good()) {
|
||||
return absl::InternalError("Could not open project file.");
|
||||
}
|
||||
|
||||
std::string line;
|
||||
std::getline(in, name);
|
||||
std::getline(in, filepath);
|
||||
std::getline(in, rom_filename_);
|
||||
std::getline(in, code_folder_);
|
||||
std::getline(in, labels_filename_);
|
||||
std::getline(in, keybindings_file);
|
||||
|
||||
while (std::getline(in, line)) {
|
||||
if (line == kEndOfProjectFile) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
in.close();
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Project::Save() {
|
||||
RETURN_IF_ERROR(CheckForEmptyFields());
|
||||
|
||||
std::ofstream out(filepath + "/" + name + ".yaze");
|
||||
if (!out.good()) {
|
||||
return absl::InternalError("Could not open project file.");
|
||||
}
|
||||
|
||||
out << name << std::endl;
|
||||
out << filepath << std::endl;
|
||||
out << rom_filename_ << std::endl;
|
||||
out << code_folder_ << std::endl;
|
||||
out << labels_filename_ << std::endl;
|
||||
out << keybindings_file << std::endl;
|
||||
|
||||
out << kEndOfProjectFile << std::endl;
|
||||
|
||||
out.close();
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
bool ResourceLabelManager::LoadLabels(const std::string& filename) {
|
||||
std::ifstream file(filename);
|
||||
@@ -124,6 +168,11 @@ void ResourceLabelManager::SelectableLabelWithNameEdit(
|
||||
}
|
||||
}
|
||||
|
||||
std::string ResourceLabelManager::GetLabel(const std::string& type,
|
||||
const std::string& key) {
|
||||
return labels_[type][key];
|
||||
}
|
||||
|
||||
std::string ResourceLabelManager::CreateOrGetLabel(
|
||||
const std::string& type, const std::string& key,
|
||||
const std::string& defaultValue) {
|
||||
@@ -136,6 +185,5 @@ std::string ResourceLabelManager::CreateOrGetLabel(
|
||||
return labels_[type][key];
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -1,26 +1,20 @@
|
||||
#ifndef YAZE_APP_CORE_PROJECT_H
|
||||
#define YAZE_APP_CORE_PROJECT_H
|
||||
|
||||
#include "absl/strings/match.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "app/core/common.h"
|
||||
#include "app/core/utils/file_util.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
constexpr absl::string_view kProjectFileExtension = ".yaze";
|
||||
constexpr absl::string_view kProjectFileFilter =
|
||||
"Yaze Project Files (*.yaze)\0*.yaze\0";
|
||||
constexpr absl::string_view kPreviousRomFilenameDelimiter =
|
||||
"PreviousRomFilename";
|
||||
constexpr absl::string_view kEndOfProjectFile = "EndOfProjectFile";
|
||||
const std::string kRecentFilesFilename = "recent_files.txt";
|
||||
constexpr char kEndOfProjectFile[] = "EndOfProjectFile";
|
||||
|
||||
/**
|
||||
* @struct Project
|
||||
@@ -31,81 +25,12 @@ constexpr absl::string_view kEndOfProjectFile = "EndOfProjectFile";
|
||||
* user can have different rom file names for a single project and keep track of
|
||||
* backups.
|
||||
*/
|
||||
|
||||
struct Project : public core::ExperimentFlags {
|
||||
/**
|
||||
* @brief Creates a new project.
|
||||
*
|
||||
* @param project_name The name of the project.
|
||||
* @param project_path The path to the project.
|
||||
* @return An absl::Status indicating the success or failure of the project
|
||||
* creation.
|
||||
*/
|
||||
absl::Status Create(const std::string &project_name) {
|
||||
absl::Status Create(const std::string& project_name) {
|
||||
name = project_name;
|
||||
project_opened_ = true;
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Open(const std::string &project_path) {
|
||||
filepath = project_path;
|
||||
name = project_path.substr(project_path.find_last_of("/") + 1);
|
||||
|
||||
std::ifstream in(project_path);
|
||||
|
||||
if (!in.good()) {
|
||||
return absl::InternalError("Could not open project file.");
|
||||
}
|
||||
|
||||
std::string line;
|
||||
std::getline(in, name);
|
||||
std::getline(in, filepath);
|
||||
std::getline(in, rom_filename_);
|
||||
std::getline(in, code_folder_);
|
||||
std::getline(in, labels_filename_);
|
||||
|
||||
while (std::getline(in, line)) {
|
||||
if (line == kEndOfProjectFile) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (absl::StrContains(line, kPreviousRomFilenameDelimiter)) {
|
||||
previous_rom_filenames_.push_back(
|
||||
line.substr(line.find(kPreviousRomFilenameDelimiter) +
|
||||
kPreviousRomFilenameDelimiter.size() + 1));
|
||||
}
|
||||
}
|
||||
|
||||
in.close();
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Save() {
|
||||
RETURN_IF_ERROR(CheckForEmptyFields());
|
||||
|
||||
std::ofstream out(filepath + "/" + name + ".yaze");
|
||||
if (!out.good()) {
|
||||
return absl::InternalError("Could not open project file.");
|
||||
}
|
||||
|
||||
out << name << std::endl;
|
||||
out << filepath << std::endl;
|
||||
out << rom_filename_ << std::endl;
|
||||
out << code_folder_ << std::endl;
|
||||
out << labels_filename_ << std::endl;
|
||||
|
||||
for (const auto &filename : previous_rom_filenames_) {
|
||||
out << kPreviousRomFilenameDelimiter << " " << filename << std::endl;
|
||||
}
|
||||
|
||||
out << kEndOfProjectFile << std::endl;
|
||||
|
||||
out.close();
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status CheckForEmptyFields() {
|
||||
if (name.empty() || filepath.empty() || rom_filename_.empty() ||
|
||||
code_folder_.empty() || labels_filename_.empty()) {
|
||||
@@ -116,20 +41,93 @@ struct Project : public core::ExperimentFlags {
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status SerializeExperimentFlags() {
|
||||
auto flags = mutable_flags();
|
||||
// TODO: Serialize flags
|
||||
return absl::OkStatus();
|
||||
}
|
||||
absl::Status Open(const std::string &project_path);
|
||||
absl::Status Save();
|
||||
|
||||
bool project_opened_ = false;
|
||||
std::string name;
|
||||
std::string flags = "";
|
||||
std::string filepath;
|
||||
std::string rom_filename_ = "";
|
||||
std::string code_folder_ = "";
|
||||
std::string labels_filename_ = "";
|
||||
std::vector<std::string> previous_rom_filenames_;
|
||||
std::string keybindings_file = "";
|
||||
};
|
||||
|
||||
// Default types
|
||||
static constexpr absl::string_view kDefaultTypes[] = {
|
||||
"Dungeon Names", "Dungeon Room Names", "Overworld Map Names"};
|
||||
|
||||
struct ResourceLabelManager {
|
||||
bool LoadLabels(const std::string& filename);
|
||||
bool SaveLabels();
|
||||
void DisplayLabels(bool* p_open);
|
||||
void EditLabel(const std::string& type, const std::string& key,
|
||||
const std::string& newValue);
|
||||
void SelectableLabelWithNameEdit(bool selected, const std::string& type,
|
||||
const std::string& key,
|
||||
const std::string& defaultValue);
|
||||
std::string GetLabel(const std::string& type, const std::string& key);
|
||||
std::string CreateOrGetLabel(const std::string& type, const std::string& key,
|
||||
const std::string& defaultValue);
|
||||
|
||||
bool labels_loaded_ = false;
|
||||
std::string filename_;
|
||||
struct ResourceType {
|
||||
std::string key_name;
|
||||
std::string display_description;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, std::string>>
|
||||
labels_;
|
||||
};
|
||||
|
||||
class RecentFilesManager {
|
||||
public:
|
||||
RecentFilesManager() : RecentFilesManager(kRecentFilesFilename) {}
|
||||
RecentFilesManager(const std::string& filename) : filename_(filename) {}
|
||||
|
||||
void AddFile(const std::string& file_path) {
|
||||
// Add a file to the list, avoiding duplicates
|
||||
auto it = std::find(recent_files_.begin(), recent_files_.end(), file_path);
|
||||
if (it == recent_files_.end()) {
|
||||
recent_files_.push_back(file_path);
|
||||
}
|
||||
}
|
||||
|
||||
void Save() {
|
||||
std::ofstream file(filename_);
|
||||
if (!file.is_open()) {
|
||||
return; // Handle the error appropriately
|
||||
}
|
||||
|
||||
for (const auto& file_path : recent_files_) {
|
||||
file << file_path << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Load() {
|
||||
std::ifstream file(filename_);
|
||||
if (!file.is_open()) {
|
||||
return;
|
||||
}
|
||||
|
||||
recent_files_.clear();
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
if (!line.empty()) {
|
||||
recent_files_.push_back(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::string>& GetRecentFiles() const {
|
||||
return recent_files_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string filename_;
|
||||
std::vector<std::string> recent_files_;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#ifndef YAZE_APP_CORE_TESTABLE_H
|
||||
#define YAZE_APP_CORE_TESTABLE_H
|
||||
|
||||
#include <imgui_test_engine/imgui_te_context.h>
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
class GuiTestable {
|
||||
public:
|
||||
virtual void RegisterTests(ImGuiTestEngine* e) = 0;
|
||||
|
||||
ImGuiTestEngine* test_engine;
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_CORE_TESTABLE_H
|
||||
74
src/app/core/utils/file_util.cc
Normal file
74
src/app/core/utils/file_util.cc
Normal file
@@ -0,0 +1,74 @@
|
||||
#include "file_util.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
std::string GetFileExtension(const std::string &filename) {
|
||||
size_t dot = filename.find_last_of(".");
|
||||
if (dot == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
return filename.substr(dot + 1);
|
||||
}
|
||||
|
||||
std::string GetFileName(const std::string &filename) {
|
||||
size_t slash = filename.find_last_of("/");
|
||||
if (slash == std::string::npos) {
|
||||
return filename;
|
||||
}
|
||||
return filename.substr(slash + 1);
|
||||
}
|
||||
|
||||
std::string LoadFile(const std::string &filename, Platform platform) {
|
||||
std::string contents;
|
||||
std::string filepath = GetConfigDirectory(platform) + "/" + filename;
|
||||
std::ifstream file(filepath);
|
||||
if (file.is_open()) {
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
contents = buffer.str();
|
||||
file.close();
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
void SaveFile(const std::string &filename, const std::string &contents,
|
||||
Platform platform) {
|
||||
std::string filepath = GetConfigDirectory(platform) + "/" + filename;
|
||||
std::ofstream file(filepath);
|
||||
if (file.is_open()) {
|
||||
file << contents;
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetConfigDirectory(Platform platform) {
|
||||
std::string config_directory = ".yaze";
|
||||
switch (platform) {
|
||||
case Platform::kWindows:
|
||||
config_directory = "~/AppData/Roaming/yaze";
|
||||
break;
|
||||
case Platform::kMacOS:
|
||||
case Platform::kLinux:
|
||||
config_directory = "~/.config/yaze";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return config_directory;
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
24
src/app/core/utils/file_util.h
Normal file
24
src/app/core/utils/file_util.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef YAZE_APP_CORE_UTILS_FILE_UTIL_H
|
||||
#define YAZE_APP_CORE_UTILS_FILE_UTIL_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
enum class Platform { kUnknown, kMacOS, kiOS, kWindows, kLinux };
|
||||
|
||||
std::string GetFileExtension(const std::string &filename);
|
||||
std::string GetFileName(const std::string &filename);
|
||||
std::string LoadFile(const std::string &filename, Platform platform);
|
||||
std::string GetConfigDirectory(Platform platform);
|
||||
|
||||
void SaveFile(const std::string &filename, const std::string &data,
|
||||
Platform platform);
|
||||
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_CORE_UTILS_FILE_UTIL_H
|
||||
40
src/app/core/utils/sdl_deleter.h
Normal file
40
src/app/core/utils/sdl_deleter.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef YAZE_APP_CORE_UTILS_SDL_DELETER_H_
|
||||
#define YAZE_APP_CORE_UTILS_SDL_DELETER_H_
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace core {
|
||||
|
||||
/**
|
||||
* @brief Deleter for SDL_Window and SDL_Renderer.
|
||||
*/
|
||||
struct SDL_Deleter {
|
||||
void operator()(SDL_Window *p) const { SDL_DestroyWindow(p); }
|
||||
void operator()(SDL_Renderer *p) const { SDL_DestroyRenderer(p); }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deleter for SDL_Texture.
|
||||
*/
|
||||
struct SDL_Texture_Deleter {
|
||||
void operator()(SDL_Texture *p) const {
|
||||
SDL_DestroyTexture(p);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deleter for SDL_Surface.
|
||||
*/
|
||||
struct SDL_Surface_Deleter {
|
||||
void operator()(SDL_Surface *p) const {
|
||||
SDL_FreeSurface(p);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_CORE_UTILS_SDL_DELETER_H_
|
||||
@@ -1,16 +1,17 @@
|
||||
#include "assembly_editor.h"
|
||||
|
||||
#include "ImGuiColorTextEdit/TextEditor.h"
|
||||
|
||||
#include "ImGuiFileDialog/ImGuiFileDialog.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "app/core/platform/file_dialog.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "core/constants.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using core::FileDialogWrapper;
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<std::string> RemoveIgnoredFiles(
|
||||
@@ -57,11 +58,11 @@ core::FolderItem LoadFolder(const std::string& folder) {
|
||||
auto root_files = FileDialogWrapper::GetFilesInFolder(current_folder.name);
|
||||
current_folder.files = RemoveIgnoredFiles(root_files, ignored_files);
|
||||
|
||||
for (const auto& folder :
|
||||
for (const auto& subfolder :
|
||||
FileDialogWrapper::GetSubdirectoriesInFolder(current_folder.name)) {
|
||||
core::FolderItem folder_item;
|
||||
folder_item.name = folder;
|
||||
std::string full_folder = current_folder.name + "/" + folder;
|
||||
folder_item.name = subfolder;
|
||||
std::string full_folder = current_folder.name + "/" + subfolder;
|
||||
auto folder_files = FileDialogWrapper::GetFilesInFolder(full_folder);
|
||||
for (const auto& files : folder_files) {
|
||||
// Remove subdirectory files
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
#ifndef YAZE_APP_EDITOR_ASSEMBLY_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_ASSEMBLY_EDITOR_H
|
||||
|
||||
#include "ImGuiColorTextEdit/TextEditor.h"
|
||||
#include "ImGuiFileDialog/ImGuiFileDialog.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <istream>
|
||||
#include <string>
|
||||
|
||||
#include "ImGuiColorTextEdit/TextEditor.h"
|
||||
#include "app/core/common.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/gui/style.h"
|
||||
|
||||
namespace yaze {
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
#ifndef YAZE_APP_EDITOR_CODE_MEMORY_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_CODE_MEMORY_EDITOR_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/misc/cpp/imgui_stdlib.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/common.h"
|
||||
#include "app/core/constants.h"
|
||||
#include "app/core/platform/file_dialog.h"
|
||||
#include "app/core/project.h"
|
||||
#include "app/editor/code/assembly_editor.h"
|
||||
#include "app/editor/code/memory_editor.h"
|
||||
#include "app/editor/dungeon/dungeon_editor.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/editor/graphics/graphics_editor.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/graphics/screen_editor.h"
|
||||
#include "app/editor/music/music_editor.h"
|
||||
#include "app/editor/overworld_editor.h"
|
||||
#include "app/editor/overworld/overworld_editor.h"
|
||||
#include "app/editor/sprite/sprite_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/utils/gfx_context.h"
|
||||
#include "app/editor/utils/recent_files.h"
|
||||
#include "app/emu/emulator.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
@@ -30,6 +23,9 @@
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/rom.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/misc/cpp/imgui_stdlib.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -46,7 +42,7 @@ struct MemoryEditorWithDiffChecker : public SharedRom {
|
||||
static Rom comparison_rom;
|
||||
ImGui::Begin("Hex Editor", &show_memory_editor);
|
||||
if (ImGui::Button("Compare Rom")) {
|
||||
auto file_name = FileDialogWrapper::ShowOpenFileDialog();
|
||||
auto file_name = core::FileDialogWrapper::ShowOpenFileDialog();
|
||||
PRINT_IF_ERROR(comparison_rom.LoadFromFile(file_name));
|
||||
show_compare_rom = true;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#include "dungeon_editor.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "app/core/common.h"
|
||||
#include "app/core/labeling.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "app/core/platform/renderer.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/color.h"
|
||||
@@ -11,13 +9,15 @@
|
||||
#include "app/gui/input.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/dungeon/object_names.h"
|
||||
#include "app/zelda3/dungeon/room_names.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "zelda3/dungeon/room.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
using ImGui::BeginChild;
|
||||
using ImGui::BeginTabBar;
|
||||
using ImGui::BeginTabItem;
|
||||
@@ -50,21 +50,22 @@ absl::Status DungeonEditor::Update() {
|
||||
refresh_graphics_ = false;
|
||||
}
|
||||
|
||||
TAB_BAR("##DungeonEditorTabBar")
|
||||
TAB_ITEM("Room Editor")
|
||||
status_ = UpdateDungeonRoomView();
|
||||
END_TAB_ITEM()
|
||||
TAB_ITEM("Usage Statistics")
|
||||
if (is_loaded_) {
|
||||
static bool calc_stats = false;
|
||||
if (!calc_stats) {
|
||||
CalculateUsageStats();
|
||||
calc_stats = true;
|
||||
if (ImGui::BeginTabBar("##DungeonEditorTabBar")) {
|
||||
TAB_ITEM("Room Editor")
|
||||
status_ = UpdateDungeonRoomView();
|
||||
END_TAB_ITEM()
|
||||
TAB_ITEM("Usage Statistics")
|
||||
if (is_loaded_) {
|
||||
static bool calc_stats = false;
|
||||
if (!calc_stats) {
|
||||
CalculateUsageStats();
|
||||
calc_stats = true;
|
||||
}
|
||||
DrawUsageStats();
|
||||
}
|
||||
DrawUsageStats();
|
||||
END_TAB_ITEM()
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
END_TAB_ITEM()
|
||||
END_TAB_BAR()
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -72,7 +73,7 @@ absl::Status DungeonEditor::Update() {
|
||||
absl::Status DungeonEditor::Initialize() {
|
||||
auto dungeon_man_pal_group = rom()->palette_group().dungeon_main;
|
||||
for (int i = 0; i < 0x100 + 40; i++) {
|
||||
rooms_.emplace_back(zelda3::dungeon::Room(i));
|
||||
rooms_.emplace_back(zelda3::dungeon::Room(/*room_id=*/i));
|
||||
rooms_[i].LoadHeader();
|
||||
rooms_[i].LoadRoomFromROM();
|
||||
if (flags()->kDrawDungeonRoomGraphics) {
|
||||
@@ -107,7 +108,7 @@ absl::Status DungeonEditor::Initialize() {
|
||||
ASSIGN_OR_RETURN(current_palette_group_,
|
||||
gfx::CreatePaletteGroupFromLargePalette(full_palette_));
|
||||
|
||||
graphics_bin_ = *rom()->mutable_bitmap_manager();
|
||||
graphics_bin_ = rom()->gfx_sheets();
|
||||
// Create a vector of pointers to the current block bitmaps
|
||||
for (int block : rooms_[current_room_id_].blocks()) {
|
||||
room_gfx_sheets_.emplace_back(&graphics_bin_[block]);
|
||||
@@ -120,14 +121,14 @@ absl::Status DungeonEditor::RefreshGraphics() {
|
||||
int block = rooms_[current_room_id_].blocks()[i];
|
||||
RETURN_IF_ERROR(graphics_bin_[block].ApplyPaletteWithTransparent(
|
||||
current_palette_group_[current_palette_id_], 0));
|
||||
rom()->UpdateBitmap(&graphics_bin_[block], true);
|
||||
Renderer::GetInstance().UpdateBitmap(&graphics_bin_[block]);
|
||||
}
|
||||
auto sprites_aux1_pal_group = rom()->palette_group().sprites_aux1;
|
||||
for (int i = 9; i < 16; i++) {
|
||||
int block = rooms_[current_room_id_].blocks()[i];
|
||||
RETURN_IF_ERROR(graphics_bin_[block].ApplyPaletteWithTransparent(
|
||||
sprites_aux1_pal_group[current_palette_id_], 0));
|
||||
rom()->UpdateBitmap(&graphics_bin_[block], true);
|
||||
Renderer::GetInstance().UpdateBitmap(&graphics_bin_[block]);
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -195,14 +196,15 @@ absl::Status DungeonEditor::UpdateDungeonRoomView() {
|
||||
TableNextRow();
|
||||
|
||||
TableNextColumn();
|
||||
TAB_BAR("##DungeonRoomTabBar");
|
||||
TAB_ITEM("Rooms");
|
||||
DrawRoomSelector();
|
||||
END_TAB_ITEM();
|
||||
TAB_ITEM("Entrances");
|
||||
DrawEntranceSelector();
|
||||
END_TAB_ITEM();
|
||||
END_TAB_BAR();
|
||||
if (ImGui::BeginTabBar("##DungeonRoomTabBar")) {
|
||||
TAB_ITEM("Rooms");
|
||||
DrawRoomSelector();
|
||||
END_TAB_ITEM();
|
||||
TAB_ITEM("Entrances");
|
||||
DrawEntranceSelector();
|
||||
END_TAB_ITEM();
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
DrawDungeonTabView();
|
||||
@@ -320,7 +322,7 @@ void DungeonEditor::DrawRoomSelector() {
|
||||
for (const auto each_room_name : zelda3::dungeon::kRoomNames) {
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
current_room_id_ == i, "Dungeon Room Names",
|
||||
core::UppercaseHexByte(i), zelda3::dungeon::kRoomNames[i].data());
|
||||
core::UppercaseHexByte(i), each_room_name.data());
|
||||
if (ImGui::IsItemClicked()) {
|
||||
// TODO: Jump to tab if room is already open
|
||||
current_room_id_ = i;
|
||||
@@ -498,7 +500,7 @@ void DungeonEditor::DrawRoomGraphics() {
|
||||
top_left_y = room_gfx_canvas_.zero_point().y + height * current_block;
|
||||
}
|
||||
room_gfx_canvas_.draw_list()->AddImage(
|
||||
(void*)graphics_bin_[block].texture(),
|
||||
(ImTextureID)(intptr_t)graphics_bin_[block].texture(),
|
||||
ImVec2(room_gfx_canvas_.zero_point().x + 2, top_left_y),
|
||||
ImVec2(room_gfx_canvas_.zero_point().x + 0x100,
|
||||
room_gfx_canvas_.zero_point().y + offset));
|
||||
@@ -547,7 +549,7 @@ void DungeonEditor::DrawObjectRenderer() {
|
||||
current_object_ = i;
|
||||
object_renderer_.LoadObject(i,
|
||||
rooms_[current_room_id_].mutable_blocks());
|
||||
rom()->RenderBitmap(object_renderer_.bitmap());
|
||||
Renderer::GetInstance().RenderBitmap(object_renderer_.bitmap());
|
||||
object_loaded_ = true;
|
||||
}
|
||||
i += 1;
|
||||
@@ -827,4 +829,4 @@ void DungeonEditor::DrawUsageGrid() {
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
} // namespace yaze
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
#ifndef YAZE_APP_EDITOR_DUNGEONEDITOR_H
|
||||
#define YAZE_APP_EDITOR_DUNGEONEDITOR_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "app/core/common.h"
|
||||
#include "app/core/labeling.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "app/editor/graphics/gfx_group_editor.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/rom.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "zelda3/dungeon/room.h"
|
||||
#include "zelda3/dungeon/room_entrance.h"
|
||||
#include "zelda3/dungeon/room_object.h"
|
||||
@@ -100,7 +98,6 @@ class DungeonEditor : public Editor,
|
||||
bool object_loaded_ = false;
|
||||
bool palette_showing_ = false;
|
||||
bool refresh_graphics_ = false;
|
||||
bool show_object_render_ = false;
|
||||
|
||||
uint16_t current_entrance_id_ = 0;
|
||||
uint16_t current_room_id_ = 0;
|
||||
@@ -120,12 +117,11 @@ class DungeonEditor : public Editor,
|
||||
gui::Canvas object_canvas_;
|
||||
|
||||
gfx::Bitmap room_gfx_bmp_;
|
||||
gfx::BitmapManager graphics_bin_;
|
||||
std::array<gfx::Bitmap, kNumGfxSheets> graphics_bin_;
|
||||
|
||||
std::vector<gfx::Bitmap*> room_gfx_sheets_;
|
||||
std::vector<zelda3::dungeon::Room> rooms_;
|
||||
std::vector<zelda3::dungeon::RoomEntrance> entrances_;
|
||||
std::vector<gfx::BitmapManager> room_graphics_;
|
||||
zelda3::dungeon::DungeonObjectRenderer object_renderer_;
|
||||
|
||||
absl::flat_hash_map<uint16_t, int> spriteset_usage_;
|
||||
|
||||
52
src/app/editor/editor.cc
Normal file
52
src/app/editor/editor.cc
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "editor.h"
|
||||
|
||||
#include "app/core/constants.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
absl::Status DrawEditor(EditorLayoutParams *params) {
|
||||
if (params->editor == nullptr) {
|
||||
return absl::InternalError("Editor is not initialized");
|
||||
}
|
||||
|
||||
// Draw the editors based on h_split and v_split in a recursive manner
|
||||
if (params->v_split) {
|
||||
ImGui::BeginTable("##VerticalSplitTable", 2);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (params->left)
|
||||
RETURN_IF_ERROR(DrawEditor(params->left));
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (params->right)
|
||||
RETURN_IF_ERROR(DrawEditor(params->right));
|
||||
|
||||
ImGui::EndTable();
|
||||
} else if (params->h_split) {
|
||||
ImGui::BeginTable("##HorizontalSplitTable", 1);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (params->top)
|
||||
RETURN_IF_ERROR(DrawEditor(params->top));
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (params->bottom)
|
||||
RETURN_IF_ERROR(DrawEditor(params->bottom));
|
||||
|
||||
ImGui::EndTable();
|
||||
} else {
|
||||
// No split, just draw the single editor
|
||||
ImGui::Text("%s Editor",
|
||||
kEditorNames[static_cast<int>(params->editor->type())]);
|
||||
RETURN_IF_ERROR(params->editor->Update());
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -1,21 +1,21 @@
|
||||
set(
|
||||
YAZE_APP_EDITOR_SRC
|
||||
app/editor/editor.cc
|
||||
app/editor/editor_manager.cc
|
||||
app/editor/dungeon/dungeon_editor.cc
|
||||
app/editor/master_editor.cc
|
||||
app/editor/master_editor_test.cc
|
||||
app/editor/settings_editor.cc
|
||||
app/editor/overworld_editor.cc
|
||||
app/editor/overworld/overworld_editor.cc
|
||||
app/editor/sprite/sprite_editor.cc
|
||||
app/editor/music/music_editor.cc
|
||||
app/editor/message/message_editor.cc
|
||||
app/editor/message/message_editor_test.cc
|
||||
app/editor/message/message_data.cc
|
||||
app/editor/code/assembly_editor.cc
|
||||
app/editor/graphics/screen_editor.cc
|
||||
app/editor/graphics/graphics_editor.cc
|
||||
app/editor/graphics/palette_editor.cc
|
||||
app/editor/graphics/tile16_editor.cc
|
||||
app/editor/graphics/gfx_group_editor.cc
|
||||
app/editor/utils/gfx_context.cc
|
||||
app/editor/overworld/refresh.cc
|
||||
app/editor/overworld/entity.cc
|
||||
)
|
||||
app/editor/system/settings_editor.cc
|
||||
app/editor/system/command_manager.cc
|
||||
app/editor/system/extension_manager.cc
|
||||
)
|
||||
@@ -1,7 +1,14 @@
|
||||
#ifndef YAZE_APP_CORE_EDITOR_H
|
||||
#define YAZE_APP_CORE_EDITOR_H
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/editor/system/command_manager.h"
|
||||
#include "app/editor/system/constant_manager.h"
|
||||
#include "app/editor/system/extension_manager.h"
|
||||
#include "app/editor/system/history_manager.h"
|
||||
#include "app/editor/system/resource_manager.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -12,6 +19,14 @@ namespace app {
|
||||
*/
|
||||
namespace editor {
|
||||
|
||||
struct EditorContext {
|
||||
ConstantManager constant_manager;
|
||||
CommandManager command_manager;
|
||||
ExtensionManager extension_manager;
|
||||
HistoryManager history_manager;
|
||||
ResourceManager resource_manager;
|
||||
};
|
||||
|
||||
enum class EditorType {
|
||||
kAssembly,
|
||||
kDungeon,
|
||||
@@ -25,7 +40,7 @@ enum class EditorType {
|
||||
kSettings,
|
||||
};
|
||||
|
||||
constexpr std::array<const char*, 10> kEditorNames = {
|
||||
constexpr std::array<const char *, 10> kEditorNames = {
|
||||
"Assembly", "Dungeon", "Graphics", "Music", "Overworld",
|
||||
"Palette", "Screen", "Sprite", "Message", "Settings",
|
||||
};
|
||||
@@ -56,10 +71,35 @@ class Editor {
|
||||
|
||||
protected:
|
||||
EditorType type_;
|
||||
EditorContext context_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Dynamic Editor Layout Parameters
|
||||
*/
|
||||
typedef struct EditorLayoutParams {
|
||||
bool v_split;
|
||||
bool h_split;
|
||||
int v_split_pos;
|
||||
int h_split_pos;
|
||||
Editor *editor = nullptr;
|
||||
EditorLayoutParams *left = nullptr;
|
||||
EditorLayoutParams *right = nullptr;
|
||||
EditorLayoutParams *top = nullptr;
|
||||
EditorLayoutParams *bottom = nullptr;
|
||||
|
||||
EditorLayoutParams() {
|
||||
v_split = false;
|
||||
h_split = false;
|
||||
v_split_pos = 0;
|
||||
h_split_pos = 0;
|
||||
}
|
||||
} EditorLayoutParams;
|
||||
|
||||
absl::Status DrawEditor(EditorLayoutParams *params);
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_CORE_EDITOR_H
|
||||
#endif // YAZE_APP_CORE_EDITOR_H
|
||||
@@ -1,48 +1,39 @@
|
||||
#include "master_editor.h"
|
||||
|
||||
#include "ImGuiColorTextEdit/TextEditor.h"
|
||||
#include "ImGuiFileDialog/ImGuiFileDialog.h"
|
||||
#include "abseil-cpp/absl/strings/match.h"
|
||||
#include "imgui/backends/imgui_impl_sdl2.h"
|
||||
#include "imgui/backends/imgui_impl_sdlrenderer2.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/misc/cpp/imgui_stdlib.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
#include "editor_manager.h"
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/common.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "app/core/constants.h"
|
||||
#include "app/core/platform/file_dialog.h"
|
||||
#include "app/core/project.h"
|
||||
#include "app/editor/code/assembly_editor.h"
|
||||
#include "app/editor/dungeon/dungeon_editor.h"
|
||||
#include "app/editor/graphics/graphics_editor.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/graphics/screen_editor.h"
|
||||
#include "app/editor/music/music_editor.h"
|
||||
#include "app/editor/overworld_editor.h"
|
||||
#include "app/editor/overworld/overworld_editor.h"
|
||||
#include "app/editor/sprite/sprite_editor.h"
|
||||
#include "app/editor/utils/flags.h"
|
||||
#include "app/editor/utils/recent_files.h"
|
||||
#include "app/editor/system/flags.h"
|
||||
#include "app/emu/emulator.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/rom.h"
|
||||
#include "editor/editor.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/misc/cpp/imgui_stdlib.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using namespace ImGui;
|
||||
using core::FileDialogWrapper;
|
||||
|
||||
namespace {
|
||||
|
||||
bool BeginCentered(const char *name) {
|
||||
ImGuiIO const &io = GetIO();
|
||||
|
||||
bool BeginCentered(const char* name) {
|
||||
ImGuiIO const& io = GetIO();
|
||||
ImVec2 pos(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f);
|
||||
SetNextWindowPos(pos, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
||||
ImGuiWindowFlags flags =
|
||||
@@ -58,21 +49,18 @@ bool IsEditorActive(Editor* editor, std::vector<Editor*>& active_editors) {
|
||||
|
||||
} // namespace
|
||||
|
||||
void MasterEditor::SetupScreen(std::shared_ptr<SDL_Renderer> renderer,
|
||||
std::string filename) {
|
||||
sdl_renderer_ = renderer;
|
||||
rom()->SetupRenderer(renderer);
|
||||
void EditorManager::SetupScreen(std::string filename) {
|
||||
if (!filename.empty()) {
|
||||
PRINT_IF_ERROR(rom()->LoadFromFile(filename));
|
||||
}
|
||||
overworld_editor_.InitializeZeml();
|
||||
InitializeCommands();
|
||||
}
|
||||
|
||||
absl::Status MasterEditor::Update() {
|
||||
absl::Status EditorManager::Update() {
|
||||
ManageKeyboardShortcuts();
|
||||
|
||||
DrawYazeMenu();
|
||||
DrawFileDialog();
|
||||
DrawStatusPopup();
|
||||
DrawAboutPopup();
|
||||
DrawInfoPopup();
|
||||
@@ -85,12 +73,15 @@ absl::Status MasterEditor::Update() {
|
||||
rom_assets_loaded_ = true;
|
||||
}
|
||||
|
||||
ManageActiveEditors();
|
||||
if (dynamic_layout_)
|
||||
RETURN_IF_ERROR(DrawDynamicLayout())
|
||||
else
|
||||
ManageActiveEditors();
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void MasterEditor::ManageActiveEditors() {
|
||||
void EditorManager::ManageActiveEditors() {
|
||||
// Show popup pane to select an editor to add
|
||||
static bool show_add_editor = false;
|
||||
if (show_add_editor) OpenPopup("AddEditor");
|
||||
@@ -253,16 +244,23 @@ void MasterEditor::ManageActiveEditors() {
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::ManageKeyboardShortcuts() {
|
||||
absl::Status EditorManager::DrawDynamicLayout() {
|
||||
// Dynamic layout for multiple editors to be open at once
|
||||
// Allows for tiling and resizing of editors using ImGui
|
||||
return DrawEditor(&root_layout_);
|
||||
}
|
||||
|
||||
void EditorManager::ManageKeyboardShortcuts() {
|
||||
bool ctrl_or_super = (GetIO().KeyCtrl || GetIO().KeySuper);
|
||||
|
||||
editor_context_.command_manager.ShowWhichKey();
|
||||
|
||||
// If CMD + R is pressed, reload the top result of recent files
|
||||
if (IsKeyDown(ImGuiKey_R) && ctrl_or_super) {
|
||||
static RecentFilesManager manager("recent_files.txt");
|
||||
manager.Load();
|
||||
if (!manager.GetRecentFiles().empty()) {
|
||||
auto front = manager.GetRecentFiles().front();
|
||||
std::cout << "Reloading: " << front << std::endl;
|
||||
OpenRomOrProject(front);
|
||||
}
|
||||
}
|
||||
@@ -311,55 +309,113 @@ void MasterEditor::ManageKeyboardShortcuts() {
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawFileDialog() {
|
||||
gui::FileDialogPipeline("ChooseFileDlgKey", ".sfc,.smc", std::nullopt, [&]() {
|
||||
std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
|
||||
status_ = rom()->LoadFromFile(filePathName);
|
||||
static RecentFilesManager manager("recent_files.txt");
|
||||
void EditorManager::InitializeCommands() {
|
||||
if (root_layout_.editor == nullptr) {
|
||||
root_layout_.editor = &overworld_editor_;
|
||||
}
|
||||
|
||||
// Load existing recent files
|
||||
manager.Load();
|
||||
// New editor popup for window management commands
|
||||
static EditorLayoutParams new_layout;
|
||||
if (ImGui::BeginPopup("NewEditor")) {
|
||||
ImGui::Text("New Editor");
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("Overworld")) {
|
||||
new_layout.editor = &overworld_editor_;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
if (ImGui::Button("Dungeon")) {
|
||||
new_layout.editor = &dungeon_editor_;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
if (ImGui::Button("Graphics")) {
|
||||
new_layout.editor = &graphics_editor_;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
if (ImGui::Button("Music")) {
|
||||
new_layout.editor = &music_editor_;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
if (ImGui::Button("Palette")) {
|
||||
new_layout.editor = &palette_editor_;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
if (ImGui::Button("Screen")) {
|
||||
new_layout.editor = &screen_editor_;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
if (ImGui::Button("Sprite")) {
|
||||
new_layout.editor = &sprite_editor_;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
if (ImGui::Button("Code")) {
|
||||
new_layout.editor = &assembly_editor_;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
if (ImGui::Button("Settings")) {
|
||||
new_layout.editor = &settings_editor_;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
if (ImGui::Button("Message")) {
|
||||
new_layout.editor = &message_editor_;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// Add a new file
|
||||
manager.AddFile(filePathName);
|
||||
|
||||
// Save the updated list
|
||||
manager.Save();
|
||||
});
|
||||
editor_context_.command_manager.RegisterPrefix("window", 'w',
|
||||
"window management", "");
|
||||
editor_context_.command_manager.RegisterSubcommand(
|
||||
"window", "vsplit", '/', "vertical split",
|
||||
"split windows vertically and place editor in new window", [this]() {
|
||||
ImGui::OpenPopup("NewEditor");
|
||||
root_layout_.v_split = true;
|
||||
});
|
||||
editor_context_.command_manager.RegisterSubcommand(
|
||||
"window", "hsplit", '-', "horizontal split",
|
||||
"split windows horizontally and place editor in new window", [this]() {
|
||||
ImGui::OpenPopup("NewEditor");
|
||||
root_layout_.h_split = true;
|
||||
});
|
||||
editor_context_.command_manager.RegisterSubcommand(
|
||||
"window", "close", 'd', "close", "close the current editor", [this]() {
|
||||
if (root_layout_.editor != nullptr) {
|
||||
root_layout_.editor = nullptr;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void MasterEditor::DrawStatusPopup() {
|
||||
void EditorManager::DrawStatusPopup() {
|
||||
static absl::Status prev_status;
|
||||
if (!status_.ok()) {
|
||||
show_status_ = true;
|
||||
prev_status_ = status_;
|
||||
prev_status = status_;
|
||||
}
|
||||
|
||||
if (show_status_ && (BeginCentered("StatusWindow"))) {
|
||||
Text("%s", ICON_MD_ERROR);
|
||||
Text("%s", prev_status_.ToString().c_str());
|
||||
Text("%s", prev_status.ToString().c_str());
|
||||
Spacing();
|
||||
NextColumn();
|
||||
Columns(1);
|
||||
Separator();
|
||||
NewLine();
|
||||
SameLine(128);
|
||||
if (Button("OK", gui::kDefaultModalSize) ||
|
||||
IsKeyPressed(GetKeyIndex(ImGuiKey_Space))) {
|
||||
if (Button("OK", gui::kDefaultModalSize) || IsKeyPressed(ImGuiKey_Space)) {
|
||||
show_status_ = false;
|
||||
status_ = absl::OkStatus();
|
||||
}
|
||||
SameLine();
|
||||
if (Button(ICON_MD_CONTENT_COPY, ImVec2(50, 0))) {
|
||||
SetClipboardText(prev_status_.ToString().c_str());
|
||||
SetClipboardText(prev_status.ToString().c_str());
|
||||
}
|
||||
End();
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawAboutPopup() {
|
||||
void EditorManager::DrawAboutPopup() {
|
||||
if (about_) OpenPopup("About");
|
||||
if (BeginPopupModal("About", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
Text("Yet Another Zelda3 Editor - v%.2f", core::kYazeVersion);
|
||||
Text("Yet Another Zelda3 Editor - v%s", core::kYazeVersion.data());
|
||||
Text("Written by: scawful");
|
||||
Spacing();
|
||||
Text("Special Thanks: Zarby89, JaredBrian");
|
||||
@@ -373,15 +429,15 @@ void MasterEditor::DrawAboutPopup() {
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawInfoPopup() {
|
||||
void EditorManager::DrawInfoPopup() {
|
||||
if (rom_info_) OpenPopup("ROM Information");
|
||||
if (BeginPopupModal("ROM Information", nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
Text("Title: %s", rom()->title());
|
||||
Text("ROM Size: %ld", rom()->size());
|
||||
Text("Title: %s", rom()->title().c_str());
|
||||
Text("ROM Size: %s", core::UppercaseHexLongLong(rom()->size()).c_str());
|
||||
|
||||
if (Button("Close", gui::kDefaultModalSize) ||
|
||||
IsKeyPressed(GetKeyIndex(ImGuiKey_Space))) {
|
||||
IsKeyPressed(ImGuiKey_Escape)) {
|
||||
rom_info_ = false;
|
||||
CloseCurrentPopup();
|
||||
}
|
||||
@@ -389,33 +445,19 @@ void MasterEditor::DrawInfoPopup() {
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawYazeMenu() {
|
||||
void EditorManager::DrawYazeMenu() {
|
||||
static bool show_display_settings = false;
|
||||
static bool show_command_line_interface = false;
|
||||
|
||||
if (BeginMenuBar()) {
|
||||
DrawFileMenu();
|
||||
DrawEditMenu();
|
||||
DrawViewMenu();
|
||||
DrawTestMenu();
|
||||
DrawProjectMenu();
|
||||
DrawHelpMenu();
|
||||
|
||||
DrawYazeMenuBar();
|
||||
SameLine(GetWindowWidth() - GetStyle().ItemSpacing.x -
|
||||
CalcTextSize(ICON_MD_DISPLAY_SETTINGS).x - 150);
|
||||
// Modify the style of the button to have no background color
|
||||
CalcTextSize(ICON_MD_DISPLAY_SETTINGS).x - 110);
|
||||
PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
if (Button(ICON_MD_DISPLAY_SETTINGS)) {
|
||||
show_display_settings = !show_display_settings;
|
||||
}
|
||||
|
||||
if (Button(ICON_MD_TERMINAL)) {
|
||||
show_command_line_interface = !show_command_line_interface;
|
||||
}
|
||||
PopStyleColor();
|
||||
|
||||
Text("%s", absl::StrCat("yaze v", core::kYazeVersion).c_str());
|
||||
|
||||
Text("yaze v%s", core::kYazeVersion.data());
|
||||
EndMenuBar();
|
||||
}
|
||||
|
||||
@@ -424,27 +466,9 @@ void MasterEditor::DrawYazeMenu() {
|
||||
gui::DrawDisplaySettings();
|
||||
End();
|
||||
}
|
||||
|
||||
if (show_command_line_interface) {
|
||||
Begin("Command Line Interface", &show_command_line_interface,
|
||||
ImGuiWindowFlags_None);
|
||||
Text("Enter a command:");
|
||||
End();
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::OpenRomOrProject(const std::string& filename) {
|
||||
if (absl::StrContains(filename, ".yaze")) {
|
||||
status_ = current_project_.Open(filename);
|
||||
if (status_.ok()) {
|
||||
status_ = OpenProject();
|
||||
}
|
||||
} else {
|
||||
status_ = rom()->LoadFromFile(filename);
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawFileMenu() {
|
||||
void EditorManager::DrawYazeMenuBar() {
|
||||
static bool save_as_menu = false;
|
||||
static bool new_project_menu = false;
|
||||
|
||||
@@ -476,7 +500,6 @@ void MasterEditor::DrawFileMenu() {
|
||||
MENU_ITEM("Save As..") { save_as_menu = true; }
|
||||
|
||||
if (rom()->is_loaded()) {
|
||||
MENU_ITEM("Reload") { status_ = rom()->Reload(); }
|
||||
MENU_ITEM("Close") {
|
||||
status_ = rom()->Close();
|
||||
rom_assets_loaded_ = false;
|
||||
@@ -492,8 +515,8 @@ void MasterEditor::DrawFileMenu() {
|
||||
}
|
||||
if (MenuItem("Open Project")) {
|
||||
// Open an existing project
|
||||
status_ =
|
||||
current_project_.Open(FileDialogWrapper::ShowOpenFileDialog());
|
||||
status_ = current_project_.Open(
|
||||
core::FileDialogWrapper::ShowOpenFileDialog());
|
||||
if (status_.ok()) {
|
||||
status_ = OpenProject();
|
||||
}
|
||||
@@ -582,9 +605,7 @@ void MasterEditor::DrawFileMenu() {
|
||||
}
|
||||
End();
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawEditMenu() {
|
||||
if (BeginMenu("Edit")) {
|
||||
MENU_ITEM2("Undo", "Ctrl+Z") { status_ = current_editor_->Undo(); }
|
||||
MENU_ITEM2("Redo", "Ctrl+Y") { status_ = current_editor_->Redo(); }
|
||||
@@ -593,12 +614,10 @@ void MasterEditor::DrawEditMenu() {
|
||||
MENU_ITEM2("Copy", "Ctrl+C") { status_ = current_editor_->Copy(); }
|
||||
MENU_ITEM2("Paste", "Ctrl+V") { status_ = current_editor_->Paste(); }
|
||||
Separator();
|
||||
MENU_ITEM2("Find", "Ctrl+F") {}
|
||||
MENU_ITEM2("Find", "Ctrl+F") { status_ = current_editor_->Find(); }
|
||||
EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawViewMenu() {
|
||||
static bool show_imgui_metrics = false;
|
||||
static bool show_memory_editor = false;
|
||||
static bool show_asm_editor = false;
|
||||
@@ -606,28 +625,17 @@ void MasterEditor::DrawViewMenu() {
|
||||
static bool show_palette_editor = false;
|
||||
static bool show_emulator = false;
|
||||
|
||||
if (show_imgui_demo) ShowDemoWindow();
|
||||
if (show_imgui_metrics) ShowMetricsWindow(&show_imgui_metrics);
|
||||
if (show_memory_editor) memory_editor_.Update(show_memory_editor);
|
||||
if (show_asm_editor) assembly_editor_.Update(show_asm_editor);
|
||||
|
||||
if (show_emulator) {
|
||||
Begin("Emulator", &show_emulator, ImGuiWindowFlags_MenuBar);
|
||||
emulator_.Run();
|
||||
End();
|
||||
}
|
||||
|
||||
if (show_imgui_metrics) {
|
||||
ShowMetricsWindow(&show_imgui_metrics);
|
||||
}
|
||||
|
||||
if (show_memory_editor) {
|
||||
memory_editor_.Update(show_memory_editor);
|
||||
}
|
||||
|
||||
if (show_imgui_demo) {
|
||||
ShowDemoWindow();
|
||||
}
|
||||
|
||||
if (show_asm_editor) {
|
||||
assembly_editor_.Update(show_asm_editor);
|
||||
}
|
||||
|
||||
if (show_palette_editor) {
|
||||
Begin("Palette Editor", &show_palette_editor);
|
||||
status_ = palette_editor_.Update();
|
||||
@@ -635,6 +643,7 @@ void MasterEditor::DrawViewMenu() {
|
||||
}
|
||||
|
||||
if (BeginMenu("View")) {
|
||||
MenuItem("Dynamic Layout", nullptr, &dynamic_layout_);
|
||||
MenuItem("Emulator", nullptr, &show_emulator);
|
||||
Separator();
|
||||
MenuItem("Memory Editor", nullptr, &show_memory_editor);
|
||||
@@ -647,23 +656,9 @@ void MasterEditor::DrawViewMenu() {
|
||||
MenuItem("ImGui Metrics", nullptr, &show_imgui_metrics);
|
||||
EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawTestMenu() {
|
||||
static bool show_tests_ = false;
|
||||
|
||||
if (BeginMenu("Tests")) {
|
||||
MenuItem("Run Tests", nullptr, &show_tests_);
|
||||
EndMenu();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MasterEditor::DrawProjectMenu() {
|
||||
static bool show_resource_label_manager = false;
|
||||
|
||||
if (current_project_.project_opened_) {
|
||||
// Project Menu
|
||||
if (BeginMenu("Project")) {
|
||||
Text("Name: %s", current_project_.name.c_str());
|
||||
Text("ROM: %s", current_project_.rom_filename_.c_str());
|
||||
@@ -674,16 +669,7 @@ void MasterEditor::DrawProjectMenu() {
|
||||
EndMenu();
|
||||
}
|
||||
}
|
||||
if (show_resource_label_manager) {
|
||||
rom()->resource_label()->DisplayLabels(&show_resource_label_manager);
|
||||
if (current_project_.project_opened_ &&
|
||||
!current_project_.labels_filename_.empty()) {
|
||||
current_project_.labels_filename_ = rom()->resource_label()->filename_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::DrawHelpMenu() {
|
||||
static bool open_rom_help = false;
|
||||
static bool open_supported_features = false;
|
||||
static bool open_manage_project = false;
|
||||
@@ -752,11 +738,10 @@ void MasterEditor::DrawHelpMenu() {
|
||||
Text("Project Menu");
|
||||
Text("Create a new project or open an existing one.");
|
||||
Text("Save the project to save the current state of the project.");
|
||||
Text(
|
||||
TextWrapped(
|
||||
"To save a project, you need to first open a ROM and initialize your "
|
||||
"code path and labels file. Label resource manager can be found in "
|
||||
"the "
|
||||
"View menu. Code path is set in the Code editor after opening a "
|
||||
"the View menu. Code path is set in the Code editor after opening a "
|
||||
"folder.");
|
||||
|
||||
if (Button("Close", gui::kDefaultModalSize)) {
|
||||
@@ -765,58 +750,51 @@ void MasterEditor::DrawHelpMenu() {
|
||||
}
|
||||
EndPopup();
|
||||
}
|
||||
|
||||
if (show_resource_label_manager) {
|
||||
rom()->resource_label()->DisplayLabels(&show_resource_label_manager);
|
||||
if (current_project_.project_opened_ &&
|
||||
!current_project_.labels_filename_.empty()) {
|
||||
current_project_.labels_filename_ = rom()->resource_label()->filename_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::LoadRom() {
|
||||
if (flags()->kNewFileDialogWrapper) {
|
||||
auto file_name = FileDialogWrapper::ShowOpenFileDialog();
|
||||
PRINT_IF_ERROR(rom()->LoadFromFile(file_name));
|
||||
void EditorManager::LoadRom() {
|
||||
auto file_name = FileDialogWrapper::ShowOpenFileDialog();
|
||||
auto load_rom = rom()->LoadFromFile(file_name);
|
||||
if (load_rom.ok()) {
|
||||
static RecentFilesManager manager("recent_files.txt");
|
||||
manager.Load();
|
||||
manager.AddFile(file_name);
|
||||
manager.Save();
|
||||
} else {
|
||||
ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Open ROM",
|
||||
".sfc,.smc", ".");
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEditor::SaveRom() {
|
||||
void EditorManager::SaveRom() {
|
||||
if (flags()->kSaveDungeonMaps) {
|
||||
status_ = screen_editor_.SaveDungeonMaps();
|
||||
RETURN_VOID_IF_ERROR(status_);
|
||||
}
|
||||
if (flags()->overworld.kSaveOverworldMaps) {
|
||||
RETURN_VOID_IF_ERROR(
|
||||
status_ = overworld_editor_.overworld()->CreateTile32Tilemap());
|
||||
status_ = overworld_editor_.overworld()->SaveMap32Tiles();
|
||||
RETURN_VOID_IF_ERROR(status_);
|
||||
status_ = overworld_editor_.overworld()->SaveMap16Tiles();
|
||||
RETURN_VOID_IF_ERROR(status_);
|
||||
status_ = overworld_editor_.overworld()->SaveOverworldMaps();
|
||||
RETURN_VOID_IF_ERROR(status_);
|
||||
}
|
||||
if (flags()->overworld.kSaveOverworldEntrances) {
|
||||
status_ = overworld_editor_.overworld()->SaveEntrances();
|
||||
RETURN_VOID_IF_ERROR(status_);
|
||||
}
|
||||
if (flags()->overworld.kSaveOverworldExits) {
|
||||
status_ = overworld_editor_.overworld()->SaveExits();
|
||||
RETURN_VOID_IF_ERROR(status_);
|
||||
}
|
||||
if (flags()->overworld.kSaveOverworldItems) {
|
||||
status_ = overworld_editor_.overworld()->SaveItems();
|
||||
RETURN_VOID_IF_ERROR(status_);
|
||||
}
|
||||
if (flags()->overworld.kSaveOverworldProperties) {
|
||||
status_ = overworld_editor_.overworld()->SaveMapProperties();
|
||||
RETURN_VOID_IF_ERROR(status_);
|
||||
}
|
||||
|
||||
status_ = overworld_editor_.Save();
|
||||
RETURN_VOID_IF_ERROR(status_);
|
||||
|
||||
status_ = rom()->SaveToFile(backup_rom_, save_new_auto_);
|
||||
}
|
||||
|
||||
absl::Status MasterEditor::OpenProject() {
|
||||
void EditorManager::OpenRomOrProject(const std::string& filename) {
|
||||
if (absl::StrContains(filename, ".yaze")) {
|
||||
status_ = current_project_.Open(filename);
|
||||
if (status_.ok()) {
|
||||
status_ = OpenProject();
|
||||
}
|
||||
} else {
|
||||
status_ = rom()->LoadFromFile(filename);
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status EditorManager::OpenProject() {
|
||||
RETURN_IF_ERROR(rom()->LoadFromFile(current_project_.rom_filename_));
|
||||
|
||||
if (!rom()->resource_label()->LoadLabels(current_project_.labels_filename_)) {
|
||||
@@ -1,17 +1,9 @@
|
||||
#ifndef YAZE_APP_EDITOR_MASTER_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_MASTER_EDITOR_H
|
||||
#ifndef YAZE_APP_EDITOR_EDITOR_MANAGER_H
|
||||
#define YAZE_APP_EDITOR_EDITOR_MANAGER_H
|
||||
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
#include "ImGuiColorTextEdit/TextEditor.h"
|
||||
#include "ImGuiFileDialog/ImGuiFileDialog.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/misc/cpp/imgui_stdlib.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/common.h"
|
||||
#include "app/core/constants.h"
|
||||
#include "app/core/project.h"
|
||||
#include "app/editor/code/assembly_editor.h"
|
||||
#include "app/editor/code/memory_editor.h"
|
||||
@@ -21,15 +13,10 @@
|
||||
#include "app/editor/graphics/screen_editor.h"
|
||||
#include "app/editor/message/message_editor.h"
|
||||
#include "app/editor/music/music_editor.h"
|
||||
#include "app/editor/overworld_editor.h"
|
||||
#include "app/editor/settings_editor.h"
|
||||
#include "app/editor/overworld/overworld_editor.h"
|
||||
#include "app/editor/sprite/sprite_editor.h"
|
||||
#include "app/editor/utils/gfx_context.h"
|
||||
#include "app/editor/system/settings_editor.h"
|
||||
#include "app/emu/emulator.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
@@ -38,28 +25,19 @@ namespace app {
|
||||
namespace editor {
|
||||
|
||||
/**
|
||||
* @class MasterEditor
|
||||
* @brief The MasterEditor class represents the main editor for a Rom in the
|
||||
* Yaze application.
|
||||
* @class EditorManager
|
||||
* @brief The EditorManager controls the main editor window and manages the
|
||||
* various editor classes.
|
||||
*
|
||||
* This class inherits from SharedRom, GfxContext, and ExperimentFlags, and
|
||||
* provides functionality for setting up the screen, updating the editor, and
|
||||
* shutting down the editor. It also includes methods for drawing various menus
|
||||
* and popups, saving the Rom, and managing editor-specific flags.
|
||||
*
|
||||
* The MasterEditor class contains instances of various editor classes such as
|
||||
* The EditorManager class contains instances of various editor classes such as
|
||||
* AssemblyEditor, DungeonEditor, GraphicsEditor, MusicEditor, OverworldEditor,
|
||||
* PaletteEditor, ScreenEditor, and SpriteEditor. The current_editor_ member
|
||||
* variable points to the currently active editor in the tab view.
|
||||
*
|
||||
* @note This class assumes the presence of an SDL_Renderer object for rendering
|
||||
* graphics.
|
||||
*/
|
||||
class MasterEditor : public SharedRom,
|
||||
public context::GfxContext,
|
||||
public core::ExperimentFlags {
|
||||
class EditorManager : public SharedRom, public core::ExperimentFlags {
|
||||
public:
|
||||
MasterEditor() {
|
||||
EditorManager() {
|
||||
current_editor_ = &overworld_editor_;
|
||||
active_editors_.push_back(&overworld_editor_);
|
||||
active_editors_.push_back(&dungeon_editor_);
|
||||
@@ -67,37 +45,33 @@ class MasterEditor : public SharedRom,
|
||||
active_editors_.push_back(&palette_editor_);
|
||||
active_editors_.push_back(&sprite_editor_);
|
||||
active_editors_.push_back(&message_editor_);
|
||||
active_editors_.push_back(&screen_editor_);
|
||||
}
|
||||
|
||||
void SetupScreen(std::shared_ptr<SDL_Renderer> renderer,
|
||||
std::string filename = "");
|
||||
void SetupScreen(std::string filename = "");
|
||||
absl::Status Update();
|
||||
|
||||
auto emulator() -> emu::Emulator& { return emulator_; }
|
||||
auto emulator() -> emu::Emulator & { return emulator_; }
|
||||
auto quit() { return quit_; }
|
||||
auto overworld_editor() -> OverworldEditor& { return overworld_editor_; }
|
||||
|
||||
private:
|
||||
void ManageActiveEditors();
|
||||
void ManageKeyboardShortcuts();
|
||||
void OpenRomOrProject(const std::string& filename);
|
||||
absl::Status DrawDynamicLayout();
|
||||
|
||||
void ManageKeyboardShortcuts();
|
||||
void InitializeCommands();
|
||||
|
||||
void DrawFileDialog();
|
||||
void DrawStatusPopup();
|
||||
void DrawAboutPopup();
|
||||
void DrawInfoPopup();
|
||||
|
||||
void DrawYazeMenu();
|
||||
void DrawFileMenu();
|
||||
void DrawEditMenu();
|
||||
void DrawViewMenu();
|
||||
void DrawTestMenu();
|
||||
void DrawProjectMenu();
|
||||
void DrawHelpMenu();
|
||||
void DrawYazeMenuBar();
|
||||
|
||||
void LoadRom();
|
||||
void SaveRom();
|
||||
|
||||
void OpenRomOrProject(const std::string &filename);
|
||||
absl::Status OpenProject();
|
||||
|
||||
bool quit_ = false;
|
||||
@@ -107,16 +81,21 @@ class MasterEditor : public SharedRom,
|
||||
bool save_new_auto_ = true;
|
||||
bool show_status_ = false;
|
||||
bool rom_assets_loaded_ = false;
|
||||
bool dynamic_layout_ = false;
|
||||
|
||||
absl::Status status_;
|
||||
absl::Status prev_status_;
|
||||
|
||||
std::shared_ptr<SDL_Renderer> sdl_renderer_;
|
||||
|
||||
emu::Emulator emulator_;
|
||||
|
||||
Project current_project_;
|
||||
std::vector<Editor *> active_editors_;
|
||||
std::vector<EditorLayoutParams> active_layouts_;
|
||||
|
||||
EditorLayoutParams root_layout_;
|
||||
|
||||
Project current_project_;
|
||||
EditorContext editor_context_;
|
||||
|
||||
Editor *current_editor_ = nullptr;
|
||||
AssemblyEditor assembly_editor_;
|
||||
DungeonEditor dungeon_editor_;
|
||||
GraphicsEditor graphics_editor_;
|
||||
@@ -128,14 +107,10 @@ class MasterEditor : public SharedRom,
|
||||
SettingsEditor settings_editor_;
|
||||
MessageEditor message_editor_;
|
||||
MemoryEditorWithDiffChecker memory_editor_;
|
||||
|
||||
ImVector<int> active_tabs_;
|
||||
std::vector<Editor*> active_editors_;
|
||||
Editor* current_editor_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_MASTER_EDITOR_H
|
||||
#endif // YAZE_APP_EDITOR_EDITOR_MANAGER_H
|
||||
@@ -1,22 +1,14 @@
|
||||
#include "gfx_group_editor.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -27,7 +19,6 @@ using ImGui::BeginGroup;
|
||||
using ImGui::BeginTabBar;
|
||||
using ImGui::BeginTabItem;
|
||||
using ImGui::BeginTable;
|
||||
using ImGui::ColorButton;
|
||||
using ImGui::EndChild;
|
||||
using ImGui::EndGroup;
|
||||
using ImGui::EndTabBar;
|
||||
@@ -39,7 +30,6 @@ using ImGui::IsItemClicked;
|
||||
using ImGui::PopID;
|
||||
using ImGui::PushID;
|
||||
using ImGui::SameLine;
|
||||
using ImGui::Selectable;
|
||||
using ImGui::Separator;
|
||||
using ImGui::SetNextItemWidth;
|
||||
using ImGui::TableHeadersRow;
|
||||
@@ -123,7 +113,7 @@ void GfxGroupEditor::DrawBlocksetViewer(bool sheet_only) {
|
||||
BeginGroup();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int sheet_id = rom()->main_blockset_ids[selected_blockset_][i];
|
||||
auto sheet = rom()->bitmap_manager()[sheet_id];
|
||||
auto sheet = rom()->gfx_sheets().at(sheet_id);
|
||||
gui::BitmapCanvasPipeline(blockset_canvas_, sheet, 256, 0x10 * 0x04,
|
||||
0x20, true, false, 22);
|
||||
}
|
||||
@@ -176,7 +166,7 @@ void GfxGroupEditor::DrawRoomsetViewer() {
|
||||
BeginGroup();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int sheet_id = rom()->room_blockset_ids[selected_roomset_][i];
|
||||
auto sheet = rom()->bitmap_manager()[sheet_id];
|
||||
auto sheet = rom()->gfx_sheets().at(sheet_id);
|
||||
gui::BitmapCanvasPipeline(roomset_canvas_, sheet, 256, 0x10 * 0x04,
|
||||
0x20, true, false, 23);
|
||||
}
|
||||
@@ -214,7 +204,7 @@ void GfxGroupEditor::DrawSpritesetViewer(bool sheet_only) {
|
||||
BeginGroup();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int sheet_id = rom()->spriteset_ids[selected_spriteset_][i];
|
||||
auto sheet = rom()->bitmap_manager()[115 + sheet_id];
|
||||
auto sheet = rom()->gfx_sheets().at(115 + sheet_id);
|
||||
gui::BitmapCanvasPipeline(spriteset_canvas_, sheet, 256, 0x10 * 0x04,
|
||||
0x20, true, false, 24);
|
||||
}
|
||||
@@ -229,7 +219,7 @@ void DrawPaletteFromPaletteGroup(gfx::SnesPalette &palette) {
|
||||
if (palette.empty()) {
|
||||
return;
|
||||
}
|
||||
for (int n = 0; n < palette.size(); n++) {
|
||||
for (size_t n = 0; n < palette.size(); n++) {
|
||||
PushID(n);
|
||||
if ((n % 8) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
|
||||
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
#ifndef YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -40,13 +29,7 @@ class GfxGroupEditor : public SharedRom {
|
||||
selected_spriteset_ = spriteset;
|
||||
}
|
||||
|
||||
void InitBlockset(gfx::Bitmap* tile16_blockset) {
|
||||
tile16_blockset_bmp_ = tile16_blockset;
|
||||
}
|
||||
|
||||
private:
|
||||
int preview_palette_id_ = 0;
|
||||
int last_sheet_id_ = 0;
|
||||
uint8_t selected_blockset_ = 0;
|
||||
uint8_t selected_roomset_ = 0;
|
||||
uint8_t selected_spriteset_ = 0;
|
||||
@@ -57,17 +40,9 @@ class GfxGroupEditor : public SharedRom {
|
||||
gui::Canvas spriteset_canvas_;
|
||||
|
||||
gfx::SnesPalette palette_;
|
||||
gfx::PaletteGroup palette_group_;
|
||||
gfx::Bitmap* tile16_blockset_bmp_;
|
||||
|
||||
std::vector<Bytes> tile16_individual_data_;
|
||||
std::vector<gfx::Bitmap> tile16_individual_;
|
||||
|
||||
gui::BitmapViewer gfx_group_viewer_;
|
||||
zelda3::overworld::Overworld overworld_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
#endif // YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H
|
||||
#endif // YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H
|
||||
|
||||
@@ -1,30 +1,33 @@
|
||||
#include "graphics_editor.h"
|
||||
|
||||
#include "ImGuiFileDialog/ImGuiFileDialog.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/misc/cpp/imgui_stdlib.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/core/platform/clipboard.h"
|
||||
#include "app/core/platform/renderer.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/compression.h"
|
||||
#include "app/gfx/scad_format.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/asset_browser.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/modules/asset_browser.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/rom.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/misc/cpp/imgui_stdlib.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
using gfx::kPaletteGroupAddressesKeys;
|
||||
using ImGui::Button;
|
||||
using ImGui::InputInt;
|
||||
@@ -32,70 +35,56 @@ using ImGui::InputText;
|
||||
using ImGui::SameLine;
|
||||
using ImGui::TableNextColumn;
|
||||
|
||||
static constexpr absl::string_view kGfxToolsetColumnNames[] = {
|
||||
"#memoryEditor",
|
||||
"##separator_gfx1",
|
||||
};
|
||||
|
||||
constexpr ImGuiTableFlags kGfxEditTableFlags =
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable |
|
||||
ImGuiTableFlags_SizingFixedFit;
|
||||
|
||||
constexpr ImGuiTabBarFlags kGfxEditTabBarFlags =
|
||||
ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
|
||||
ImGuiTabBarFlags_FittingPolicyResizeDown |
|
||||
ImGuiTabBarFlags_TabListPopupButton;
|
||||
|
||||
constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_SizingStretchSame;
|
||||
|
||||
absl::Status GraphicsEditor::Update() {
|
||||
TAB_BAR("##TabBar")
|
||||
status_ = UpdateGfxEdit();
|
||||
TAB_ITEM("Sheet Browser")
|
||||
if (asset_browser_.Initialized == false) {
|
||||
asset_browser_.Initialize(rom()->mutable_bitmap_manager());
|
||||
if (ImGui::BeginTabBar("##TabBar")) {
|
||||
status_ = UpdateGfxEdit();
|
||||
TAB_ITEM("Sheet Browser")
|
||||
if (asset_browser_.Initialized == false) {
|
||||
asset_browser_.Initialize(rom()->gfx_sheets());
|
||||
}
|
||||
asset_browser_.Draw(rom()->gfx_sheets());
|
||||
END_TAB_ITEM()
|
||||
status_ = UpdateScadView();
|
||||
status_ = UpdateLinkGfxView();
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
asset_browser_.Draw(rom()->mutable_bitmap_manager());
|
||||
|
||||
END_TAB_ITEM()
|
||||
status_ = UpdateScadView();
|
||||
status_ = UpdateLinkGfxView();
|
||||
END_TAB_BAR()
|
||||
CLEAR_AND_RETURN_STATUS(status_)
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status GraphicsEditor::UpdateGfxEdit() {
|
||||
TAB_ITEM("Sheet Editor")
|
||||
if (ImGui::BeginTabItem("Sheet Editor")) {
|
||||
if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
|
||||
ImVec2(0, 0))) {
|
||||
for (const auto& name :
|
||||
{"Tilesheets", "Current Graphics", "Palette Controls"})
|
||||
ImGui::TableSetupColumn(name);
|
||||
|
||||
if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
|
||||
ImVec2(0, 0))) {
|
||||
for (const auto& name :
|
||||
{"Tilesheets", "Current Graphics", "Palette Controls"})
|
||||
ImGui::TableSetupColumn(name);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
NEXT_COLUMN();
|
||||
status_ = UpdateGfxSheetList();
|
||||
|
||||
NEXT_COLUMN();
|
||||
status_ = UpdateGfxSheetList();
|
||||
NEXT_COLUMN();
|
||||
if (rom()->is_loaded()) {
|
||||
DrawGfxEditToolset();
|
||||
status_ = UpdateGfxTabView();
|
||||
}
|
||||
|
||||
NEXT_COLUMN();
|
||||
if (rom()->is_loaded()) {
|
||||
DrawGfxEditToolset();
|
||||
status_ = UpdateGfxTabView();
|
||||
NEXT_COLUMN();
|
||||
if (rom()->is_loaded()) {
|
||||
status_ = UpdatePaletteColumn();
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
|
||||
NEXT_COLUMN();
|
||||
if (rom()->is_loaded()) {
|
||||
status_ = UpdatePaletteColumn();
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
|
||||
END_TAB_ITEM()
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -127,8 +116,8 @@ void GraphicsEditor::DrawGfxEditToolset() {
|
||||
TableNextColumn();
|
||||
if (Button(ICON_MD_CONTENT_COPY)) {
|
||||
std::vector<uint8_t> png_data =
|
||||
rom()->bitmap_manager().shared_bitmap(current_sheet_).GetPngData();
|
||||
CopyImageToClipboard(png_data);
|
||||
rom()->gfx_sheets().at(current_sheet_).GetPngData();
|
||||
core::CopyImageToClipboard(png_data);
|
||||
}
|
||||
HOVER_HINT("Copy to Clipboard");
|
||||
|
||||
@@ -136,14 +125,14 @@ void GraphicsEditor::DrawGfxEditToolset() {
|
||||
if (Button(ICON_MD_CONTENT_PASTE)) {
|
||||
std::vector<uint8_t> png_data;
|
||||
int width, height;
|
||||
GetImageFromClipboard(png_data, width, height);
|
||||
core::GetImageFromClipboard(png_data, width, height);
|
||||
if (png_data.size() > 0) {
|
||||
rom()
|
||||
->mutable_bitmap_manager()
|
||||
->mutable_bitmap(current_sheet_)
|
||||
->Create(width, height, 8, png_data);
|
||||
rom()->UpdateBitmap(
|
||||
rom()->mutable_bitmap_manager()->mutable_bitmap(current_sheet_));
|
||||
->mutable_gfx_sheets()
|
||||
->at(current_sheet_)
|
||||
.Create(width, height, 8, png_data);
|
||||
Renderer::GetInstance().UpdateBitmap(
|
||||
&rom()->mutable_gfx_sheets()->at(current_sheet_));
|
||||
}
|
||||
}
|
||||
HOVER_HINT("Paste from Clipboard");
|
||||
@@ -163,7 +152,7 @@ void GraphicsEditor::DrawGfxEditToolset() {
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
auto bitmap = rom()->bitmap_manager()[current_sheet_];
|
||||
auto bitmap = rom()->gfx_sheets()[current_sheet_];
|
||||
auto palette = bitmap.palette();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ImGui::SameLine();
|
||||
@@ -192,28 +181,28 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
|
||||
static ImGuiSelectionBasicStorage selection;
|
||||
ImGuiMultiSelectFlags flags =
|
||||
ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
|
||||
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(
|
||||
flags, selection.Size, rom()->bitmap_manager().size());
|
||||
ImGuiMultiSelectIO* ms_io =
|
||||
ImGui::BeginMultiSelect(flags, selection.Size, kNumGfxSheets);
|
||||
selection.ApplyRequests(ms_io);
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(rom()->bitmap_manager().size());
|
||||
clipper.Begin(kNumGfxSheets);
|
||||
if (ms_io->RangeSrcItem != -1)
|
||||
clipper.IncludeItemByIndex(
|
||||
(int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
|
||||
|
||||
for (auto& [key, value] : rom()->bitmap_manager()) {
|
||||
int key = 0;
|
||||
for (auto& value : rom()->gfx_sheets()) {
|
||||
ImGui::BeginChild(absl::StrFormat("##GfxSheet%02X", key).c_str(),
|
||||
ImVec2(0x100 + 1, 0x40 + 1), true,
|
||||
ImGuiWindowFlags_NoDecoration);
|
||||
ImGui::PopStyleVar();
|
||||
gui::Canvas graphics_bin_canvas_;
|
||||
|
||||
graphics_bin_canvas_.DrawBackground(ImVec2(0x100 + 1, 0x40 + 1));
|
||||
graphics_bin_canvas_.DrawContextMenu();
|
||||
if (value.is_active()) {
|
||||
auto texture = value.texture();
|
||||
graphics_bin_canvas_.draw_list()->AddImage(
|
||||
(void*)texture,
|
||||
(ImTextureID)(intptr_t)texture,
|
||||
ImVec2(graphics_bin_canvas_.zero_point().x + 2,
|
||||
graphics_bin_canvas_.zero_point().y + 2),
|
||||
ImVec2(graphics_bin_canvas_.zero_point().x +
|
||||
@@ -240,6 +229,8 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
|
||||
graphics_bin_canvas_.draw_list()->AddText(
|
||||
text_pos, IM_COL32(125, 255, 125, 255),
|
||||
absl::StrFormat("%02X", key).c_str());
|
||||
|
||||
key++;
|
||||
}
|
||||
graphics_bin_canvas_.DrawGrid(16.0f);
|
||||
graphics_bin_canvas_.DrawOverlay();
|
||||
@@ -256,6 +247,10 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
|
||||
|
||||
absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
static int next_tab_id = 0;
|
||||
constexpr ImGuiTabBarFlags kGfxEditTabBarFlags =
|
||||
ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
|
||||
ImGuiTabBarFlags_FittingPolicyResizeDown |
|
||||
ImGuiTabBarFlags_TabListPopupButton;
|
||||
|
||||
if (ImGui::BeginTabBar("##GfxEditTabBar", kGfxEditTabBarFlags)) {
|
||||
if (ImGui::TabItemButton(ICON_MD_ADD, ImGuiTabItemFlags_Trailing |
|
||||
@@ -285,18 +280,17 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar |
|
||||
ImGuiWindowFlags_AlwaysHorizontalScrollbar);
|
||||
|
||||
gfx::Bitmap& current_bitmap =
|
||||
*rom()->mutable_bitmap_manager()->mutable_bitmap(sheet_id);
|
||||
gfx::Bitmap& current_bitmap = rom()->mutable_gfx_sheets()->at(sheet_id);
|
||||
|
||||
auto draw_tile_event = [&]() {
|
||||
current_sheet_canvas_.DrawTileOnBitmap(tile_size_, ¤t_bitmap,
|
||||
current_color_);
|
||||
rom()->UpdateBitmap(¤t_bitmap, true);
|
||||
Renderer::GetInstance().UpdateBitmap(¤t_bitmap);
|
||||
};
|
||||
|
||||
current_sheet_canvas_.UpdateColorPainter(
|
||||
rom()->bitmap_manager()[sheet_id], current_color_, draw_tile_event,
|
||||
tile_size_, current_scale_);
|
||||
rom()->mutable_gfx_sheets()->at(sheet_id), current_color_,
|
||||
draw_tile_event, tile_size_, current_scale_);
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::EndTabItem();
|
||||
@@ -328,7 +322,7 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
current_sheet_ = id;
|
||||
// ImVec2(0x100, 0x40),
|
||||
current_sheet_canvas_.UpdateColorPainter(
|
||||
rom()->bitmap_manager()[id], current_color_,
|
||||
rom()->mutable_gfx_sheets()->at(id), current_color_,
|
||||
[&]() {
|
||||
|
||||
},
|
||||
@@ -365,11 +359,12 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
|
||||
|
||||
if (refresh_graphics_ && !open_sheets_.empty()) {
|
||||
RETURN_IF_ERROR(
|
||||
rom()->bitmap_manager()[current_sheet_].ApplyPaletteWithTransparent(
|
||||
palette, edit_palette_sub_index_));
|
||||
rom()->UpdateBitmap(
|
||||
rom()->mutable_bitmap_manager()->mutable_bitmap(current_sheet_),
|
||||
true);
|
||||
rom()
|
||||
->mutable_gfx_sheets()
|
||||
->data()[current_sheet_]
|
||||
.ApplyPaletteWithTransparent(palette, edit_palette_sub_index_));
|
||||
Renderer::GetInstance().UpdateBitmap(
|
||||
&rom()->mutable_gfx_sheets()->data()[current_sheet_]);
|
||||
refresh_graphics_ = false;
|
||||
}
|
||||
}
|
||||
@@ -391,9 +386,9 @@ absl::Status GraphicsEditor::UpdateLinkGfxView() {
|
||||
link_canvas_.DrawGrid(16.0f);
|
||||
|
||||
int i = 0;
|
||||
for (auto [key, link_sheet] : *rom()->mutable_link_graphics()) {
|
||||
for (auto link_sheet : *rom()->mutable_link_graphics()) {
|
||||
int x_offset = 0;
|
||||
int y_offset = core::kTilesheetHeight * i * 4;
|
||||
int y_offset = gfx::kTilesheetHeight * i * 4;
|
||||
link_canvas_.DrawContextMenu(&link_sheet);
|
||||
link_canvas_.DrawBitmap(link_sheet, x_offset, y_offset, 4);
|
||||
i++;
|
||||
@@ -435,6 +430,10 @@ absl::Status GraphicsEditor::UpdateScadView() {
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_SizingStretchSame;
|
||||
|
||||
BEGIN_TABLE("#gfxEditTable", 4, kGfxEditFlags)
|
||||
SETUP_COLUMN("File Import (BIN, CGX, ROM)")
|
||||
SETUP_COLUMN("Palette (COL)")
|
||||
@@ -458,17 +457,18 @@ absl::Status GraphicsEditor::UpdateScadView() {
|
||||
|
||||
NEXT_COLUMN()
|
||||
if (super_donkey_) {
|
||||
if (refresh_graphics_) {
|
||||
for (int i = 0; i < graphics_bin_.size(); i++) {
|
||||
status_ = graphics_bin_[i].ApplyPalette(
|
||||
col_file_palette_group_[current_palette_index_]);
|
||||
rom()->UpdateBitmap(&graphics_bin_[i]);
|
||||
}
|
||||
refresh_graphics_ = false;
|
||||
}
|
||||
// TODO: Implement the Super Donkey 1 graphics decompression
|
||||
// if (refresh_graphics_) {
|
||||
// for (int i = 0; i < kNumGfxSheets; i++) {
|
||||
// status_ = graphics_bin_[i].ApplyPalette(
|
||||
// col_file_palette_group_[current_palette_index_]);
|
||||
// Renderer::GetInstance().UpdateBitmap(&graphics_bin_[i]);
|
||||
// }
|
||||
// refresh_graphics_ = false;
|
||||
// }
|
||||
// Load the full graphics space from `super_donkey_1.bin`
|
||||
gui::GraphicsBinCanvasPipeline(0x100, 0x40, 0x20, num_sheets_to_load_, 3,
|
||||
super_donkey_, graphics_bin_);
|
||||
// gui::GraphicsBinCanvasPipeline(0x100, 0x40, 0x20, num_sheets_to_load_, 3,
|
||||
// super_donkey_, graphics_bin_);
|
||||
} else if (cgx_loaded_ && col_file_) {
|
||||
// Load the CGX graphics
|
||||
gui::BitmapCanvasPipeline(import_canvas_, cgx_bitmap_, 0x100, 16384, 0x20,
|
||||
@@ -485,6 +485,11 @@ absl::Status GraphicsEditor::UpdateScadView() {
|
||||
}
|
||||
|
||||
absl::Status GraphicsEditor::DrawToolset() {
|
||||
static constexpr absl::string_view kGfxToolsetColumnNames[] = {
|
||||
"#memoryEditor",
|
||||
"##separator_gfx1",
|
||||
};
|
||||
|
||||
if (ImGui::BeginTable("GraphicsToolset", 2, ImGuiTableFlags_SizingFixedFit,
|
||||
ImVec2(0, 0))) {
|
||||
for (const auto& name : kGfxToolsetColumnNames)
|
||||
@@ -535,7 +540,7 @@ absl::Status GraphicsEditor::DrawCgxImport() {
|
||||
cgx_bitmap_.Create(0x80, 0x200, 8, decoded_cgx_);
|
||||
if (col_file_) {
|
||||
cgx_bitmap_.ApplyPalette(decoded_col_);
|
||||
rom()->RenderBitmap(&cgx_bitmap_);
|
||||
Renderer::GetInstance().RenderBitmap(&cgx_bitmap_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,7 +575,7 @@ absl::Status GraphicsEditor::DrawScrImport() {
|
||||
scr_bitmap_.Create(0x100, 0x100, 8, decoded_scr_data_);
|
||||
if (scr_loaded_) {
|
||||
scr_bitmap_.ApplyPalette(decoded_col_);
|
||||
rom()->RenderBitmap(&scr_bitmap_);
|
||||
Renderer::GetInstance().RenderBitmap(&scr_bitmap_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,7 +599,7 @@ absl::Status GraphicsEditor::DrawPaletteControls() {
|
||||
/*z3_load=*/false);
|
||||
auto col_data_ = gfx::GetColFileData(temp_rom_.data());
|
||||
if (col_file_palette_group_.size() != 0) {
|
||||
col_file_palette_group_.Clear();
|
||||
col_file_palette_group_.clear();
|
||||
}
|
||||
auto col_file_palette_group_status =
|
||||
gfx::CreatePaletteGroupFromColFile(col_data_);
|
||||
@@ -707,7 +712,8 @@ absl::Status GraphicsEditor::DrawClipboardImport() {
|
||||
if (Button("Paste From Clipboard")) {
|
||||
const char* text = ImGui::GetClipboardText();
|
||||
if (text) {
|
||||
const auto clipboard_data = Bytes(text, text + strlen(text));
|
||||
const auto clipboard_data =
|
||||
std::vector<uint8_t>(text, text + strlen(text));
|
||||
ImGui::MemFree((void*)text);
|
||||
status_ = temp_rom_.LoadFromBytes(clipboard_data);
|
||||
is_open_ = true;
|
||||
@@ -762,7 +768,7 @@ absl::Status GraphicsEditor::DecompressImportData(int size) {
|
||||
temp_rom_.data(), current_offset_, size))
|
||||
|
||||
auto converted_sheet = gfx::SnesTo8bppSheet(import_data_, 3);
|
||||
bin_bitmap_.Create(core::kTilesheetWidth, 0x2000, core::kTilesheetDepth,
|
||||
bin_bitmap_.Create(gfx::kTilesheetWidth, 0x2000, gfx::kTilesheetDepth,
|
||||
converted_sheet);
|
||||
|
||||
if (rom()->is_loaded()) {
|
||||
@@ -775,7 +781,7 @@ absl::Status GraphicsEditor::DecompressImportData(int size) {
|
||||
}
|
||||
}
|
||||
|
||||
rom()->RenderBitmap(&bin_bitmap_);
|
||||
Renderer::GetInstance().RenderBitmap(&bin_bitmap_);
|
||||
gfx_loaded_ = true;
|
||||
|
||||
return absl::OkStatus();
|
||||
@@ -790,11 +796,10 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
|
||||
auto decompressed_data,
|
||||
gfx::lc_lz2::DecompressV2(temp_rom_.data(), offset_value, 0x1000))
|
||||
auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
|
||||
graphics_bin_[i] =
|
||||
gfx::Bitmap(core::kTilesheetWidth, core::kTilesheetHeight,
|
||||
core::kTilesheetDepth, converted_sheet);
|
||||
gfx_sheets_[i] = gfx::Bitmap(gfx::kTilesheetWidth, gfx::kTilesheetHeight,
|
||||
gfx::kTilesheetDepth, converted_sheet);
|
||||
if (col_file_) {
|
||||
status_ = graphics_bin_[i].ApplyPalette(
|
||||
status_ = gfx_sheets_[i].ApplyPalette(
|
||||
col_file_palette_group_[current_palette_index_]);
|
||||
} else {
|
||||
// ROM palette
|
||||
@@ -802,10 +807,10 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
|
||||
auto palette_group = rom()->palette_group().get_group(
|
||||
kPaletteGroupAddressesKeys[current_palette_]);
|
||||
z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
|
||||
status_ = graphics_bin_[i].ApplyPalette(z3_rom_palette_);
|
||||
status_ = gfx_sheets_[i].ApplyPalette(z3_rom_palette_);
|
||||
}
|
||||
|
||||
rom()->RenderBitmap(&graphics_bin_[i]);
|
||||
Renderer::GetInstance().RenderBitmap(&gfx_sheets_[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -816,21 +821,20 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
|
||||
auto decompressed_data,
|
||||
gfx::lc_lz2::DecompressV2(temp_rom_.data(), offset_value, 0x1000))
|
||||
auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
|
||||
graphics_bin_[i] =
|
||||
gfx::Bitmap(core::kTilesheetWidth, core::kTilesheetHeight,
|
||||
core::kTilesheetDepth, converted_sheet);
|
||||
gfx_sheets_[i] = gfx::Bitmap(gfx::kTilesheetWidth, gfx::kTilesheetHeight,
|
||||
gfx::kTilesheetDepth, converted_sheet);
|
||||
if (col_file_) {
|
||||
status_ = graphics_bin_[i].ApplyPalette(
|
||||
status_ = gfx_sheets_[i].ApplyPalette(
|
||||
col_file_palette_group_[current_palette_index_]);
|
||||
} else {
|
||||
// ROM palette
|
||||
auto palette_group = rom()->palette_group().get_group(
|
||||
kPaletteGroupAddressesKeys[current_palette_]);
|
||||
z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
|
||||
status_ = graphics_bin_[i].ApplyPalette(z3_rom_palette_);
|
||||
status_ = gfx_sheets_[i].ApplyPalette(z3_rom_palette_);
|
||||
}
|
||||
|
||||
rom()->RenderBitmap(&graphics_bin_[i]);
|
||||
Renderer::GetInstance().RenderBitmap(&gfx_sheets_[i]);
|
||||
i++;
|
||||
}
|
||||
super_donkey_ = true;
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
#ifndef YAZE_APP_EDITOR_GRAPHICS_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_GRAPHICS_EDITOR_H
|
||||
|
||||
#include "ImGuiFileDialog/ImGuiFileDialog.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/misc/cpp/imgui_stdlib.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
#include <stack>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/asset_browser.h"
|
||||
#include "app/gui/modules/asset_browser.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -167,8 +164,8 @@ class GraphicsEditor : public SharedRom, public Editor {
|
||||
MemoryEditor cgx_memory_editor_;
|
||||
MemoryEditor col_memory_editor_;
|
||||
PaletteEditor palette_editor_;
|
||||
Bytes import_data_;
|
||||
Bytes graphics_buffer_;
|
||||
std::vector<uint8_t> import_data_;
|
||||
std::vector<uint8_t> graphics_buffer_;
|
||||
std::vector<uint8_t> decoded_cgx_;
|
||||
std::vector<uint8_t> cgx_data_;
|
||||
std::vector<uint8_t> extra_cgx_data_;
|
||||
@@ -179,9 +176,8 @@ class GraphicsEditor : public SharedRom, public Editor {
|
||||
gfx::Bitmap scr_bitmap_;
|
||||
gfx::Bitmap bin_bitmap_;
|
||||
gfx::Bitmap link_full_sheet_;
|
||||
gfx::BitmapTable graphics_bin_;
|
||||
gfx::BitmapTable clipboard_graphics_bin_;
|
||||
gfx::BitmapTable link_graphics_;
|
||||
std::array<gfx::Bitmap, kNumGfxSheets> gfx_sheets_;
|
||||
|
||||
gfx::PaletteGroup col_file_palette_group_;
|
||||
gfx::SnesPalette z3_rom_palette_;
|
||||
gfx::SnesPalette col_file_palette_;
|
||||
@@ -189,11 +185,12 @@ class GraphicsEditor : public SharedRom, public Editor {
|
||||
gui::Canvas import_canvas_;
|
||||
gui::Canvas scr_canvas_;
|
||||
gui::Canvas super_donkey_canvas_;
|
||||
gui::Canvas graphics_bin_canvas_;
|
||||
gui::Canvas current_sheet_canvas_{"CurrentSheetCanvas", ImVec2(0x80, 0x20),
|
||||
gui::CanvasGridSize::k8x8};
|
||||
gui::Canvas link_canvas_{
|
||||
"LinkCanvas",
|
||||
ImVec2(core::kTilesheetWidth * 4, core::kTilesheetHeight * 0x10 * 4),
|
||||
ImVec2(gfx::kTilesheetWidth * 4, gfx::kTilesheetHeight * 0x10 * 4),
|
||||
gui::CanvasGridSize::k16x16};
|
||||
absl::Status status_;
|
||||
};
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
#include "palette_editor.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -86,6 +84,93 @@ static inline float color_saturate(float f) {
|
||||
0.5f)) // Saturated, always output 0..255
|
||||
} // namespace
|
||||
|
||||
absl::Status DisplayPalette(gfx::SnesPalette& palette, bool loaded) {
|
||||
static ImVec4 color = ImVec4(0, 0, 0, 255.f);
|
||||
static ImVec4 current_palette[256] = {};
|
||||
ImGuiColorEditFlags misc_flags = ImGuiColorEditFlags_AlphaPreview |
|
||||
ImGuiColorEditFlags_NoDragDrop |
|
||||
ImGuiColorEditFlags_NoOptions;
|
||||
|
||||
// Generate a default palette. The palette will persist and can be edited.
|
||||
static bool init = false;
|
||||
if (loaded && !init) {
|
||||
for (int n = 0; n < palette.size(); n++) {
|
||||
ASSIGN_OR_RETURN(auto color, palette.GetColor(n));
|
||||
current_palette[n].x = color.rgb().x / 255;
|
||||
current_palette[n].y = color.rgb().y / 255;
|
||||
current_palette[n].z = color.rgb().z / 255;
|
||||
current_palette[n].w = 255; // Alpha
|
||||
}
|
||||
init = true;
|
||||
}
|
||||
|
||||
static ImVec4 backup_color;
|
||||
bool open_popup = ColorButton("MyColor##3b", color, misc_flags);
|
||||
SameLine(0, GetStyle().ItemInnerSpacing.x);
|
||||
open_popup |= Button("Palette");
|
||||
if (open_popup) {
|
||||
OpenPopup("mypicker");
|
||||
backup_color = color;
|
||||
}
|
||||
|
||||
if (BeginPopup("mypicker")) {
|
||||
TEXT_WITH_SEPARATOR("Current Overworld Palette");
|
||||
ColorPicker4("##picker", (float*)&color,
|
||||
misc_flags | ImGuiColorEditFlags_NoSidePreview |
|
||||
ImGuiColorEditFlags_NoSmallPreview);
|
||||
SameLine();
|
||||
|
||||
BeginGroup(); // Lock X position
|
||||
Text("Current ==>");
|
||||
SameLine();
|
||||
Text("Previous");
|
||||
|
||||
if (Button("Update Map Palette")) {
|
||||
}
|
||||
|
||||
ColorButton(
|
||||
"##current", color,
|
||||
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
|
||||
ImVec2(60, 40));
|
||||
SameLine();
|
||||
|
||||
if (ColorButton(
|
||||
"##previous", backup_color,
|
||||
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
|
||||
ImVec2(60, 40)))
|
||||
color = backup_color;
|
||||
|
||||
// List of Colors in Overworld Palette
|
||||
Separator();
|
||||
Text("Palette");
|
||||
for (int n = 0; n < IM_ARRAYSIZE(current_palette); n++) {
|
||||
PushID(n);
|
||||
if ((n % 8) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
|
||||
|
||||
if (ColorButton("##palette", current_palette[n], kPalButtonFlags2,
|
||||
ImVec2(20, 20)))
|
||||
color = ImVec4(current_palette[n].x, current_palette[n].y,
|
||||
current_palette[n].z, color.w); // Preserve alpha!
|
||||
|
||||
if (BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload =
|
||||
AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
|
||||
memcpy((float*)¤t_palette[n], payload->Data, sizeof(float) * 3);
|
||||
if (const ImGuiPayload* payload =
|
||||
AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
|
||||
memcpy((float*)¤t_palette[n], payload->Data, sizeof(float) * 4);
|
||||
EndDragDropTarget();
|
||||
}
|
||||
|
||||
PopID();
|
||||
}
|
||||
EndGroup();
|
||||
EndPopup();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::Update() {
|
||||
if (rom()->is_loaded()) {
|
||||
// Initialize the labels
|
||||
@@ -348,7 +433,7 @@ absl::Status PaletteEditor::HandleColorPopup(gfx::SnesPalette& palette, int i,
|
||||
|
||||
// SNES Format
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "$%04X",
|
||||
ConvertRGBtoSNES(ImVec4(col[0], col[1], col[2], 1.0f)));
|
||||
ConvertRgbToSnes(ImVec4(col[0], col[1], col[2], 1.0f)));
|
||||
if (Selectable(buf)) SetClipboardText(buf);
|
||||
|
||||
EndPopup();
|
||||
@@ -358,84 +443,6 @@ absl::Status PaletteEditor::HandleColorPopup(gfx::SnesPalette& palette, int i,
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void PaletteEditor::DisplayPalette(gfx::SnesPalette& palette, bool loaded) {
|
||||
static ImVec4 color = ImVec4(0, 0, 0, 255.f);
|
||||
ImGuiColorEditFlags misc_flags = ImGuiColorEditFlags_AlphaPreview |
|
||||
ImGuiColorEditFlags_NoDragDrop |
|
||||
ImGuiColorEditFlags_NoOptions;
|
||||
|
||||
// Generate a default palette. The palette will persist and can be edited.
|
||||
static bool init = false;
|
||||
if (loaded && !init) {
|
||||
status_ = InitializeSavedPalette(palette);
|
||||
init = true;
|
||||
}
|
||||
|
||||
static ImVec4 backup_color;
|
||||
bool open_popup = ColorButton("MyColor##3b", color, misc_flags);
|
||||
SameLine(0, GetStyle().ItemInnerSpacing.x);
|
||||
open_popup |= Button("Palette");
|
||||
if (open_popup) {
|
||||
OpenPopup("mypicker");
|
||||
backup_color = color;
|
||||
}
|
||||
|
||||
if (BeginPopup("mypicker")) {
|
||||
TEXT_WITH_SEPARATOR("Current Overworld Palette");
|
||||
ColorPicker4("##picker", (float*)&color,
|
||||
misc_flags | ImGuiColorEditFlags_NoSidePreview |
|
||||
ImGuiColorEditFlags_NoSmallPreview);
|
||||
SameLine();
|
||||
|
||||
BeginGroup(); // Lock X position
|
||||
Text("Current ==>");
|
||||
SameLine();
|
||||
Text("Previous");
|
||||
|
||||
if (Button("Update Map Palette")) {
|
||||
}
|
||||
|
||||
ColorButton(
|
||||
"##current", color,
|
||||
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
|
||||
ImVec2(60, 40));
|
||||
SameLine();
|
||||
|
||||
if (ColorButton(
|
||||
"##previous", backup_color,
|
||||
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
|
||||
ImVec2(60, 40)))
|
||||
color = backup_color;
|
||||
|
||||
// List of Colors in Overworld Palette
|
||||
Separator();
|
||||
Text("Palette");
|
||||
for (int n = 0; n < IM_ARRAYSIZE(saved_palette_); n++) {
|
||||
PushID(n);
|
||||
if ((n % 8) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
|
||||
|
||||
if (ColorButton("##palette", saved_palette_[n], kPalButtonFlags2,
|
||||
ImVec2(20, 20)))
|
||||
color = ImVec4(saved_palette_[n].x, saved_palette_[n].y,
|
||||
saved_palette_[n].z, color.w); // Preserve alpha!
|
||||
|
||||
if (BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload =
|
||||
AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
|
||||
memcpy((float*)&saved_palette_[n], payload->Data, sizeof(float) * 3);
|
||||
if (const ImGuiPayload* payload =
|
||||
AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
|
||||
memcpy((float*)&saved_palette_[n], payload->Data, sizeof(float) * 4);
|
||||
EndDragDropTarget();
|
||||
}
|
||||
|
||||
PopID();
|
||||
}
|
||||
EndGroup();
|
||||
EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::EditColorInPalette(gfx::SnesPalette& palette,
|
||||
int index) {
|
||||
if (index >= palette.size()) {
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
#ifndef YAZE_APP_EDITOR_PALETTE_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_PALETTE_EDITOR_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/editor/graphics/gfx_group_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gfx/snes_color.h"
|
||||
#include "app/rom.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -26,56 +28,51 @@ struct PaletteChange {
|
||||
|
||||
class PaletteEditorHistory {
|
||||
public:
|
||||
// Record a change in the palette editor
|
||||
void RecordChange(const std::string& groupName, size_t paletteIndex,
|
||||
size_t colorIndex, const gfx::SnesColor& originalColor,
|
||||
const gfx::SnesColor& newColor) {
|
||||
// Check size and remove the oldest if necessary
|
||||
if (recentChanges.size() >= maxHistorySize) {
|
||||
recentChanges.pop_front();
|
||||
void RecordChange(const std::string& group_name, size_t palette_index,
|
||||
size_t color_index, const gfx::SnesColor& original_color,
|
||||
const gfx::SnesColor& new_color) {
|
||||
if (recent_changes_.size() >= kMaxHistorySize) {
|
||||
recent_changes_.pop_front();
|
||||
}
|
||||
|
||||
// Push the new change
|
||||
recentChanges.push_back(
|
||||
{groupName, paletteIndex, colorIndex, originalColor, newColor});
|
||||
recent_changes_.push_back(
|
||||
{group_name, palette_index, color_index, original_color, new_color});
|
||||
}
|
||||
|
||||
// Get recent changes for display in the palette editor
|
||||
const std::deque<PaletteChange>& GetRecentChanges() const {
|
||||
return recentChanges;
|
||||
}
|
||||
|
||||
// Restore the original color
|
||||
gfx::SnesColor RestoreOriginalColor(const std::string& groupName,
|
||||
size_t paletteIndex,
|
||||
size_t colorIndex) const {
|
||||
for (const auto& change : recentChanges) {
|
||||
if (change.group_name == groupName &&
|
||||
change.palette_index == paletteIndex &&
|
||||
change.color_index == colorIndex) {
|
||||
gfx::SnesColor RestoreOriginalColor(const std::string& group_name,
|
||||
size_t palette_index,
|
||||
size_t color_index) const {
|
||||
for (const auto& change : recent_changes_) {
|
||||
if (change.group_name == group_name &&
|
||||
change.palette_index == palette_index &&
|
||||
change.color_index == color_index) {
|
||||
return change.original_color;
|
||||
}
|
||||
}
|
||||
// Handle error or return default (this is just an example,
|
||||
// handle as appropriate for your application)
|
||||
return gfx::SnesColor();
|
||||
}
|
||||
|
||||
auto size() const { return recentChanges.size(); }
|
||||
auto size() const { return recent_changes_.size(); }
|
||||
|
||||
gfx::SnesColor& GetModifiedColor(size_t index) {
|
||||
return recentChanges[index].new_color;
|
||||
return recent_changes_[index].new_color;
|
||||
}
|
||||
gfx::SnesColor& GetOriginalColor(size_t index) {
|
||||
return recentChanges[index].original_color;
|
||||
return recent_changes_[index].original_color;
|
||||
}
|
||||
|
||||
const std::deque<PaletteChange>& GetRecentChanges() const {
|
||||
return recent_changes_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::deque<PaletteChange> recentChanges;
|
||||
static const size_t maxHistorySize = 50; // or any other number you deem fit
|
||||
std::deque<PaletteChange> recent_changes_;
|
||||
static const size_t kMaxHistorySize = 50;
|
||||
};
|
||||
} // namespace palette_internal
|
||||
|
||||
absl::Status DisplayPalette(gfx::SnesPalette& palette, bool loaded);
|
||||
|
||||
/**
|
||||
* @class PaletteEditor
|
||||
* @brief Allows the user to view and edit in game palettes.
|
||||
@@ -101,7 +98,6 @@ class PaletteEditor : public SharedRom, public Editor {
|
||||
absl::Status EditColorInPalette(gfx::SnesPalette& palette, int index);
|
||||
absl::Status ResetColorToOriginal(gfx::SnesPalette& palette, int index,
|
||||
const gfx::SnesPalette& originalPalette);
|
||||
void DisplayPalette(gfx::SnesPalette& palette, bool loaded);
|
||||
absl::Status DrawPaletteGroup(int category, bool right_side = false);
|
||||
|
||||
void DrawCustomPalette();
|
||||
@@ -110,16 +106,6 @@ class PaletteEditor : public SharedRom, public Editor {
|
||||
|
||||
private:
|
||||
absl::Status HandleColorPopup(gfx::SnesPalette& palette, int i, int j, int n);
|
||||
absl::Status InitializeSavedPalette(const gfx::SnesPalette& palette) {
|
||||
for (int n = 0; n < palette.size(); n++) {
|
||||
ASSIGN_OR_RETURN(auto color, palette.GetColor(n));
|
||||
saved_palette_[n].x = color.rgb().x / 255;
|
||||
saved_palette_[n].y = color.rgb().y / 255;
|
||||
saved_palette_[n].z = color.rgb().z / 255;
|
||||
saved_palette_[n].w = 255; // Alpha
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status status_;
|
||||
gfx::SnesColor current_color_;
|
||||
@@ -137,4 +123,4 @@ class PaletteEditor : public SharedRom, public Editor {
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,44 +1,45 @@
|
||||
#include "screen_editor.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "app/core/common.h"
|
||||
#include "app/core/constants.h"
|
||||
#include "app/core/platform/file_dialog.h"
|
||||
#include "app/core/platform/renderer.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gfx/tilesheet.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/zelda3/dungeon/room.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
absl::Status ScreenEditor::Update() {
|
||||
TAB_BAR("##TabBar")
|
||||
TAB_ITEM("Dungeon Maps")
|
||||
if (rom()->is_loaded()) {
|
||||
DrawDungeonMapsEditor();
|
||||
}
|
||||
END_TAB_ITEM()
|
||||
DrawInventoryMenuEditor();
|
||||
DrawOverworldMapEditor();
|
||||
DrawTitleScreenEditor();
|
||||
DrawNamingScreenEditor();
|
||||
END_TAB_BAR()
|
||||
using core::Renderer;
|
||||
|
||||
return absl::OkStatus();
|
||||
constexpr uint32_t kRedPen = 0xFF0000FF;
|
||||
|
||||
absl::Status ScreenEditor::Update() {
|
||||
if (ImGui::BeginTabBar("##ScreenEditorTabBar")) {
|
||||
if (ImGui::BeginTabItem("Dungeon Maps")) {
|
||||
if (rom()->is_loaded()) {
|
||||
DrawDungeonMapsEditor();
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
DrawInventoryMenuEditor();
|
||||
DrawOverworldMapEditor();
|
||||
DrawTitleScreenEditor();
|
||||
DrawNamingScreenEditor();
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
return status_;
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawInventoryMenuEditor() {
|
||||
@@ -121,16 +122,16 @@ absl::Status ScreenEditor::LoadDungeonMaps() {
|
||||
int ptr,
|
||||
rom()->ReadWord(zelda3::screen::kDungeonMapRoomsPtr + (d * 2)));
|
||||
ASSIGN_OR_RETURN(
|
||||
int ptrGFX,
|
||||
rom()->ReadWord(zelda3::screen::kDungeonMapRoomsPtr + (d * 2)));
|
||||
ptr |= 0x0A0000; // Add bank to the short ptr
|
||||
ptrGFX |= 0x0A0000; // Add bank to the short ptr
|
||||
int pcPtr = core::SnesToPc(ptr); // Contains data for the next 25 rooms
|
||||
int pcPtrGFX =
|
||||
core::SnesToPc(ptrGFX); // Contains data for the next 25 rooms
|
||||
int ptr_gfx,
|
||||
rom()->ReadWord(zelda3::screen::kDungeonMapGfxPtr + (d * 2)));
|
||||
ptr |= 0x0A0000; // Add bank to the short ptr
|
||||
ptr_gfx |= 0x0A0000; // Add bank to the short ptr
|
||||
int pc_ptr = core::SnesToPc(ptr); // Contains data for the next 25 rooms
|
||||
int pc_ptr_gfx =
|
||||
core::SnesToPc(ptr_gfx); // Contains data for the next 25 rooms
|
||||
|
||||
ASSIGN_OR_RETURN(
|
||||
ushort bossRoomD,
|
||||
ushort boss_room_d,
|
||||
rom()->ReadWord(zelda3::screen::kDungeonMapBossRooms + (d * 2)));
|
||||
|
||||
ASSIGN_OR_RETURN(
|
||||
@@ -157,14 +158,13 @@ absl::Status ScreenEditor::LoadDungeonMaps() {
|
||||
|
||||
// for each room on the floor
|
||||
for (int j = 0; j < 25; j++) {
|
||||
// rdata[j] = 0x0F;
|
||||
gdata[j] = 0xFF;
|
||||
rdata[j] = rom()->data()[pcPtr + j + (i * 25)]; // Set the rooms
|
||||
rdata[j] = rom()->data()[pc_ptr + j + (i * 25)]; // Set the rooms
|
||||
|
||||
if (rdata[j] == 0x0F) {
|
||||
gdata[j] = 0xFF;
|
||||
} else {
|
||||
gdata[j] = rom()->data()[pcPtrGFX++];
|
||||
gdata[j] = rom()->data()[pc_ptr_gfx++];
|
||||
}
|
||||
|
||||
std::string label = core::UppercaseHexByte(rdata[j]);
|
||||
@@ -175,7 +175,7 @@ absl::Status ScreenEditor::LoadDungeonMaps() {
|
||||
current_floor_rooms_d.push_back(rdata); // Add new floor data
|
||||
}
|
||||
|
||||
dungeon_maps_.emplace_back(bossRoomD, nbr_floor_d, nbr_basement_d,
|
||||
dungeon_maps_.emplace_back(boss_room_d, nbr_floor_d, nbr_basement_d,
|
||||
current_floor_rooms_d, current_floor_gfx_d);
|
||||
}
|
||||
|
||||
@@ -185,23 +185,19 @@ absl::Status ScreenEditor::LoadDungeonMaps() {
|
||||
absl::Status ScreenEditor::SaveDungeonMaps() {
|
||||
for (int d = 0; d < 14; d++) {
|
||||
int ptr = zelda3::screen::kDungeonMapRoomsPtr + (d * 2);
|
||||
int ptrGFX = zelda3::screen::kDungeonMapGfxPtr + (d * 2);
|
||||
int pcPtr = core::SnesToPc(ptr);
|
||||
int pcPtrGFX = core::SnesToPc(ptrGFX);
|
||||
int ptr_gfx = zelda3::screen::kDungeonMapGfxPtr + (d * 2);
|
||||
int pc_ptr = core::SnesToPc(ptr);
|
||||
int pc_ptr_gfx = core::SnesToPc(ptr_gfx);
|
||||
|
||||
const int nbr_floors = dungeon_maps_[d].nbr_of_floor;
|
||||
const int nbr_basements = dungeon_maps_[d].nbr_of_basement;
|
||||
for (int i = 0; i < nbr_floors + nbr_basements; i++) {
|
||||
for (int j = 0; j < 25; j++) {
|
||||
// rom()->data()[pcPtr + j + (i * 25)] =
|
||||
// dungeon_maps_[d].floor_rooms[i][j];
|
||||
// rom()->data()[pcPtrGFX++] = dungeon_maps_[d].floor_gfx[i][j];
|
||||
|
||||
RETURN_IF_ERROR(rom()->WriteByte(ptr + j + (i * 25),
|
||||
RETURN_IF_ERROR(rom()->WriteByte(pc_ptr + j + (i * 25),
|
||||
dungeon_maps_[d].floor_rooms[i][j]));
|
||||
RETURN_IF_ERROR(rom()->WriteByte(ptrGFX + j + (i * 25),
|
||||
RETURN_IF_ERROR(rom()->WriteByte(pc_ptr_gfx + j + (i * 25),
|
||||
dungeon_maps_[d].floor_gfx[i][j]));
|
||||
pcPtrGFX++;
|
||||
pc_ptr_gfx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,7 +205,8 @@ absl::Status ScreenEditor::SaveDungeonMaps() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status ScreenEditor::LoadDungeonMapTile16() {
|
||||
absl::Status ScreenEditor::LoadDungeonMapTile16(
|
||||
const std::vector<uint8_t>& gfx_data, bool bin_mode) {
|
||||
tile16_sheet_.Init(256, 192, gfx::TileType::Tile16);
|
||||
|
||||
for (int i = 0; i < 186; i++) {
|
||||
@@ -230,69 +227,99 @@ absl::Status ScreenEditor::LoadDungeonMapTile16() {
|
||||
ASSIGN_OR_RETURN(auto br, rom()->ReadWord(addr + 6 + (i * 8)));
|
||||
gfx::TileInfo t4 = gfx::WordToTileInfo(br); // Bottom right
|
||||
|
||||
tile16_sheet_.ComposeTile16(rom()->graphics_buffer(), t1, t2, t3, t4);
|
||||
int sheet_offset = 212;
|
||||
if (bin_mode) {
|
||||
sheet_offset = 0;
|
||||
}
|
||||
tile16_sheet_.ComposeTile16(gfx_data, t1, t2, t3, t4, sheet_offset);
|
||||
}
|
||||
|
||||
RETURN_IF_ERROR(tile16_sheet_.mutable_bitmap()->ApplyPalette(
|
||||
*rom()->mutable_dungeon_palette(3)));
|
||||
rom()->RenderBitmap(&*tile16_sheet_.mutable_bitmap().get());
|
||||
Renderer::GetInstance().RenderBitmap(&*tile16_sheet_.mutable_bitmap().get());
|
||||
|
||||
for (int i = 0; i < tile16_sheet_.num_tiles(); ++i) {
|
||||
if (tile16_individual_.count(i) == 0) {
|
||||
auto tile = tile16_sheet_.GetTile16(i);
|
||||
tile16_individual_[i] = tile;
|
||||
rom()->RenderBitmap(&tile16_individual_[i]);
|
||||
}
|
||||
auto tile = tile16_sheet_.GetTile16(i);
|
||||
tile16_individual_[i] = tile;
|
||||
RETURN_IF_ERROR(
|
||||
tile16_individual_[i].ApplyPalette(*rom()->mutable_dungeon_palette(3)));
|
||||
Renderer::GetInstance().RenderBitmap(&tile16_individual_[i]);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status ScreenEditor::SaveDungeonMapTile16() {
|
||||
for (int i = 0; i < 186; i++) {
|
||||
int addr = zelda3::screen::kDungeonMapTile16;
|
||||
if (rom()->data()[zelda3::screen::kDungeonMapExpCheck] != 0xB9) {
|
||||
addr = zelda3::screen::kDungeonMapTile16Expanded;
|
||||
}
|
||||
|
||||
gfx::TileInfo t1 = tile16_sheet_.tile_info()[i].tiles[0];
|
||||
gfx::TileInfo t2 = tile16_sheet_.tile_info()[i].tiles[1];
|
||||
gfx::TileInfo t3 = tile16_sheet_.tile_info()[i].tiles[2];
|
||||
gfx::TileInfo t4 = tile16_sheet_.tile_info()[i].tiles[3];
|
||||
|
||||
auto tl = gfx::TileInfoToWord(t1);
|
||||
RETURN_IF_ERROR(rom()->WriteWord(addr + (i * 8), tl));
|
||||
|
||||
auto tr = gfx::TileInfoToWord(t2);
|
||||
RETURN_IF_ERROR(rom()->WriteWord(addr + 2 + (i * 8), tr));
|
||||
|
||||
auto bl = gfx::TileInfoToWord(t3);
|
||||
RETURN_IF_ERROR(rom()->WriteWord(addr + 4 + (i * 8), bl));
|
||||
|
||||
auto br = gfx::TileInfoToWord(t4);
|
||||
RETURN_IF_ERROR(rom()->WriteWord(addr + 6 + (i * 8), br));
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawDungeonMapsTabs() {
|
||||
auto current_dungeon = dungeon_maps_[selected_dungeon];
|
||||
auto& current_dungeon = dungeon_maps_[selected_dungeon];
|
||||
if (ImGui::BeginTabBar("##DungeonMapTabs")) {
|
||||
auto nbr_floors =
|
||||
current_dungeon.nbr_of_floor + current_dungeon.nbr_of_basement;
|
||||
for (int i = 0; i < nbr_floors; i++) {
|
||||
std::string tab_name = absl::StrFormat("Floor %d", i + 1);
|
||||
if (i >= current_dungeon.nbr_of_floor) {
|
||||
tab_name = absl::StrFormat("Basement %d",
|
||||
i - current_dungeon.nbr_of_floor + 1);
|
||||
int basement_num = current_dungeon.nbr_of_basement - i;
|
||||
std::string tab_name = absl::StrFormat("Basement %d", basement_num);
|
||||
if (i >= current_dungeon.nbr_of_basement) {
|
||||
tab_name = absl::StrFormat("Floor %d",
|
||||
i - current_dungeon.nbr_of_basement + 1);
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem(tab_name.c_str())) {
|
||||
floor_number = i;
|
||||
// screen_canvas_.LoadCustomLabels(dungeon_map_labels_[selected_dungeon]);
|
||||
// screen_canvas_.set_current_labels(floor_number);
|
||||
screen_canvas_.DrawBackground(ImVec2(325, 325));
|
||||
screen_canvas_.DrawTileSelector(64.f);
|
||||
|
||||
auto boss_room = current_dungeon.boss_room;
|
||||
for (int j = 0; j < 25; j++) {
|
||||
if (current_dungeon.floor_rooms[floor_number][j] != 0x0F) {
|
||||
int tile16_id = current_dungeon.floor_rooms[floor_number][j];
|
||||
int tile_x = (tile16_id % 16) * 16;
|
||||
int tile_y = (tile16_id / 16) * 16;
|
||||
int tile16_id = current_dungeon.floor_gfx[floor_number][j];
|
||||
int posX = ((j % 5) * 32);
|
||||
int posY = ((j / 5) * 32);
|
||||
|
||||
if (tile16_individual_.count(tile16_id) == 0) {
|
||||
auto tile = tile16_sheet_.GetTile16(tile16_id);
|
||||
std::cout << "Tile16: " << tile16_id << std::endl;
|
||||
rom()->RenderBitmap(&tile);
|
||||
tile16_individual_[tile16_id] = tile;
|
||||
tile16_individual_[tile16_id] =
|
||||
tile16_sheet_.GetTile16(tile16_id);
|
||||
Renderer::GetInstance().RenderBitmap(
|
||||
&tile16_individual_[tile16_id]);
|
||||
}
|
||||
screen_canvas_.DrawBitmap(tile16_individual_[tile16_id], (posX * 2),
|
||||
(posY * 2), 4.0f);
|
||||
|
||||
if (current_dungeon.floor_rooms[floor_number][j] == boss_room) {
|
||||
screen_canvas_.DrawOutlineWithColor((posX * 2), (posY * 2), 64,
|
||||
64, core::kRedPen);
|
||||
64, kRedPen);
|
||||
}
|
||||
|
||||
std::string label =
|
||||
dungeon_map_labels_[selected_dungeon][floor_number][j];
|
||||
screen_canvas_.DrawText(label, (posX * 2), (posY * 2));
|
||||
std::string gfx_id = core::UppercaseHexByte(tile16_id);
|
||||
screen_canvas_.DrawText(gfx_id, (posX * 2), (posY * 2) + 16);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,31 +343,76 @@ void ScreenEditor::DrawDungeonMapsTabs() {
|
||||
|
||||
gui::InputHexWord("Boss Room", ¤t_dungeon.boss_room);
|
||||
|
||||
if (ImGui::Button("Copy Floor", ImVec2(100, 0))) {
|
||||
const ImVec2 button_size = ImVec2(130, 0);
|
||||
|
||||
// Add Floor Button
|
||||
if (ImGui::Button("Add Floor", button_size) &&
|
||||
current_dungeon.nbr_of_floor < 8) {
|
||||
current_dungeon.nbr_of_floor++;
|
||||
dungeon_map_labels_[selected_dungeon].emplace_back();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Remove Floor", button_size) &&
|
||||
current_dungeon.nbr_of_floor > 0) {
|
||||
current_dungeon.nbr_of_floor--;
|
||||
dungeon_map_labels_[selected_dungeon].pop_back();
|
||||
}
|
||||
|
||||
// Add Basement Button
|
||||
if (ImGui::Button("Add Basement", button_size) &&
|
||||
current_dungeon.nbr_of_basement < 8) {
|
||||
current_dungeon.nbr_of_basement++;
|
||||
dungeon_map_labels_[selected_dungeon].emplace_back();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Remove Basement", button_size) &&
|
||||
current_dungeon.nbr_of_basement > 0) {
|
||||
current_dungeon.nbr_of_basement--;
|
||||
dungeon_map_labels_[selected_dungeon].pop_back();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Copy Floor", button_size)) {
|
||||
copy_button_pressed = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Paste Floor", ImVec2(100, 0))) {
|
||||
if (ImGui::Button("Paste Floor", button_size)) {
|
||||
paste_button_pressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawDungeonMapsEditor() {
|
||||
if (!dungeon_maps_loaded_) {
|
||||
if (LoadDungeonMaps().ok()) {
|
||||
if (LoadDungeonMapTile16().ok()) {
|
||||
auto bitmap_manager = rom()->mutable_bitmap_manager();
|
||||
sheets_.emplace(0, *bitmap_manager->mutable_bitmap(212));
|
||||
sheets_.emplace(1, *bitmap_manager->mutable_bitmap(213));
|
||||
sheets_.emplace(2, *bitmap_manager->mutable_bitmap(214));
|
||||
sheets_.emplace(3, *bitmap_manager->mutable_bitmap(215));
|
||||
dungeon_maps_loaded_ = true;
|
||||
} else {
|
||||
ImGui::Text("Failed to load dungeon map tile16");
|
||||
}
|
||||
} else {
|
||||
if (!LoadDungeonMaps().ok()) {
|
||||
ImGui::Text("Failed to load dungeon maps");
|
||||
}
|
||||
|
||||
if (LoadDungeonMapTile16(rom()->graphics_buffer()).ok()) {
|
||||
// TODO: Load roomset gfx based on dungeon ID
|
||||
sheets_.emplace(0, rom()->gfx_sheets()[212]);
|
||||
sheets_.emplace(1, rom()->gfx_sheets()[213]);
|
||||
sheets_.emplace(2, rom()->gfx_sheets()[214]);
|
||||
sheets_.emplace(3, rom()->gfx_sheets()[215]);
|
||||
dungeon_maps_loaded_ = true;
|
||||
} else {
|
||||
ImGui::Text("Failed to load dungeon map tile16");
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("##DungeonMapToolset", 2, ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableSetupColumn("Draw Mode");
|
||||
ImGui::TableSetupColumn("Edit Mode");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(ICON_MD_DRAW)) {
|
||||
current_mode_ = EditingMode::DRAW;
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(ICON_MD_EDIT)) {
|
||||
current_mode_ = EditingMode::EDIT;
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
static std::vector<std::string> dungeon_names = {
|
||||
@@ -350,7 +422,10 @@ void ScreenEditor::DrawDungeonMapsEditor() {
|
||||
"Thieves' Town", "Ice Palace", "Misery Mire",
|
||||
"Turtle Rock", "Ganon's Tower"};
|
||||
|
||||
if (ImGui::BeginTable("DungeonMapsTable", 4, ImGuiTableFlags_Resizable)) {
|
||||
if (ImGui::BeginTable("DungeonMapsTable", 4,
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Hideable)) {
|
||||
ImGui::TableSetupColumn("Dungeon");
|
||||
ImGui::TableSetupColumn("Map");
|
||||
ImGui::TableSetupColumn("Rooms Gfx");
|
||||
@@ -384,32 +459,113 @@ void ScreenEditor::DrawDungeonMapsEditor() {
|
||||
if (!tilesheet_canvas_.points().empty()) {
|
||||
selected_tile16_ = tilesheet_canvas_.points().front().x / 32 +
|
||||
(tilesheet_canvas_.points().front().y / 32) * 16;
|
||||
current_tile16_info = tile16_sheet_.tile_info().at(selected_tile16_);
|
||||
|
||||
// Draw the selected tile
|
||||
if (!screen_canvas_.points().empty()) {
|
||||
dungeon_maps_[selected_dungeon]
|
||||
.floor_gfx[floor_number][selected_room] = selected_tile16_;
|
||||
tilesheet_canvas_.mutable_points()->clear();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
current_tile_canvas_.DrawBackground(ImVec2(64 * 2 + 2, 64 * 2 + 4));
|
||||
current_tile_canvas_.DrawContextMenu();
|
||||
current_tile_canvas_.DrawBitmap(tile16_individual_[selected_tile16_], 2,
|
||||
4.0f);
|
||||
current_tile_canvas_.DrawGrid(16.f);
|
||||
current_tile_canvas_.DrawOverlay();
|
||||
|
||||
gui::InputTileInfo("TL", ¤t_tile16_info.tiles[0]);
|
||||
ImGui::SameLine();
|
||||
gui::InputTileInfo("TR", ¤t_tile16_info.tiles[1]);
|
||||
gui::InputTileInfo("BL", ¤t_tile16_info.tiles[2]);
|
||||
ImGui::SameLine();
|
||||
gui::InputTileInfo("BR", ¤t_tile16_info.tiles[3]);
|
||||
|
||||
if (ImGui::Button("Modify Tile16")) {
|
||||
tile16_sheet_.ModifyTile16(
|
||||
rom()->graphics_buffer(), current_tile16_info.tiles[0],
|
||||
current_tile16_info.tiles[1], current_tile16_info.tiles[2],
|
||||
current_tile16_info.tiles[3], selected_tile16_, 212);
|
||||
tile16_individual_[selected_tile16_] =
|
||||
tile16_sheet_.GetTile16(selected_tile16_);
|
||||
RETURN_VOID_IF_ERROR(tile16_individual_[selected_tile16_].ApplyPalette(
|
||||
*rom()->mutable_dungeon_palette(3)));
|
||||
Renderer::GetInstance().RenderBitmap(
|
||||
&tile16_individual_[selected_tile16_]);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
tilemap_canvas_.DrawBackground(ImVec2(128 * 2 + 2, (192 * 2) + 4));
|
||||
tilemap_canvas_.DrawBackground();
|
||||
tilemap_canvas_.DrawContextMenu();
|
||||
if (tilemap_canvas_.DrawTileSelector(16.f)) {
|
||||
// Get the tile8 ID to use for the tile16 drawing above
|
||||
selected_tile8_ = tilemap_canvas_.GetTileIdFromMousePos();
|
||||
}
|
||||
tilemap_canvas_.DrawBitmapTable(sheets_);
|
||||
tilemap_canvas_.DrawGrid();
|
||||
tilemap_canvas_.DrawOverlay();
|
||||
|
||||
ImGui::Text("Selected tile8: %d", selected_tile8_);
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("For use with custom inserted graphics assembly patches.");
|
||||
if (ImGui::Button("Load GFX from BIN file")) LoadBinaryGfx();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenEditor::LoadBinaryGfx() {
|
||||
std::string bin_file = core::FileDialogWrapper::ShowOpenFileDialog();
|
||||
if (!bin_file.empty()) {
|
||||
std::ifstream file(bin_file, std::ios::binary);
|
||||
if (file.is_open()) {
|
||||
// Read the gfx data into a buffer
|
||||
std::vector<uint8_t> bin_data((std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
auto converted_bin = gfx::SnesTo8bppSheet(bin_data, 4, 4);
|
||||
gfx_bin_data_ = converted_bin;
|
||||
tile16_sheet_.clear();
|
||||
if (LoadDungeonMapTile16(converted_bin, true).ok()) {
|
||||
sheets_.clear();
|
||||
std::vector<std::vector<uint8_t>> gfx_sheets;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
gfx_sheets.emplace_back(converted_bin.begin() + (i * 0x1000),
|
||||
converted_bin.begin() + ((i + 1) * 0x1000));
|
||||
sheets_.emplace(i, gfx::Bitmap(128, 32, 8, gfx_sheets[i]));
|
||||
sheets_[i].ApplyPalette(*rom()->mutable_dungeon_palette(3));
|
||||
Renderer::GetInstance().RenderBitmap(&sheets_[i]);
|
||||
}
|
||||
binary_gfx_loaded_ = true;
|
||||
} else {
|
||||
status_ = absl::InternalError("Failed to load dungeon map tile16");
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawTitleScreenEditor() {
|
||||
TAB_ITEM("Title Screen")
|
||||
END_TAB_ITEM()
|
||||
if (ImGui::BeginTabItem("Title Screen")) {
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawNamingScreenEditor() {
|
||||
TAB_ITEM("Naming Screen")
|
||||
END_TAB_ITEM()
|
||||
if (ImGui::BeginTabItem("Naming Screen")) {
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawOverworldMapEditor() {
|
||||
TAB_ITEM("Overworld Map")
|
||||
END_TAB_ITEM()
|
||||
if (ImGui::BeginTabItem("Overworld Map")) {
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawToolset() {
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
#ifndef YAZE_APP_EDITOR_SCREEN_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_SCREEN_EDITOR_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/constants.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gfx/tilesheet.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/screen/dungeon_map.h"
|
||||
#include "app/zelda3/screen/inventory.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -38,7 +33,7 @@ namespace editor {
|
||||
* The class inherits from the SharedRom class.
|
||||
*/
|
||||
class ScreenEditor : public SharedRom, public Editor {
|
||||
public:
|
||||
public:
|
||||
ScreenEditor() {
|
||||
screen_canvas_.SetCanvasSize(ImVec2(512, 512));
|
||||
type_ = EditorType::kScreen;
|
||||
@@ -55,7 +50,7 @@ class ScreenEditor : public SharedRom, public Editor {
|
||||
|
||||
absl::Status SaveDungeonMaps();
|
||||
|
||||
private:
|
||||
private:
|
||||
void DrawTitleScreenEditor();
|
||||
void DrawNamingScreenEditor();
|
||||
void DrawOverworldMapEditor();
|
||||
@@ -65,42 +60,58 @@ class ScreenEditor : public SharedRom, public Editor {
|
||||
void DrawInventoryToolset();
|
||||
|
||||
absl::Status LoadDungeonMaps();
|
||||
absl::Status LoadDungeonMapTile16();
|
||||
absl::Status LoadDungeonMapTile16(const std::vector<uint8_t> &gfx_data,
|
||||
bool bin_mode = false);
|
||||
absl::Status SaveDungeonMapTile16();
|
||||
void DrawDungeonMapsTabs();
|
||||
void DrawDungeonMapsEditor();
|
||||
|
||||
std::vector<zelda3::screen::DungeonMap> dungeon_maps_;
|
||||
std::vector<std::vector<std::array<std::string, 25>>> dungeon_map_labels_;
|
||||
void LoadBinaryGfx();
|
||||
|
||||
std::unordered_map<int, gfx::Bitmap> tile16_individual_;
|
||||
enum class EditingMode { DRAW, EDIT };
|
||||
|
||||
EditingMode current_mode_ = EditingMode::DRAW;
|
||||
|
||||
bool dungeon_maps_loaded_ = false;
|
||||
bool binary_gfx_loaded_ = false;
|
||||
|
||||
int selected_tile16_ = 0;
|
||||
int selected_dungeon = 0;
|
||||
uint8_t selected_room = 0;
|
||||
uint8_t boss_room = 0;
|
||||
|
||||
int selected_tile16_ = 0;
|
||||
int selected_tile8_ = 0;
|
||||
int selected_dungeon = 0;
|
||||
int floor_number = 1;
|
||||
|
||||
bool copy_button_pressed = false;
|
||||
bool paste_button_pressed = false;
|
||||
|
||||
Bytes all_gfx_;
|
||||
zelda3::screen::Inventory inventory_;
|
||||
gfx::SnesPalette palette_;
|
||||
gui::Canvas screen_canvas_;
|
||||
gui::Canvas tilesheet_canvas_;
|
||||
gui::Canvas tilemap_canvas_;
|
||||
|
||||
gfx::BitmapTable sheets_;
|
||||
|
||||
gfx::Tilesheet tile16_sheet_;
|
||||
std::vector<uint8_t> all_gfx_;
|
||||
std::unordered_map<int, gfx::Bitmap> tile16_individual_;
|
||||
std::vector<zelda3::screen::DungeonMap> dungeon_maps_;
|
||||
std::vector<std::vector<std::array<std::string, 25>>> dungeon_map_labels_;
|
||||
std::array<uint16_t, 4> current_tile16_data_;
|
||||
std::vector<uint8_t> gfx_bin_data_;
|
||||
|
||||
absl::Status status_;
|
||||
|
||||
gfx::SnesPalette palette_;
|
||||
gfx::BitmapTable sheets_;
|
||||
gfx::Tilesheet tile16_sheet_;
|
||||
gfx::InternalTile16 current_tile16_info;
|
||||
|
||||
gui::Canvas current_tile_canvas_{"##CurrentTileCanvas"};
|
||||
gui::Canvas screen_canvas_;
|
||||
gui::Canvas tilesheet_canvas_;
|
||||
gui::Canvas tilemap_canvas_{"##TilemapCanvas",
|
||||
ImVec2(128 + 2, (192) + 4),
|
||||
gui::CanvasGridSize::k8x8, 2.f};
|
||||
|
||||
zelda3::screen::Inventory inventory_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
#include "tile16_editor.h"
|
||||
|
||||
#include "ImGuiFileDialog/ImGuiFileDialog.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "ImGuiFileDialog/ImGuiFileDialog.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/core/platform/renderer.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
@@ -19,11 +18,14 @@
|
||||
#include "app/gui/style.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
using ImGui::BeginChild;
|
||||
using ImGui::BeginMenu;
|
||||
using ImGui::BeginMenuBar;
|
||||
@@ -48,14 +50,13 @@ using ImGui::TableSetupColumn;
|
||||
using ImGui::Text;
|
||||
|
||||
absl::Status Tile16Editor::InitBlockset(
|
||||
gfx::Bitmap* tile16_blockset_bmp, gfx::Bitmap current_gfx_bmp,
|
||||
const gfx::Bitmap& tile16_blockset_bmp, const gfx::Bitmap& current_gfx_bmp,
|
||||
const std::vector<gfx::Bitmap>& tile16_individual,
|
||||
uint8_t all_tiles_types[0x200]) {
|
||||
all_tiles_types_ = all_tiles_types;
|
||||
tile16_blockset_bmp_ = tile16_blockset_bmp;
|
||||
tile16_individual_ = tile16_individual;
|
||||
current_gfx_bmp_ = current_gfx_bmp;
|
||||
tile8_gfx_data_ = current_gfx_bmp_.vector();
|
||||
RETURN_IF_ERROR(LoadTile8());
|
||||
ImVector<std::string> tile16_names;
|
||||
for (int i = 0; i < 0x200; ++i) {
|
||||
@@ -75,7 +76,7 @@ absl::Status Tile16Editor::Update() {
|
||||
|
||||
RETURN_IF_ERROR(DrawMenu());
|
||||
if (BeginTabBar("Tile16 Editor Tabs")) {
|
||||
RETURN_IF_ERROR(DrawTile16Editor());
|
||||
DrawTile16Editor();
|
||||
RETURN_IF_ERROR(UpdateTile16Transfer());
|
||||
EndTabBar();
|
||||
}
|
||||
@@ -97,7 +98,7 @@ absl::Status Tile16Editor::DrawMenu() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Tile16Editor::DrawTile16Editor() {
|
||||
void Tile16Editor::DrawTile16Editor() {
|
||||
if (BeginTabItem("Tile16 Editing")) {
|
||||
if (BeginTable("#Tile16EditorTable", 2, TABLE_BORDERS_RESIZABLE,
|
||||
ImVec2(0, 0))) {
|
||||
@@ -108,18 +109,23 @@ absl::Status Tile16Editor::DrawTile16Editor() {
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
TableNextColumn();
|
||||
RETURN_IF_ERROR(UpdateBlockset());
|
||||
status_ = UpdateBlockset();
|
||||
if (!status_.ok()) {
|
||||
EndTable();
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
RETURN_IF_ERROR(UpdateTile16Edit());
|
||||
RETURN_IF_ERROR(DrawTileEditControls());
|
||||
status_ = UpdateTile16Edit();
|
||||
if (status_ != absl::OkStatus()) {
|
||||
EndTable();
|
||||
}
|
||||
status_ = DrawTileEditControls();
|
||||
|
||||
EndTable();
|
||||
}
|
||||
|
||||
EndTabItem();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Tile16Editor::UpdateBlockset() {
|
||||
@@ -127,14 +133,12 @@ absl::Status Tile16Editor::UpdateBlockset() {
|
||||
gui::BeginChildWithScrollbar("##Tile16EditorBlocksetScrollRegion");
|
||||
blockset_canvas_.DrawBackground();
|
||||
gui::EndPadding();
|
||||
{
|
||||
blockset_canvas_.DrawContextMenu();
|
||||
blockset_canvas_.DrawTileSelector(32);
|
||||
blockset_canvas_.DrawBitmap(*tile16_blockset_bmp_, 0, map_blockset_loaded_);
|
||||
blockset_canvas_.DrawGrid();
|
||||
blockset_canvas_.DrawOverlay();
|
||||
EndChild();
|
||||
}
|
||||
blockset_canvas_.DrawContextMenu();
|
||||
blockset_canvas_.DrawTileSelector(32);
|
||||
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 0, map_blockset_loaded_);
|
||||
blockset_canvas_.DrawGrid();
|
||||
blockset_canvas_.DrawOverlay();
|
||||
EndChild();
|
||||
|
||||
if (!blockset_canvas_.points().empty()) {
|
||||
notify_tile16.mutable_get() = blockset_canvas_.GetTileIdFromMousePos();
|
||||
@@ -142,11 +146,11 @@ absl::Status Tile16Editor::UpdateBlockset() {
|
||||
|
||||
if (notify_tile16.modified()) {
|
||||
current_tile16_ = notify_tile16.get();
|
||||
current_tile16_bmp_ = &tile16_individual_[notify_tile16];
|
||||
current_tile16_bmp_ = tile16_individual_[notify_tile16];
|
||||
auto ow_main_pal_group = rom()->palette_group().overworld_main;
|
||||
RETURN_IF_ERROR(current_tile16_bmp_->ApplyPalette(
|
||||
RETURN_IF_ERROR(current_tile16_bmp_.ApplyPalette(
|
||||
ow_main_pal_group[current_palette_]));
|
||||
rom()->RenderBitmap(current_tile16_bmp_);
|
||||
Renderer::GetInstance().RenderBitmap(¤t_tile16_bmp_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +170,8 @@ absl::Status Tile16Editor::DrawToCurrentTile16(ImVec2 click_position) {
|
||||
|
||||
// Calculate the pixel start position within the Tile16
|
||||
ImVec2 start_position;
|
||||
start_position.x = ((tile_index_x) / 4) * 0x40;
|
||||
start_position.y = ((tile_index_y) / 4) * 0x40;
|
||||
start_position.x = tile_index_x * 0x40;
|
||||
start_position.y = tile_index_y * 0x40;
|
||||
std::cout << "Start Position X: " << start_position.x << std::endl;
|
||||
std::cout << "Start Position Y: " << start_position.y << std::endl;
|
||||
|
||||
@@ -177,7 +181,7 @@ absl::Status Tile16Editor::DrawToCurrentTile16(ImVec2 click_position) {
|
||||
int pixel_index =
|
||||
(start_position.y + y) * tile16_size + ((start_position.x) + x);
|
||||
int gfx_pixel_index = y * tile8_size + x;
|
||||
current_tile16_bmp_->WriteToPixel(
|
||||
current_tile16_bmp_.WriteToPixel(
|
||||
pixel_index,
|
||||
current_gfx_individual_[current_tile8_].data()[gfx_pixel_index]);
|
||||
}
|
||||
@@ -197,7 +201,8 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
RETURN_IF_ERROR(
|
||||
current_gfx_individual_[current_tile8_].ApplyPaletteWithTransparent(
|
||||
ow_main_pal_group[0], current_palette_));
|
||||
rom()->UpdateBitmap(¤t_gfx_individual_[current_tile8_]);
|
||||
Renderer::GetInstance().UpdateBitmap(
|
||||
¤t_gfx_individual_[current_tile8_]);
|
||||
}
|
||||
tile8_source_canvas_.DrawBitmap(current_gfx_bmp_, 0, 0, 4.0f);
|
||||
tile8_source_canvas_.DrawGrid();
|
||||
@@ -214,20 +219,21 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
RETURN_IF_ERROR(
|
||||
current_gfx_individual_[current_tile8_].ApplyPaletteWithTransparent(
|
||||
ow_main_pal_group[0], current_palette_));
|
||||
rom()->UpdateBitmap(¤t_gfx_individual_[current_tile8_]);
|
||||
Renderer::GetInstance().UpdateBitmap(
|
||||
¤t_gfx_individual_[current_tile8_]);
|
||||
}
|
||||
|
||||
if (BeginChild("Tile16 Editor Options",
|
||||
ImVec2(GetContentRegionAvail().x, 0x50), true)) {
|
||||
tile16_edit_canvas_.DrawBackground();
|
||||
tile16_edit_canvas_.DrawContextMenu(current_tile16_bmp_);
|
||||
tile16_edit_canvas_.DrawBitmap(*current_tile16_bmp_, 0, 0, 4.0f);
|
||||
tile16_edit_canvas_.DrawContextMenu(¤t_tile16_bmp_);
|
||||
tile16_edit_canvas_.DrawBitmap(current_tile16_bmp_, 0, 0, 4.0f);
|
||||
if (!tile8_source_canvas_.points().empty()) {
|
||||
if (tile16_edit_canvas_.DrawTilePainter(
|
||||
current_gfx_individual_[current_tile8_], 16, 2.0f)) {
|
||||
RETURN_IF_ERROR(
|
||||
DrawToCurrentTile16(tile16_edit_canvas_.drawn_tile_position()));
|
||||
rom()->UpdateBitmap(current_tile16_bmp_);
|
||||
Renderer::GetInstance().UpdateBitmap(¤t_tile16_bmp_);
|
||||
}
|
||||
}
|
||||
tile16_edit_canvas_.DrawGrid();
|
||||
@@ -258,10 +264,11 @@ absl::Status Tile16Editor::DrawTileEditControls() {
|
||||
if (value > 0x00) {
|
||||
RETURN_IF_ERROR(
|
||||
current_gfx_bmp_.ApplyPaletteWithTransparent(palette, value));
|
||||
Renderer::GetInstance().UpdateBitmap(¤t_gfx_bmp_);
|
||||
|
||||
RETURN_IF_ERROR(
|
||||
current_tile16_bmp_->ApplyPaletteWithTransparent(palette, value));
|
||||
rom()->UpdateBitmap(¤t_gfx_bmp_);
|
||||
rom()->UpdateBitmap(current_tile16_bmp_);
|
||||
current_tile16_bmp_.ApplyPaletteWithTransparent(palette, value));
|
||||
Renderer::GetInstance().UpdateBitmap(¤t_tile16_bmp_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +297,6 @@ absl::Status Tile16Editor::LoadTile8() {
|
||||
|
||||
// Calculate the position in the current gfx data
|
||||
int num_columns = current_gfx_bmp_.width() / 8;
|
||||
int num_rows = current_gfx_bmp_.height() / 8;
|
||||
int x = (index % num_columns) * 8 + tx;
|
||||
int y = (index / num_columns) * 8 + ty;
|
||||
int gfx_position = x + (y * 0x100);
|
||||
@@ -310,7 +316,7 @@ absl::Status Tile16Editor::LoadTile8() {
|
||||
current_gfx_individual_[index].Create(0x08, 0x08, 0x08, tile_data);
|
||||
RETURN_IF_ERROR(current_gfx_individual_[index].ApplyPaletteWithTransparent(
|
||||
ow_main_pal_group[0], current_palette_));
|
||||
rom()->RenderBitmap(¤t_gfx_individual_[index]);
|
||||
Renderer::GetInstance().RenderBitmap(¤t_gfx_individual_[index]);
|
||||
}
|
||||
|
||||
map_blockset_loaded_ = true;
|
||||
@@ -318,8 +324,17 @@ absl::Status Tile16Editor::LoadTile8() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tile16 Transfer
|
||||
absl::Status Tile16Editor::SetCurrentTile(int id) {
|
||||
current_tile16_ = id;
|
||||
current_tile16_bmp_ = tile16_individual_[id];
|
||||
auto ow_main_pal_group = rom()->palette_group().overworld_main;
|
||||
RETURN_IF_ERROR(
|
||||
current_tile16_bmp_.ApplyPalette(ow_main_pal_group[current_palette_]));
|
||||
Renderer::GetInstance().RenderBitmap(¤t_tile16_bmp_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
#pragma mark - Tile16Transfer
|
||||
|
||||
absl::Status Tile16Editor::UpdateTile16Transfer() {
|
||||
if (BeginTabItem("Tile16 Transfer")) {
|
||||
@@ -363,16 +378,15 @@ absl::Status Tile16Editor::UpdateTransferTileCanvas() {
|
||||
// TODO: Implement tile16 transfer
|
||||
if (transfer_started_ && !transfer_blockset_loaded_) {
|
||||
PRINT_IF_ERROR(transfer_rom_.LoadAllGraphicsData())
|
||||
graphics_bin_ = transfer_rom_.graphics_bin();
|
||||
|
||||
// Load the Link to the Past overworld.
|
||||
PRINT_IF_ERROR(transfer_overworld_.Load(transfer_rom_))
|
||||
transfer_overworld_.set_current_map(0);
|
||||
palette_ = transfer_overworld_.AreaPalette();
|
||||
palette_ = transfer_overworld_.current_area_palette();
|
||||
|
||||
// Create the tile16 blockset image
|
||||
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
|
||||
0x80, 0x2000, 0x80, transfer_overworld_.Tile16Blockset(),
|
||||
RETURN_IF_ERROR(Renderer::GetInstance().CreateAndRenderBitmap(
|
||||
0x80, 0x2000, 0x80, transfer_overworld_.tile16_blockset_data(),
|
||||
transfer_blockset_bmp_, palette_));
|
||||
transfer_blockset_loaded_ = true;
|
||||
}
|
||||
@@ -385,16 +399,6 @@ absl::Status Tile16Editor::UpdateTransferTileCanvas() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Tile16Editor::SetCurrentTile(int id) {
|
||||
current_tile16_ = id;
|
||||
current_tile16_bmp_ = &tile16_individual_[id];
|
||||
auto ow_main_pal_group = rom()->palette_group().overworld_main;
|
||||
RETURN_IF_ERROR(
|
||||
current_tile16_bmp_->ApplyPalette(ow_main_pal_group[current_palette_]));
|
||||
rom()->RenderBitmap(current_tile16_bmp_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
#ifndef YAZE_APP_EDITOR_TILE16EDITOR_H
|
||||
#define YAZE_APP_EDITOR_TILE16EDITOR_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/core/common.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/utils/gfx_context.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gfx/tilesheet.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -26,17 +19,17 @@ namespace editor {
|
||||
/**
|
||||
* @brief Popup window to edit Tile16 data
|
||||
*/
|
||||
class Tile16Editor : public context::GfxContext, public SharedRom {
|
||||
class Tile16Editor : public GfxContext, public SharedRom {
|
||||
public:
|
||||
absl::Status InitBlockset(gfx::Bitmap* tile16_blockset_bmp,
|
||||
gfx::Bitmap current_gfx_bmp,
|
||||
absl::Status InitBlockset(const gfx::Bitmap& tile16_blockset_bmp,
|
||||
const gfx::Bitmap& current_gfx_bmp,
|
||||
const std::vector<gfx::Bitmap>& tile16_individual,
|
||||
uint8_t all_tiles_types[0x200]);
|
||||
|
||||
absl::Status Update();
|
||||
absl::Status DrawMenu();
|
||||
|
||||
absl::Status DrawTile16Editor();
|
||||
void DrawTile16Editor();
|
||||
absl::Status UpdateTile16Transfer();
|
||||
absl::Status UpdateBlockset();
|
||||
|
||||
@@ -75,38 +68,32 @@ class Tile16Editor : public context::GfxContext, public SharedRom {
|
||||
// Tile16 blockset for selecting the tile to edit
|
||||
gui::Canvas blockset_canvas_{"blocksetCanvas", ImVec2(0x100, 0x4000),
|
||||
gui::CanvasGridSize::k32x32};
|
||||
gfx::Bitmap* tile16_blockset_bmp_;
|
||||
gfx::Bitmap tile16_blockset_bmp_;
|
||||
|
||||
// Canvas for editing the selected tile
|
||||
gui::Canvas tile16_edit_canvas_{"Tile16EditCanvas", ImVec2(0x40, 0x40),
|
||||
gui::CanvasGridSize::k64x64};
|
||||
gfx::Bitmap* current_tile16_bmp_;
|
||||
gfx::Bitmap current_tile16_bmp_;
|
||||
|
||||
// Tile8 canvas to get the tile to drawing in the tile16_edit_canvas_
|
||||
gui::Canvas tile8_source_canvas_{
|
||||
"Tile8SourceCanvas",
|
||||
ImVec2(core::kTilesheetWidth * 4, core::kTilesheetHeight * 0x10 * 4),
|
||||
ImVec2(gfx::kTilesheetWidth * 4, gfx::kTilesheetHeight * 0x10 * 4),
|
||||
gui::CanvasGridSize::k32x32};
|
||||
gfx::Bitmap current_gfx_bmp_;
|
||||
|
||||
gui::Canvas transfer_canvas_;
|
||||
gfx::Bitmap transfer_blockset_bmp_;
|
||||
|
||||
std::vector<Bytes> tile16_individual_data_;
|
||||
std::vector<gfx::Bitmap> tile16_individual_;
|
||||
|
||||
std::vector<gfx::Bitmap> current_gfx_individual_;
|
||||
|
||||
std::vector<uint8_t> current_tile16_data_;
|
||||
|
||||
std::vector<uint8_t> tile8_gfx_data_;
|
||||
|
||||
PaletteEditor palette_editor_;
|
||||
|
||||
gfx::SnesPalette palette_;
|
||||
zelda3::overworld::Overworld transfer_overworld_;
|
||||
|
||||
gfx::BitmapTable graphics_bin_;
|
||||
absl::Status status_;
|
||||
|
||||
Rom transfer_rom_;
|
||||
absl::Status transfer_status_;
|
||||
@@ -115,4 +102,4 @@ class Tile16Editor : public context::GfxContext, public SharedRom {
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
#endif // YAZE_APP_EDITOR_TILE16EDITOR_H
|
||||
#endif // YAZE_APP_EDITOR_TILE16EDITOR_H
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "master_editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
182
src/app/editor/message/message_data.cc
Normal file
182
src/app/editor/message/message_data.cc
Normal file
@@ -0,0 +1,182 @@
|
||||
#include "message_data.h"
|
||||
|
||||
#include "app/core/common.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
uint8_t FindMatchingCharacter(char value) {
|
||||
for (const auto [key, char_value] : CharEncoder) {
|
||||
if (value == char_value) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
uint8_t FindDictionaryEntry(uint8_t value) {
|
||||
if (value < DICTOFF || value == 0xFF) {
|
||||
return -1;
|
||||
}
|
||||
return value - DICTOFF;
|
||||
}
|
||||
|
||||
TextElement FindMatchingCommand(uint8_t b) {
|
||||
TextElement empty_element;
|
||||
for (const auto& text_element : TextCommands) {
|
||||
if (text_element.ID == b) {
|
||||
return text_element;
|
||||
}
|
||||
}
|
||||
return empty_element;
|
||||
}
|
||||
|
||||
TextElement FindMatchingSpecial(uint8_t value) {
|
||||
auto it = std::find_if(SpecialChars.begin(), SpecialChars.end(),
|
||||
[value](const TextElement& text_element) {
|
||||
return text_element.ID == value;
|
||||
});
|
||||
if (it != SpecialChars.end()) {
|
||||
return *it;
|
||||
}
|
||||
|
||||
return TextElement();
|
||||
}
|
||||
|
||||
ParsedElement FindMatchingElement(const std::string& str) {
|
||||
std::smatch match;
|
||||
for (auto& textElement : TextCommands) {
|
||||
match = textElement.MatchMe(str);
|
||||
if (match.size() > 0) {
|
||||
if (textElement.HasArgument) {
|
||||
return ParsedElement(textElement,
|
||||
std::stoi(match[1].str(), nullptr, 16));
|
||||
} else {
|
||||
return ParsedElement(textElement, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto dictionary_element =
|
||||
TextElement(0x80, DICTIONARYTOKEN, true, "Dictionary");
|
||||
|
||||
match = dictionary_element.MatchMe(str);
|
||||
if (match.size() > 0) {
|
||||
return ParsedElement(dictionary_element,
|
||||
DICTOFF + std::stoi(match[1].str(), nullptr, 16));
|
||||
}
|
||||
return ParsedElement();
|
||||
}
|
||||
|
||||
std::string ParseTextDataByte(uint8_t value) {
|
||||
if (CharEncoder.contains(value)) {
|
||||
char c = CharEncoder.at(value);
|
||||
std::string str = "";
|
||||
str.push_back(c);
|
||||
return str;
|
||||
}
|
||||
|
||||
// Check for command.
|
||||
TextElement textElement = FindMatchingCommand(value);
|
||||
if (!textElement.Empty()) {
|
||||
return textElement.GenericToken;
|
||||
}
|
||||
|
||||
// Check for special characters.
|
||||
textElement = FindMatchingSpecial(value);
|
||||
if (!textElement.Empty()) {
|
||||
return textElement.GenericToken;
|
||||
}
|
||||
|
||||
// Check for dictionary.
|
||||
int dictionary = FindDictionaryEntry(value);
|
||||
if (dictionary >= 0) {
|
||||
return absl::StrFormat("[%s:%X]", DICTIONARYTOKEN, dictionary);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ParseMessageToData(std::string str) {
|
||||
std::vector<uint8_t> bytes;
|
||||
std::string temp_string = str;
|
||||
int pos = 0;
|
||||
|
||||
while (pos < temp_string.size()) {
|
||||
// Get next text fragment.
|
||||
if (temp_string[pos] == '[') {
|
||||
int next = temp_string.find(']', pos);
|
||||
if (next == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
ParsedElement parsedElement =
|
||||
FindMatchingElement(temp_string.substr(pos, next - pos + 1));
|
||||
|
||||
const auto dictionary_element =
|
||||
TextElement(0x80, DICTIONARYTOKEN, true, "Dictionary");
|
||||
|
||||
if (!parsedElement.Active) {
|
||||
core::logf("Error parsing message: %s", temp_string);
|
||||
break;
|
||||
} else if (parsedElement.Parent == dictionary_element) {
|
||||
bytes.push_back(parsedElement.Value);
|
||||
} else {
|
||||
bytes.push_back(parsedElement.Parent.ID);
|
||||
|
||||
if (parsedElement.Parent.HasArgument) {
|
||||
bytes.push_back(parsedElement.Value);
|
||||
}
|
||||
}
|
||||
|
||||
pos = next + 1;
|
||||
continue;
|
||||
} else {
|
||||
uint8_t bb = FindMatchingCharacter(temp_string[pos++]);
|
||||
|
||||
if (bb != 0xFF) {
|
||||
core::logf("Error parsing message: %s", temp_string);
|
||||
bytes.push_back(bb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::vector<DictionaryEntry> BuildDictionaryEntries(app::Rom* rom) {
|
||||
std::vector<DictionaryEntry> AllDictionaries;
|
||||
for (int i = 0; i < kNumDictionaryEntries; i++) {
|
||||
std::vector<uint8_t> bytes;
|
||||
std::stringstream stringBuilder;
|
||||
|
||||
int address = core::SnesToPc(
|
||||
kTextData + (rom->data()[kPointersDictionaries + (i * 2) + 1] << 8) +
|
||||
rom->data()[kPointersDictionaries + (i * 2)]);
|
||||
|
||||
int temppush_backress = core::SnesToPc(
|
||||
kTextData +
|
||||
(rom->data()[kPointersDictionaries + ((i + 1) * 2) + 1] << 8) +
|
||||
rom->data()[kPointersDictionaries + ((i + 1) * 2)]);
|
||||
|
||||
while (address < temppush_backress) {
|
||||
uint8_t uint8_tDictionary = rom->data()[address++];
|
||||
bytes.push_back(uint8_tDictionary);
|
||||
stringBuilder << ParseTextDataByte(uint8_tDictionary);
|
||||
}
|
||||
|
||||
AllDictionaries.push_back(DictionaryEntry{(uint8_t)i, stringBuilder.str()});
|
||||
}
|
||||
|
||||
std::sort(AllDictionaries.begin(), AllDictionaries.end(),
|
||||
[](const DictionaryEntry& a, const DictionaryEntry& b) {
|
||||
return a.Contents.size() > b.Contents.size();
|
||||
});
|
||||
|
||||
return AllDictionaries;
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -1,23 +1,90 @@
|
||||
#ifndef YAZE_APP_EDITOR_MESSAGE_MESSAGE_DATA_H
|
||||
#define YAZE_APP_EDITOR_MESSAGE_MESSAGE_DATA_H
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/str_replace.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
const uint8_t MESSAGETERMINATOR = 0x7F;
|
||||
const uint8_t kMessageTerminator = 0x7F;
|
||||
const std::string BANKToken = "BANK";
|
||||
const std::string DICTIONARYTOKEN = "D";
|
||||
constexpr uint8_t DICTOFF = 0x88;
|
||||
|
||||
static std::string AddNewLinesToCommands(std::string str);
|
||||
static std::string ReplaceAllDictionaryWords(std::string str);
|
||||
static std::vector<uint8_t> ParseMessageToData(std::string str);
|
||||
static const std::unordered_map<uint8_t, wchar_t> CharEncoder = {
|
||||
{0x00, 'A'}, {0x01, 'B'}, {0x02, 'C'}, {0x03, 'D'}, {0x04, 'E'},
|
||||
{0x05, 'F'}, {0x06, 'G'}, {0x07, 'H'}, {0x08, 'I'}, {0x09, 'J'},
|
||||
{0x0A, 'K'}, {0x0B, 'L'}, {0x0C, 'M'}, {0x0D, 'N'}, {0x0E, 'O'},
|
||||
{0x0F, 'P'}, {0x10, 'Q'}, {0x11, 'R'}, {0x12, 'S'}, {0x13, 'T'},
|
||||
{0x14, 'U'}, {0x15, 'V'}, {0x16, 'W'}, {0x17, 'X'}, {0x18, 'Y'},
|
||||
{0x19, 'Z'}, {0x1A, 'a'}, {0x1B, 'b'}, {0x1C, 'c'}, {0x1D, 'd'},
|
||||
{0x1E, 'e'}, {0x1F, 'f'}, {0x20, 'g'}, {0x21, 'h'}, {0x22, 'i'},
|
||||
{0x23, 'j'}, {0x24, 'k'}, {0x25, 'l'}, {0x26, 'm'}, {0x27, 'n'},
|
||||
{0x28, 'o'}, {0x29, 'p'}, {0x2A, 'q'}, {0x2B, 'r'}, {0x2C, 's'},
|
||||
{0x2D, 't'}, {0x2E, 'u'}, {0x2F, 'v'}, {0x30, 'w'}, {0x31, 'x'},
|
||||
{0x32, 'y'}, {0x33, 'z'}, {0x34, '0'}, {0x35, '1'}, {0x36, '2'},
|
||||
{0x37, '3'}, {0x38, '4'}, {0x39, '5'}, {0x3A, '6'}, {0x3B, '7'},
|
||||
{0x3C, '8'}, {0x3D, '9'}, {0x3E, '!'}, {0x3F, '?'}, {0x40, '-'},
|
||||
{0x41, '.'}, {0x42, ','}, {0x44, '>'}, {0x45, '('}, {0x46, ')'},
|
||||
{0x4C, '"'}, {0x51, '\''}, {0x59, ' '}, {0x5A, '<'}, {0x5F, L'¡'},
|
||||
{0x60, L'¡'}, {0x61, L'¡'}, {0x62, L' '}, {0x63, L' '}, {0x64, L' '},
|
||||
{0x65, ' '}, {0x66, '_'},
|
||||
};
|
||||
|
||||
const std::string CHEESE = "\uBEBE"; // Inserted into commands to protect
|
||||
// them from dictionary replacements.
|
||||
uint8_t FindMatchingCharacter(char value);
|
||||
uint8_t FindDictionaryEntry(uint8_t value);
|
||||
|
||||
std::vector<uint8_t> ParseMessageToData(std::string str);
|
||||
|
||||
struct DictionaryEntry {
|
||||
uint8_t ID;
|
||||
std::string Contents;
|
||||
std::vector<uint8_t> Data;
|
||||
int Length;
|
||||
std::string Token;
|
||||
|
||||
DictionaryEntry() = default;
|
||||
DictionaryEntry(uint8_t i, std::string s)
|
||||
: Contents(s), ID(i), Length(s.length()) {
|
||||
Token = absl::StrFormat("[%s:%00X]", DICTIONARYTOKEN, ID);
|
||||
Data = ParseMessageToData(Contents);
|
||||
}
|
||||
|
||||
bool ContainedInString(std::string s) {
|
||||
return s.find(Contents) != std::string::npos;
|
||||
}
|
||||
|
||||
std::string ReplaceInstancesOfIn(std::string s) {
|
||||
std::string replacedString = s;
|
||||
size_t pos = replacedString.find(Contents);
|
||||
while (pos != std::string::npos) {
|
||||
replacedString.replace(pos, Contents.length(), Token);
|
||||
pos = replacedString.find(Contents, pos + Token.length());
|
||||
}
|
||||
return replacedString;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr int kTextData = 0xE0000;
|
||||
constexpr int kTextDataEnd = 0xE7FFF;
|
||||
constexpr int kNumDictionaryEntries = 97;
|
||||
constexpr int kPointersDictionaries = 0x74703;
|
||||
|
||||
std::vector<DictionaryEntry> BuildDictionaryEntries(app::Rom* rom);
|
||||
|
||||
std::string ReplaceAllDictionaryWords(std::string str,
|
||||
std::vector<DictionaryEntry> dictionary);
|
||||
|
||||
// Inserted into commands to protect them from dictionary replacements.
|
||||
const std::string CHEESE = "\uBEBE";
|
||||
|
||||
struct MessageData {
|
||||
int ID;
|
||||
@@ -49,35 +116,13 @@ struct MessageData {
|
||||
ContentsParsed = other.ContentsParsed;
|
||||
}
|
||||
|
||||
void SetMessage(std::string messageString) {
|
||||
ContentsParsed = messageString;
|
||||
RawString = OptimizeMessageForDictionary(messageString);
|
||||
RecalculateData();
|
||||
}
|
||||
|
||||
std::string ToString() {
|
||||
return absl::StrFormat("%0X - %s", ID, ContentsParsed);
|
||||
}
|
||||
|
||||
std::string GetReadableDumpedContents() {
|
||||
std::stringstream stringBuilder;
|
||||
for (const auto& b : Data) {
|
||||
stringBuilder << absl::StrFormat("%0X ", b);
|
||||
}
|
||||
stringBuilder << absl::StrFormat("%00X", MESSAGETERMINATOR);
|
||||
|
||||
return absl::StrFormat(
|
||||
"[[[[\r\nMessage "
|
||||
"%000X]]]]\r\n[Contents]\r\n%s\r\n\r\n[Data]\r\n%s"
|
||||
"\r\n\r\n\r\n\r\n",
|
||||
ID, AddNewLinesToCommands(ContentsParsed), stringBuilder.str());
|
||||
}
|
||||
|
||||
std::string GetDumpedContents() {
|
||||
return absl::StrFormat("%000X : %s\r\n\r\n", ID, ContentsParsed);
|
||||
}
|
||||
|
||||
std::string OptimizeMessageForDictionary(std::string messageString) {
|
||||
std::string OptimizeMessageForDictionary(
|
||||
std::string messageString,
|
||||
const std::vector<DictionaryEntry>& dictionary) {
|
||||
std::stringstream protons;
|
||||
bool command = false;
|
||||
for (const auto& c : messageString) {
|
||||
@@ -94,16 +139,18 @@ struct MessageData {
|
||||
}
|
||||
|
||||
std::string protonsString = protons.str();
|
||||
std::string replacedString = ReplaceAllDictionaryWords(protonsString);
|
||||
std::string replacedString =
|
||||
ReplaceAllDictionaryWords(protonsString, dictionary);
|
||||
std::string finalString =
|
||||
absl::StrReplaceAll(replacedString, {{CHEESE, ""}});
|
||||
|
||||
return finalString;
|
||||
}
|
||||
|
||||
void RecalculateData() {
|
||||
Data = ParseMessageToData(RawString);
|
||||
DataParsed = ParseMessageToData(ContentsParsed);
|
||||
void SetMessage(const std::string& message,
|
||||
const std::vector<DictionaryEntry>& dictionary) {
|
||||
RawString = message;
|
||||
ContentsParsed = OptimizeMessageForDictionary(message, dictionary);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -155,8 +202,64 @@ struct TextElement {
|
||||
}
|
||||
|
||||
bool Empty() { return ID == 0; }
|
||||
|
||||
// Comparison operator
|
||||
bool operator==(const TextElement& other) const { return ID == other.ID; }
|
||||
};
|
||||
|
||||
static const std::vector<TextElement> TextCommands = {
|
||||
TextElement(0x6B, "W", true, "Window border"),
|
||||
TextElement(0x6D, "P", true, "Window position"),
|
||||
TextElement(0x6E, "SPD", true, "Scroll speed"),
|
||||
TextElement(0x7A, "S", true, "Text draw speed"),
|
||||
TextElement(0x77, "C", true, "Text color"),
|
||||
TextElement(0x6A, "L", false, "Player name"),
|
||||
TextElement(0x74, "1", false, "Line 1"),
|
||||
TextElement(0x75, "2", false, "Line 2"),
|
||||
TextElement(0x76, "3", false, "Line 3"),
|
||||
TextElement(0x7E, "K", false, "Wait for key"),
|
||||
TextElement(0x73, "V", false, "Scroll text"),
|
||||
TextElement(0x78, "WT", true, "Delay X"),
|
||||
TextElement(0x6C, "N", true, "BCD number"),
|
||||
TextElement(0x79, "SFX", true, "Sound effect"),
|
||||
TextElement(0x71, "CH3", false, "Choose 3"),
|
||||
TextElement(0x72, "CH2", false, "Choose 2 high"),
|
||||
TextElement(0x6F, "CH2L", false, "Choose 2 low"),
|
||||
TextElement(0x68, "CH2I", false, "Choose 2 indented"),
|
||||
TextElement(0x69, "CHI", false, "Choose item"),
|
||||
TextElement(0x67, "IMG", false, "Next attract image"),
|
||||
TextElement(0x80, BANKToken, false, "Bank marker (automatic)"),
|
||||
TextElement(0x70, "NONO", false, "Crash"),
|
||||
};
|
||||
|
||||
TextElement FindMatchingCommand(uint8_t b);
|
||||
|
||||
static const std::vector<TextElement> SpecialChars = {
|
||||
TextElement(0x43, "...", false, "Ellipsis …"),
|
||||
TextElement(0x4D, "UP", false, "Arrow ↑"),
|
||||
TextElement(0x4E, "DOWN", false, "Arrow ↓"),
|
||||
TextElement(0x4F, "LEFT", false, "Arrow ←"),
|
||||
TextElement(0x50, "RIGHT", false, "Arrow →"),
|
||||
TextElement(0x5B, "A", false, "Button Ⓐ"),
|
||||
TextElement(0x5C, "B", false, "Button Ⓑ"),
|
||||
TextElement(0x5D, "X", false, "Button ⓧ"),
|
||||
TextElement(0x5E, "Y", false, "Button ⓨ"),
|
||||
TextElement(0x52, "HP1L", false, "1 HP left"),
|
||||
TextElement(0x53, "HP1R", false, "1 HP right"),
|
||||
TextElement(0x54, "HP2L", false, "2 HP left"),
|
||||
TextElement(0x55, "HP3L", false, "3 HP left"),
|
||||
TextElement(0x56, "HP3R", false, "3 HP right"),
|
||||
TextElement(0x57, "HP4L", false, "4 HP left"),
|
||||
TextElement(0x58, "HP4R", false, "4 HP right"),
|
||||
TextElement(0x47, "HY0", false, "Hieroglyph ☥"),
|
||||
TextElement(0x48, "HY1", false, "Hieroglyph 𓈗"),
|
||||
TextElement(0x49, "HY2", false, "Hieroglyph Ƨ"),
|
||||
TextElement(0x4A, "LFL", false, "Link face left"),
|
||||
TextElement(0x4B, "LFR", false, "Link face right"),
|
||||
};
|
||||
|
||||
TextElement FindMatchingSpecial(uint8_t b);
|
||||
|
||||
struct ParsedElement {
|
||||
TextElement Parent;
|
||||
uint8_t Value;
|
||||
@@ -170,8 +273,12 @@ struct ParsedElement {
|
||||
}
|
||||
};
|
||||
|
||||
ParsedElement FindMatchingElement(const std::string& str);
|
||||
|
||||
std::string ParseTextDataByte(uint8_t value);
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_MESSAGE_MESSAGE_DATA_H
|
||||
#endif // YAZE_APP_EDITOR_MESSAGE_MESSAGE_DATA_H
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "message_editor.h"
|
||||
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@@ -9,26 +7,24 @@
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/str_replace.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "app/core/common.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/core/platform/renderer.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/rom.h"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using ImGui::Begin;
|
||||
using core::Renderer;
|
||||
|
||||
using ImGui::BeginChild;
|
||||
using ImGui::BeginTable;
|
||||
using ImGui::Button;
|
||||
using ImGui::End;
|
||||
using ImGui::EndChild;
|
||||
using ImGui::EndTable;
|
||||
using ImGui::InputText;
|
||||
@@ -37,216 +33,24 @@ using ImGui::SameLine;
|
||||
using ImGui::Separator;
|
||||
using ImGui::TableHeadersRow;
|
||||
using ImGui::TableNextColumn;
|
||||
using ImGui::TableNextRow;
|
||||
using ImGui::TableSetupColumn;
|
||||
using ImGui::Text;
|
||||
using ImGui::TextWrapped;
|
||||
using ImGui::TreeNode;
|
||||
|
||||
static ParsedElement FindMatchingElement(string str) {
|
||||
std::smatch match;
|
||||
for (auto& textElement : TextCommands) {
|
||||
match = textElement.MatchMe(str);
|
||||
if (match.size() > 0) {
|
||||
if (textElement.HasArgument) {
|
||||
return ParsedElement(textElement,
|
||||
std::stoi(match[1].str(), nullptr, 16));
|
||||
} else {
|
||||
return ParsedElement(textElement, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
constexpr ImGuiTableFlags kMessageTableFlags = ImGuiTableFlags_Hideable |
|
||||
ImGuiTableFlags_Borders |
|
||||
ImGuiTableFlags_Resizable;
|
||||
|
||||
match = DictionaryElement.MatchMe(str);
|
||||
if (match.size() > 0) {
|
||||
return ParsedElement(DictionaryElement,
|
||||
DICTOFF + std::stoi(match[1].str(), nullptr, 16));
|
||||
}
|
||||
return ParsedElement();
|
||||
}
|
||||
|
||||
static string ReplaceAllDictionaryWords(string str) {
|
||||
string temp = str;
|
||||
for (const auto& entry : AllDictionaries) {
|
||||
if (absl::StrContains(temp, entry.Contents)) {
|
||||
temp = absl::StrReplaceAll(temp, {{entry.Contents, entry.Contents}});
|
||||
}
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> ParseMessageToData(string str) {
|
||||
std::vector<uint8_t> bytes;
|
||||
string tempString = str;
|
||||
int pos = 0;
|
||||
|
||||
while (pos < tempString.size()) {
|
||||
// Get next text fragment.
|
||||
if (tempString[pos] == '[') {
|
||||
int next = tempString.find(']', pos);
|
||||
if (next == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
ParsedElement parsedElement =
|
||||
FindMatchingElement(tempString.substr(pos, next - pos + 1));
|
||||
if (!parsedElement.Active) {
|
||||
break; // TODO: handle badness.
|
||||
// } else if (parsedElement.Parent == DictionaryElement) {
|
||||
// bytes.push_back(parsedElement.Value);
|
||||
} else {
|
||||
bytes.push_back(parsedElement.Parent.ID);
|
||||
|
||||
if (parsedElement.Parent.HasArgument) {
|
||||
bytes.push_back(parsedElement.Value);
|
||||
}
|
||||
}
|
||||
|
||||
pos = next + 1;
|
||||
continue;
|
||||
} else {
|
||||
uint8_t bb = MessageEditor::FindMatchingCharacter(tempString[pos++]);
|
||||
|
||||
if (bb != 0xFF) {
|
||||
// TODO: handle badness.
|
||||
bytes.push_back(bb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
absl::Status MessageEditor::Update() {
|
||||
if (rom()->is_loaded() && !data_loaded_) {
|
||||
RETURN_IF_ERROR(Initialize());
|
||||
CurrentMessage = ListOfTexts[1];
|
||||
data_loaded_ = true;
|
||||
}
|
||||
|
||||
if (BeginTable("##MessageEditor", 3,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable)) {
|
||||
TableSetupColumn("List");
|
||||
TableSetupColumn("Contents");
|
||||
TableSetupColumn("Commands");
|
||||
|
||||
TableHeadersRow();
|
||||
|
||||
TableNextColumn();
|
||||
DrawMessageList();
|
||||
|
||||
TableNextColumn();
|
||||
DrawCurrentMessage();
|
||||
|
||||
TableNextColumn();
|
||||
DrawTextCommands();
|
||||
|
||||
EndTable();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void MessageEditor::DrawMessageList() {
|
||||
if (InputText("Search", &search_text_)) {
|
||||
DisplayedMessages.clear();
|
||||
for (const auto& message : ListOfTexts) {
|
||||
if (absl::StrContains(message.ContentsParsed, search_text_)) {
|
||||
DisplayedMessages.push_back(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (BeginChild("##MessagesList", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
if (BeginTable("##MessagesTable", 3,
|
||||
ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders |
|
||||
ImGuiTableFlags_Resizable)) {
|
||||
TableSetupColumn("ID");
|
||||
TableSetupColumn("Contents");
|
||||
TableSetupColumn("Data");
|
||||
|
||||
TableHeadersRow();
|
||||
|
||||
for (const auto& message : ListOfTexts) {
|
||||
TableNextColumn();
|
||||
if (Button(core::UppercaseHexWord(message.ID).c_str())) {
|
||||
CurrentMessage = message;
|
||||
DrawMessagePreview();
|
||||
}
|
||||
TableNextColumn();
|
||||
TextWrapped("%s", ParsedMessages[message.ID].c_str());
|
||||
TableNextColumn();
|
||||
TextWrapped(
|
||||
"%s",
|
||||
core::UppercaseHexLong(ListOfTexts[message.ID].Address).c_str());
|
||||
}
|
||||
|
||||
EndTable();
|
||||
}
|
||||
EndChild();
|
||||
}
|
||||
}
|
||||
|
||||
void MessageEditor::DrawCurrentMessage() {
|
||||
Button(absl::StrCat("Message ", CurrentMessage.ID).c_str());
|
||||
if (InputTextMultiline("##MessageEditor", &ParsedMessages[CurrentMessage.ID],
|
||||
ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
|
||||
CurrentMessage.Data = ParseMessageToData(message_text_box_.text);
|
||||
DrawMessagePreview();
|
||||
}
|
||||
Separator();
|
||||
|
||||
Text("Font Graphics");
|
||||
gui::BeginPadding(1);
|
||||
BeginChild("MessageEditorCanvas", ImVec2(0, 130));
|
||||
font_gfx_canvas_.DrawBackground();
|
||||
font_gfx_canvas_.DrawContextMenu();
|
||||
font_gfx_canvas_.DrawBitmap(font_gfx_bitmap_, 0, 0);
|
||||
font_gfx_canvas_.DrawGrid();
|
||||
font_gfx_canvas_.DrawOverlay();
|
||||
EndChild();
|
||||
gui::EndPadding();
|
||||
Separator();
|
||||
|
||||
Text("Message Preview");
|
||||
if (Button("Refresh Bitmap")) {
|
||||
rom()->UpdateBitmap(¤t_font_gfx16_bitmap_);
|
||||
}
|
||||
gui::BeginPadding(1);
|
||||
BeginChild("CurrentGfxFont", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||
current_font_gfx16_canvas_.DrawBackground();
|
||||
gui::EndPadding();
|
||||
current_font_gfx16_canvas_.DrawContextMenu();
|
||||
current_font_gfx16_canvas_.DrawBitmap(current_font_gfx16_bitmap_, 0, 0);
|
||||
current_font_gfx16_canvas_.DrawGrid();
|
||||
current_font_gfx16_canvas_.DrawOverlay();
|
||||
EndChild();
|
||||
}
|
||||
|
||||
void MessageEditor::DrawTextCommands() {
|
||||
if (BeginChild("##TextCommands", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
for (const auto& text_element : TextCommands) {
|
||||
if (Button(text_element.GenericToken.c_str())) {
|
||||
}
|
||||
SameLine();
|
||||
TextWrapped("%s", text_element.Description.c_str());
|
||||
Separator();
|
||||
}
|
||||
EndChild();
|
||||
}
|
||||
}
|
||||
constexpr ImGuiTableFlags kDictTableFlags =
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable;
|
||||
|
||||
absl::Status MessageEditor::Initialize() {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
for (int i = 0; i < kWidthArraySize; i++) {
|
||||
width_array[i] = rom()->data()[kCharactersWidth + i];
|
||||
}
|
||||
|
||||
BuildDictionaryEntries();
|
||||
ReadAllTextData();
|
||||
all_dictionaries_ = BuildDictionaryEntries(rom());
|
||||
ReadAllTextDataV2();
|
||||
|
||||
font_preview_colors_.AddColor(0x7FFF); // White
|
||||
font_preview_colors_.AddColor(0x7C00); // Red
|
||||
@@ -257,21 +61,23 @@ absl::Status MessageEditor::Initialize() {
|
||||
for (int i = 0; i < 0x4000; i++) {
|
||||
data[i] = rom()->data()[kGfxFont + i];
|
||||
}
|
||||
font_gfx16_data = gfx::SnesTo8bppSheet(data, /*bpp=*/2);
|
||||
font_gfx16_data_ = gfx::SnesTo8bppSheet(data, /*bpp=*/2, /*num_sheets=*/2);
|
||||
|
||||
// 4bpp
|
||||
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
|
||||
128, 128, 8, font_gfx16_data, font_gfx_bitmap_, font_preview_colors_))
|
||||
RETURN_IF_ERROR(Renderer::GetInstance().CreateAndRenderBitmap(
|
||||
kFontGfxMessageSize, kFontGfxMessageSize, kFontGfxMessageDepth,
|
||||
font_gfx16_data_, font_gfx_bitmap_, font_preview_colors_))
|
||||
|
||||
current_font_gfx16_data_.reserve(172 * 4096);
|
||||
for (int i = 0; i < 172 * 4096; i++) {
|
||||
current_font_gfx16_data_.reserve(kCurrentMessageWidth *
|
||||
kCurrentMessageHeight);
|
||||
for (int i = 0; i < kCurrentMessageWidth * kCurrentMessageHeight; i++) {
|
||||
current_font_gfx16_data_.push_back(0);
|
||||
}
|
||||
|
||||
// 8bpp
|
||||
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
|
||||
172, 4096, 64, current_font_gfx16_data_, current_font_gfx16_bitmap_,
|
||||
font_preview_colors_))
|
||||
RETURN_IF_ERROR(Renderer::GetInstance().CreateAndRenderBitmap(
|
||||
kCurrentMessageWidth, kCurrentMessageHeight, 64, current_font_gfx16_data_,
|
||||
current_font_gfx16_bitmap_, font_preview_colors_))
|
||||
|
||||
gfx::SnesPalette color_palette = font_gfx_bitmap_.palette();
|
||||
for (int i = 0; i < font_preview_colors_.size(); i++) {
|
||||
@@ -280,11 +86,11 @@ absl::Status MessageEditor::Initialize() {
|
||||
|
||||
*font_gfx_bitmap_.mutable_palette() = color_palette;
|
||||
|
||||
for (const auto& message : ListOfTexts) {
|
||||
DisplayedMessages.push_back(message);
|
||||
}
|
||||
for (const auto& each_message : list_of_texts_) {
|
||||
std::cout << "Message #" << each_message.ID << " at address "
|
||||
<< core::UppercaseHexLong(each_message.Address) << std::endl;
|
||||
std::cout << " " << each_message.RawString << std::endl;
|
||||
|
||||
for (const auto& each_message : ListOfTexts) {
|
||||
// Each string has a [:XX] char encoded
|
||||
// The corresponding character is found in CharEncoder unordered_map
|
||||
std::string parsed_message = "";
|
||||
@@ -313,7 +119,8 @@ absl::Status MessageEditor::Initialize() {
|
||||
}
|
||||
}
|
||||
}
|
||||
ParsedMessages.push_back(parsed_message);
|
||||
std::cout << " > " << parsed_message << std::endl;
|
||||
parsed_messages_.push_back(parsed_message);
|
||||
}
|
||||
|
||||
DrawMessagePreview();
|
||||
@@ -321,38 +128,245 @@ absl::Status MessageEditor::Initialize() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void MessageEditor::BuildDictionaryEntries() {
|
||||
for (int i = 0; i < 97; i++) {
|
||||
std::vector<uint8_t> bytes;
|
||||
std::stringstream stringBuilder;
|
||||
|
||||
int address = core::SnesToPc(
|
||||
0x0E0000 + (rom()->data()[kPointersDictionaries + (i * 2) + 1] << 8) +
|
||||
rom()->data()[kPointersDictionaries + (i * 2)]);
|
||||
|
||||
int temppush_backress = core::SnesToPc(
|
||||
0x0E0000 +
|
||||
(rom()->data()[kPointersDictionaries + ((i + 1) * 2) + 1] << 8) +
|
||||
rom()->data()[kPointersDictionaries + ((i + 1) * 2)]);
|
||||
|
||||
while (address < temppush_backress) {
|
||||
uint8_t uint8_tDictionary = rom()->data()[address++];
|
||||
bytes.push_back(uint8_tDictionary);
|
||||
stringBuilder << ParseTextDataByte(uint8_tDictionary);
|
||||
}
|
||||
|
||||
// AllDictionaries[i] = DictionaryEntry{(uint8_t)i, stringBuilder.str()};
|
||||
AllDictionaries.push_back(DictionaryEntry{(uint8_t)i, stringBuilder.str()});
|
||||
absl::Status MessageEditor::Update() {
|
||||
if (rom()->is_loaded() && !data_loaded_) {
|
||||
RETURN_IF_ERROR(Initialize());
|
||||
current_message_ = list_of_texts_[1];
|
||||
data_loaded_ = true;
|
||||
}
|
||||
|
||||
// AllDictionaries.OrderByDescending(dictionary = > dictionary.Length);
|
||||
AllDictionaries[0].Length = 0;
|
||||
if (BeginTable("##MessageEditor", 4, kDictTableFlags)) {
|
||||
TableSetupColumn("List");
|
||||
TableSetupColumn("Contents");
|
||||
TableSetupColumn("Commands");
|
||||
TableSetupColumn("Dictionary");
|
||||
|
||||
TableHeadersRow();
|
||||
|
||||
TableNextColumn();
|
||||
DrawMessageList();
|
||||
|
||||
TableNextColumn();
|
||||
DrawCurrentMessage();
|
||||
|
||||
TableNextColumn();
|
||||
DrawTextCommands();
|
||||
|
||||
TableNextColumn();
|
||||
DrawDictionary();
|
||||
|
||||
EndTable();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void MessageEditor::DrawMessageList() {
|
||||
if (InputText("Search", &search_text_)) {
|
||||
// TODO: ImGui style text filtering
|
||||
}
|
||||
|
||||
if (BeginChild("##MessagesList", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
if (BeginTable("##MessagesTable", 3, kMessageTableFlags)) {
|
||||
TableSetupColumn("ID");
|
||||
TableSetupColumn("Contents");
|
||||
TableSetupColumn("Data");
|
||||
|
||||
TableHeadersRow();
|
||||
|
||||
for (const auto& message : list_of_texts_) {
|
||||
TableNextColumn();
|
||||
if (Button(core::UppercaseHexWord(message.ID).c_str())) {
|
||||
current_message_ = message;
|
||||
DrawMessagePreview();
|
||||
}
|
||||
TableNextColumn();
|
||||
TextWrapped("%s", parsed_messages_[message.ID].c_str());
|
||||
TableNextColumn();
|
||||
TextWrapped(
|
||||
"%s",
|
||||
core::UppercaseHexLong(list_of_texts_[message.ID].Address).c_str());
|
||||
}
|
||||
|
||||
EndTable();
|
||||
}
|
||||
EndChild();
|
||||
}
|
||||
}
|
||||
|
||||
void MessageEditor::DrawCurrentMessage() {
|
||||
Button(absl::StrCat("Message ", current_message_.ID).c_str());
|
||||
if (InputTextMultiline("##MessageEditor",
|
||||
&parsed_messages_[current_message_.ID],
|
||||
ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
|
||||
current_message_.Data = ParseMessageToData(message_text_box_.text);
|
||||
DrawMessagePreview();
|
||||
}
|
||||
Separator();
|
||||
|
||||
Text("Font Graphics");
|
||||
gui::BeginPadding(1);
|
||||
BeginChild("MessageEditorCanvas", ImVec2(0, 130));
|
||||
font_gfx_canvas_.DrawBackground();
|
||||
font_gfx_canvas_.DrawContextMenu();
|
||||
font_gfx_canvas_.DrawBitmap(font_gfx_bitmap_, 0, 0);
|
||||
font_gfx_canvas_.DrawGrid();
|
||||
font_gfx_canvas_.DrawOverlay();
|
||||
EndChild();
|
||||
gui::EndPadding();
|
||||
Separator();
|
||||
|
||||
Text("Message Preview");
|
||||
if (Button("Refresh Bitmap")) {
|
||||
Renderer::GetInstance().UpdateBitmap(¤t_font_gfx16_bitmap_);
|
||||
}
|
||||
gui::BeginPadding(1);
|
||||
BeginChild("CurrentGfxFont", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||
current_font_gfx16_canvas_.DrawBackground();
|
||||
gui::EndPadding();
|
||||
current_font_gfx16_canvas_.DrawContextMenu();
|
||||
current_font_gfx16_canvas_.DrawBitmap(current_font_gfx16_bitmap_, 0, 0);
|
||||
current_font_gfx16_canvas_.DrawGrid();
|
||||
current_font_gfx16_canvas_.DrawOverlay();
|
||||
EndChild();
|
||||
}
|
||||
|
||||
void MessageEditor::DrawTextCommands() {
|
||||
if (BeginChild("##TextCommands", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
for (const auto& text_element : TextCommands) {
|
||||
if (Button(text_element.GenericToken.c_str())) {
|
||||
}
|
||||
SameLine();
|
||||
TextWrapped("%s", text_element.Description.c_str());
|
||||
Separator();
|
||||
}
|
||||
EndChild();
|
||||
}
|
||||
}
|
||||
|
||||
void MessageEditor::DrawDictionary() {
|
||||
if (ImGui::BeginChild("##DictionaryChild", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
if (BeginTable("##Dictionary", 2, kDictTableFlags)) {
|
||||
TableSetupColumn("ID");
|
||||
TableSetupColumn("Contents");
|
||||
|
||||
for (const auto& dictionary : all_dictionaries_) {
|
||||
TableNextColumn();
|
||||
Text("%s", core::UppercaseHexWord(dictionary.ID).c_str());
|
||||
TableNextColumn();
|
||||
Text("%s", dictionary.Contents.c_str());
|
||||
}
|
||||
EndTable();
|
||||
}
|
||||
|
||||
EndChild();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Fix the command parsing.
|
||||
void MessageEditor::ReadAllTextDataV2() {
|
||||
// Read all text data from the ROM.
|
||||
int pos = kTextData;
|
||||
int message_id = 0;
|
||||
|
||||
std::vector<uint8_t> raw_message;
|
||||
std::vector<uint8_t> parsed_message;
|
||||
|
||||
std::string current_raw_message;
|
||||
std::string current_parsed_message;
|
||||
|
||||
uint8_t current_byte = 0;
|
||||
while (current_byte != 0xFF) {
|
||||
current_byte = rom()->data()[pos++];
|
||||
if (current_byte == kMessageTerminator) {
|
||||
auto message =
|
||||
MessageData(message_id++, pos, current_raw_message, raw_message,
|
||||
current_parsed_message, parsed_message);
|
||||
|
||||
list_of_texts_.push_back(message);
|
||||
|
||||
raw_message.clear();
|
||||
parsed_message.clear();
|
||||
current_raw_message.clear();
|
||||
current_parsed_message.clear();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
raw_message.push_back(current_byte);
|
||||
|
||||
// Check for command.
|
||||
TextElement text_element = FindMatchingCommand(current_byte);
|
||||
if (!text_element.Empty()) {
|
||||
parsed_message.push_back(current_byte);
|
||||
if (text_element.HasArgument) {
|
||||
current_byte = rom()->data()[pos++];
|
||||
raw_message.push_back(current_byte);
|
||||
parsed_message.push_back(current_byte);
|
||||
}
|
||||
|
||||
current_raw_message.append(
|
||||
text_element.GetParameterizedToken(current_byte));
|
||||
current_parsed_message.append(
|
||||
text_element.GetParameterizedToken(current_byte));
|
||||
|
||||
if (text_element.Token == BANKToken) {
|
||||
pos = kTextData2;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for special characters.
|
||||
text_element = FindMatchingSpecial(current_byte);
|
||||
if (!text_element.Empty()) {
|
||||
current_raw_message.append(text_element.GetParameterizedToken());
|
||||
current_parsed_message.append(text_element.GetParameterizedToken());
|
||||
parsed_message.push_back(current_byte);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for dictionary.
|
||||
int dictionary = FindDictionaryEntry(current_byte);
|
||||
if (dictionary >= 0) {
|
||||
current_raw_message.append("[");
|
||||
current_raw_message.append(DICTIONARYTOKEN);
|
||||
current_raw_message.append(":");
|
||||
current_raw_message.append(core::UppercaseHexWord(dictionary));
|
||||
current_raw_message.append("]");
|
||||
|
||||
uint32_t address = core::Get24LocalFromPC(
|
||||
rom()->data(), kPointersDictionaries + (dictionary * 2));
|
||||
uint32_t address_end = core::Get24LocalFromPC(
|
||||
rom()->data(), kPointersDictionaries + ((dictionary + 1) * 2));
|
||||
|
||||
for (uint32_t i = address; i < address_end; i++) {
|
||||
parsed_message.push_back(rom()->data()[i]);
|
||||
current_parsed_message.append(ParseTextDataByte(rom()->data()[i]));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Everything else.
|
||||
if (CharEncoder.contains(current_byte)) {
|
||||
std::string str = "";
|
||||
str.push_back(CharEncoder.at(current_byte));
|
||||
current_raw_message.append(str);
|
||||
current_parsed_message.append(str);
|
||||
parsed_message.push_back(current_byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageEditor::ReadAllTextData() {
|
||||
int messageID = 0;
|
||||
uint8_t current_byte;
|
||||
int pos = kTextData;
|
||||
int message_id = 0;
|
||||
uint8_t current_byte;
|
||||
std::vector<uint8_t> temp_bytes_raw;
|
||||
std::vector<uint8_t> temp_bytes_parsed;
|
||||
|
||||
@@ -363,12 +377,12 @@ void MessageEditor::ReadAllTextData() {
|
||||
while (true) {
|
||||
current_byte = rom()->data()[pos++];
|
||||
|
||||
if (current_byte == MESSAGETERMINATOR) {
|
||||
if (current_byte == kMessageTerminator) {
|
||||
auto message =
|
||||
MessageData(messageID++, pos, current_message_raw, temp_bytes_raw,
|
||||
MessageData(message_id++, pos, current_message_raw, temp_bytes_raw,
|
||||
current_message_parsed, temp_bytes_parsed);
|
||||
|
||||
ListOfTexts.push_back(message);
|
||||
list_of_texts_.push_back(message);
|
||||
|
||||
temp_bytes_raw.clear();
|
||||
temp_bytes_parsed.clear();
|
||||
@@ -407,7 +421,6 @@ void MessageEditor::ReadAllTextData() {
|
||||
|
||||
// Check for special characters.
|
||||
text_element = FindMatchingSpecial(current_byte);
|
||||
|
||||
if (!text_element.Empty()) {
|
||||
current_message_raw.append(text_element.GetParameterizedToken());
|
||||
current_message_parsed.append(text_element.GetParameterizedToken());
|
||||
@@ -449,83 +462,29 @@ void MessageEditor::ReadAllTextData() {
|
||||
}
|
||||
}
|
||||
|
||||
TextElement MessageEditor::FindMatchingCommand(uint8_t b) {
|
||||
TextElement empty_element;
|
||||
for (const auto text_element : TextCommands) {
|
||||
if (text_element.ID == b) {
|
||||
return text_element;
|
||||
std::string ReplaceAllDictionaryWords(std::string str,
|
||||
std::vector<DictionaryEntry> dictionary) {
|
||||
std::string temp = str;
|
||||
for (const auto& entry : dictionary) {
|
||||
if (absl::StrContains(temp, entry.Contents)) {
|
||||
temp = absl::StrReplaceAll(temp, {{entry.Contents, entry.Contents}});
|
||||
}
|
||||
}
|
||||
return empty_element;
|
||||
return temp;
|
||||
}
|
||||
|
||||
TextElement MessageEditor::FindMatchingSpecial(uint8_t value) {
|
||||
TextElement empty_element;
|
||||
for (const auto text_element : SpecialChars) {
|
||||
if (text_element.ID == value) {
|
||||
return text_element;
|
||||
}
|
||||
}
|
||||
return empty_element;
|
||||
}
|
||||
|
||||
MessageEditor::DictionaryEntry MessageEditor::GetDictionaryFromID(
|
||||
uint8_t value) {
|
||||
if (value < 0 || value >= AllDictionaries.size()) {
|
||||
DictionaryEntry MessageEditor::GetDictionaryFromID(uint8_t value) {
|
||||
if (value < 0 || value >= all_dictionaries_.size()) {
|
||||
return DictionaryEntry();
|
||||
}
|
||||
return AllDictionaries[value];
|
||||
}
|
||||
|
||||
uint8_t MessageEditor::FindDictionaryEntry(uint8_t value) {
|
||||
if (value < DICTOFF || value == 0xFF) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return value - DICTOFF;
|
||||
}
|
||||
|
||||
uint8_t MessageEditor::FindMatchingCharacter(char value) {
|
||||
for (const auto [key, char_value] : CharEncoder) {
|
||||
if (value == char_value) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
string MessageEditor::ParseTextDataByte(uint8_t value) {
|
||||
if (CharEncoder.contains(value)) {
|
||||
char c = CharEncoder.at(value);
|
||||
string str = "";
|
||||
str.push_back(c);
|
||||
return str;
|
||||
}
|
||||
|
||||
// Check for command.
|
||||
TextElement textElement = FindMatchingCommand(value);
|
||||
if (!textElement.Empty()) {
|
||||
return textElement.GenericToken;
|
||||
}
|
||||
|
||||
// Check for special characters.
|
||||
textElement = FindMatchingSpecial(value);
|
||||
if (!textElement.Empty()) {
|
||||
return textElement.GenericToken;
|
||||
}
|
||||
|
||||
// Check for dictionary.
|
||||
int dictionary = FindDictionaryEntry(value);
|
||||
if (dictionary >= 0) {
|
||||
return absl::StrFormat("[%s:%X]", DICTIONARYTOKEN, dictionary);
|
||||
}
|
||||
|
||||
return "";
|
||||
return all_dictionaries_[value];
|
||||
}
|
||||
|
||||
void MessageEditor::DrawTileToPreview(int x, int y, int srcx, int srcy, int pal,
|
||||
int sizex, int sizey) {
|
||||
int drawid = srcx + (srcy * 32);
|
||||
const int num_x_tiles = 16;
|
||||
const int img_width = 512; // (imgwidth/2)
|
||||
int draw_id = srcx + (srcy * 32);
|
||||
for (int yl = 0; yl < sizey * 8; yl++) {
|
||||
for (int xl = 0; xl < 4; xl++) {
|
||||
int mx = xl;
|
||||
@@ -533,8 +492,9 @@ void MessageEditor::DrawTileToPreview(int x, int y, int srcx, int srcy, int pal,
|
||||
|
||||
// Formula information to get tile index position in the array.
|
||||
// ((ID / nbrofXtiles) * (imgwidth/2) + (ID - ((ID/16)*16) ))
|
||||
int tx = ((drawid / 16) * 512) + ((drawid - ((drawid / 16) * 16)) * 4);
|
||||
uint8_t pixel = font_gfx16_data[tx + (yl * 64) + xl];
|
||||
int tx = ((draw_id / num_x_tiles) * img_width) +
|
||||
((draw_id - ((draw_id / 16) * 16)) * 4);
|
||||
uint8_t pixel = font_gfx16_data_[tx + (yl * 64) + xl];
|
||||
|
||||
// nx,ny = object position, xx,yy = tile position, xl,yl = pixel
|
||||
// position
|
||||
@@ -552,7 +512,7 @@ void MessageEditor::DrawTileToPreview(int x, int y, int srcx, int srcy, int pal,
|
||||
}
|
||||
}
|
||||
|
||||
void MessageEditor::DrawStringToPreview(string str) {
|
||||
void MessageEditor::DrawStringToPreview(std::string str) {
|
||||
for (const auto c : str) {
|
||||
DrawCharacterToPreview(c);
|
||||
}
|
||||
@@ -573,25 +533,25 @@ void MessageEditor::DrawCharacterToPreview(const std::vector<uint8_t>& text) {
|
||||
int srcy = value / 16;
|
||||
int srcx = value - (value & (~0xF));
|
||||
|
||||
if (text_pos >= 170) {
|
||||
text_pos = 0;
|
||||
text_line++;
|
||||
if (text_position_ >= 170) {
|
||||
text_position_ = 0;
|
||||
text_line_++;
|
||||
}
|
||||
|
||||
DrawTileToPreview(text_pos, text_line * 16, srcx, srcy, 0, 1, 2);
|
||||
text_pos += width_array[value];
|
||||
DrawTileToPreview(text_position_, text_line_ * 16, srcx, srcy, 0, 1, 2);
|
||||
text_position_ += width_array[value];
|
||||
} else if (value == kLine1) {
|
||||
text_pos = 0;
|
||||
text_line = 0;
|
||||
text_position_ = 0;
|
||||
text_line_ = 0;
|
||||
} else if (value == kScrollVertical) {
|
||||
text_pos = 0;
|
||||
text_line += 1;
|
||||
text_position_ = 0;
|
||||
text_line_ += 1;
|
||||
} else if (value == kLine2) {
|
||||
text_pos = 0;
|
||||
text_line = 1;
|
||||
text_position_ = 0;
|
||||
text_line_ = 1;
|
||||
} else if (value == kLine3) {
|
||||
text_pos = 0;
|
||||
text_line = 2;
|
||||
text_position_ = 0;
|
||||
text_line_ = 2;
|
||||
} else if (value == 0x6B || value == 0x6D || value == 0x6E ||
|
||||
value == 0x77 || value == 0x78 || value == 0x79 ||
|
||||
value == 0x7A) {
|
||||
@@ -615,15 +575,15 @@ void MessageEditor::DrawCharacterToPreview(const std::vector<uint8_t>& text) {
|
||||
}
|
||||
}
|
||||
|
||||
void MessageEditor::DrawMessagePreview() // From Parsing.
|
||||
{
|
||||
text_line = 0;
|
||||
void MessageEditor::DrawMessagePreview() {
|
||||
// From Parsing.
|
||||
text_line_ = 0;
|
||||
for (int i = 0; i < (172 * 4096); i++) {
|
||||
current_font_gfx16_data_[i] = 0;
|
||||
}
|
||||
text_pos = 0;
|
||||
DrawCharacterToPreview(CurrentMessage.Data);
|
||||
shown_lines = 0;
|
||||
text_position_ = 0;
|
||||
DrawCharacterToPreview(current_message_.Data);
|
||||
shown_lines_ = 0;
|
||||
}
|
||||
|
||||
absl::Status MessageEditor::Cut() {
|
||||
@@ -675,7 +635,7 @@ absl::Status MessageEditor::Save() {
|
||||
int pos = kTextData;
|
||||
bool in_second_bank = false;
|
||||
|
||||
for (const auto& message : ListOfTexts) {
|
||||
for (const auto& message : list_of_texts_) {
|
||||
for (const auto value : message.Data) {
|
||||
RETURN_IF_ERROR(rom()->Write(pos, value));
|
||||
|
||||
@@ -695,7 +655,7 @@ absl::Status MessageEditor::Save() {
|
||||
}
|
||||
|
||||
RETURN_IF_ERROR(
|
||||
rom()->Write(pos++, MESSAGETERMINATOR)); // , true, "Terminator text"
|
||||
rom()->Write(pos++, kMessageTerminator)); // , true, "Terminator text"
|
||||
}
|
||||
|
||||
// Verify that we didn't go over the space available for the second block.
|
||||
@@ -712,9 +672,10 @@ absl::Status MessageEditor::Save() {
|
||||
|
||||
std::string MessageEditor::DisplayTextOverflowError(int pos, bool bank) {
|
||||
int space = bank ? kTextDataEnd - kTextData : kTextData2End - kTextData2;
|
||||
string bankSTR = bank ? "1st" : "2nd";
|
||||
string posSTR = bank ? absl::StrFormat("%X4", pos & 0xFFFF)
|
||||
: absl::StrFormat("%X4", (pos - kTextData2) & 0xFFFF);
|
||||
std::string bankSTR = bank ? "1st" : "2nd";
|
||||
std::string posSTR =
|
||||
bank ? absl::StrFormat("%X4", pos & 0xFFFF)
|
||||
: absl::StrFormat("%X4", (pos - kTextData2) & 0xFFFF);
|
||||
std::string message = absl::StrFormat(
|
||||
"There is too much text data in the %s block to save.\n"
|
||||
"Available: %X4 | Used: %s",
|
||||
@@ -722,30 +683,6 @@ std::string MessageEditor::DisplayTextOverflowError(int pos, bool bank) {
|
||||
return message;
|
||||
}
|
||||
|
||||
// push_backs a command to the text field when the push_back command button is
|
||||
// pressed or the command is double clicked in the list.
|
||||
void MessageEditor::InsertCommandButton_Click_1() {
|
||||
// InsertSelectedText(
|
||||
// TextCommands[TextCommandList.SelectedIndex].GetParameterizedToken(
|
||||
// (uint8_t)ParamsBox.HexValue));
|
||||
}
|
||||
|
||||
// push_backs a special character to the text field when the push_back command
|
||||
// button is pressed or the character is double clicked in the list.
|
||||
void MessageEditor::InsertSpecialButton_Click() {
|
||||
// InsertSelectedText(
|
||||
// SpecialChars[SpecialsList.SelectedIndex].GetParameterizedToken());
|
||||
}
|
||||
|
||||
void MessageEditor::InsertSelectedText(string str) {
|
||||
int textboxPos = message_text_box_.selection_start;
|
||||
from_form = true;
|
||||
// message_text_box_.Text = message_text_box_.Text.Insert(textboxPos, str);
|
||||
from_form = false;
|
||||
message_text_box_.selection_start = textboxPos + str.size();
|
||||
message_text_box_.Focus();
|
||||
}
|
||||
|
||||
void MessageEditor::Delete() {
|
||||
// Determine if any text is selected in the TextBox control.
|
||||
if (message_text_box_.selection_length == 0) {
|
||||
|
||||
@@ -1,229 +1,54 @@
|
||||
#ifndef YAZE_APP_EDITOR_MESSAGE_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_MESSAGE_EDITOR_H
|
||||
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/str_replace.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "app/editor/message/message_data.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
using std::string;
|
||||
|
||||
// TEXT EDITOR RELATED CONSTANTS
|
||||
const int kGfxFont = 0x70000; // 2bpp format
|
||||
const int kTextData = 0xE0000;
|
||||
const int kTextDataEnd = 0xE7FFF;
|
||||
const int kTextData2 = 0x75F40;
|
||||
const int kTextData2End = 0x773FF;
|
||||
const int kPointersDictionaries = 0x74703;
|
||||
const int kCharactersWidth = 0x74ADF;
|
||||
|
||||
const string DICTIONARYTOKEN = "D";
|
||||
const uint8_t DICTOFF = 0x88;
|
||||
const string BANKToken = "BANK";
|
||||
const uint8_t BANKID = 0x80;
|
||||
constexpr int kGfxFont = 0x70000; // 2bpp format
|
||||
constexpr int kTextData2 = 0x75F40;
|
||||
constexpr int kTextData2End = 0x773FF;
|
||||
constexpr int kCharactersWidth = 0x74ADF;
|
||||
constexpr int kNumMessages = 396;
|
||||
constexpr int kCurrentMessageWidth = 172;
|
||||
constexpr int kCurrentMessageHeight = 4096;
|
||||
constexpr int kFontGfxMessageSize = 128;
|
||||
constexpr int kFontGfxMessageDepth = 8;
|
||||
|
||||
constexpr uint8_t kWidthArraySize = 100;
|
||||
constexpr uint8_t kBlockTerminator = 0x80;
|
||||
|
||||
static std::vector<uint8_t> ParseMessageToData(string str);
|
||||
|
||||
static ParsedElement FindMatchingElement(string str);
|
||||
|
||||
constexpr uint8_t kMessageBankChangeId = 0x80;
|
||||
constexpr uint8_t kScrollVertical = 0x73;
|
||||
constexpr uint8_t kLine1 = 0x74;
|
||||
constexpr uint8_t kLine2 = 0x75;
|
||||
constexpr uint8_t kLine3 = 0x76;
|
||||
|
||||
static const TextElement TextCommands[] = {
|
||||
TextElement(0x6B, "W", true, "Window border"),
|
||||
TextElement(0x6D, "P", true, "Window position"),
|
||||
TextElement(0x6E, "SPD", true, "Scroll speed"),
|
||||
TextElement(0x7A, "S", true, "Text draw speed"),
|
||||
TextElement(0x77, "C", true, "Text color"),
|
||||
TextElement(0x6A, "L", false, "Player name"),
|
||||
TextElement(0x74, "1", false, "Line 1"),
|
||||
TextElement(0x75, "2", false, "Line 2"),
|
||||
TextElement(0x76, "3", false, "Line 3"),
|
||||
TextElement(0x7E, "K", false, "Wait for key"),
|
||||
TextElement(0x73, "V", false, "Scroll text"),
|
||||
TextElement(0x78, "WT", true, "Delay X"),
|
||||
TextElement(0x6C, "N", true, "BCD number"),
|
||||
TextElement(0x79, "SFX", true, "Sound effect"),
|
||||
TextElement(0x71, "CH3", false, "Choose 3"),
|
||||
TextElement(0x72, "CH2", false, "Choose 2 high"),
|
||||
TextElement(0x6F, "CH2L", false, "Choose 2 low"),
|
||||
TextElement(0x68, "CH2I", false, "Choose 2 indented"),
|
||||
TextElement(0x69, "CHI", false, "Choose item"),
|
||||
TextElement(0x67, "IMG", false, "Next attract image"),
|
||||
TextElement(0x80, BANKToken, false, "Bank marker (automatic)"),
|
||||
TextElement(0x70, "NONO", false, "Crash"),
|
||||
};
|
||||
|
||||
static std::vector<TextElement> SpecialChars = {
|
||||
TextElement(0x43, "...", false, "Ellipsis …"),
|
||||
TextElement(0x4D, "UP", false, "Arrow ↑"),
|
||||
TextElement(0x4E, "DOWN", false, "Arrow ↓"),
|
||||
TextElement(0x4F, "LEFT", false, "Arrow ←"),
|
||||
TextElement(0x50, "RIGHT", false, "Arrow →"),
|
||||
TextElement(0x5B, "A", false, "Button Ⓐ"),
|
||||
TextElement(0x5C, "B", false, "Button Ⓑ"),
|
||||
TextElement(0x5D, "X", false, "Button ⓧ"),
|
||||
TextElement(0x5E, "Y", false, "Button ⓨ"),
|
||||
TextElement(0x52, "HP1L", false, "1 HP left"),
|
||||
TextElement(0x53, "HP1R", false, "1 HP right"),
|
||||
TextElement(0x54, "HP2L", false, "2 HP left"),
|
||||
TextElement(0x55, "HP3L", false, "3 HP left"),
|
||||
TextElement(0x56, "HP3R", false, "3 HP right"),
|
||||
TextElement(0x57, "HP4L", false, "4 HP left"),
|
||||
TextElement(0x58, "HP4R", false, "4 HP right"),
|
||||
TextElement(0x47, "HY0", false, "Hieroglyph ☥"),
|
||||
TextElement(0x48, "HY1", false, "Hieroglyph 𓈗"),
|
||||
TextElement(0x49, "HY2", false, "Hieroglyph Ƨ"),
|
||||
TextElement(0x4A, "LFL", false, "Link face left"),
|
||||
TextElement(0x4B, "LFR", false, "Link face right"),
|
||||
};
|
||||
|
||||
static const std::unordered_map<uint8_t, wchar_t> CharEncoder = {
|
||||
{0x00, 'A'},
|
||||
{0x01, 'B'},
|
||||
{0x02, 'C'},
|
||||
{0x03, 'D'},
|
||||
{0x04, 'E'},
|
||||
{0x05, 'F'},
|
||||
{0x06, 'G'},
|
||||
{0x07, 'H'},
|
||||
{0x08, 'I'},
|
||||
{0x09, 'J'},
|
||||
{0x0A, 'K'},
|
||||
{0x0B, 'L'},
|
||||
{0x0C, 'M'},
|
||||
{0x0D, 'N'},
|
||||
{0x0E, 'O'},
|
||||
{0x0F, 'P'},
|
||||
{0x10, 'Q'},
|
||||
{0x11, 'R'},
|
||||
{0x12, 'S'},
|
||||
{0x13, 'T'},
|
||||
{0x14, 'U'},
|
||||
{0x15, 'V'},
|
||||
{0x16, 'W'},
|
||||
{0x17, 'X'},
|
||||
{0x18, 'Y'},
|
||||
{0x19, 'Z'},
|
||||
{0x1A, 'a'},
|
||||
{0x1B, 'b'},
|
||||
{0x1C, 'c'},
|
||||
{0x1D, 'd'},
|
||||
{0x1E, 'e'},
|
||||
{0x1F, 'f'},
|
||||
{0x20, 'g'},
|
||||
{0x21, 'h'},
|
||||
{0x22, 'i'},
|
||||
{0x23, 'j'},
|
||||
{0x24, 'k'},
|
||||
{0x25, 'l'},
|
||||
{0x26, 'm'},
|
||||
{0x27, 'n'},
|
||||
{0x28, 'o'},
|
||||
{0x29, 'p'},
|
||||
{0x2A, 'q'},
|
||||
{0x2B, 'r'},
|
||||
{0x2C, 's'},
|
||||
{0x2D, 't'},
|
||||
{0x2E, 'u'},
|
||||
{0x2F, 'v'},
|
||||
{0x30, 'w'},
|
||||
{0x31, 'x'},
|
||||
{0x32, 'y'},
|
||||
{0x33, 'z'},
|
||||
{0x34, '0'},
|
||||
{0x35, '1'},
|
||||
{0x36, '2'},
|
||||
{0x37, '3'},
|
||||
{0x38, '4'},
|
||||
{0x39, '5'},
|
||||
{0x3A, '6'},
|
||||
{0x3B, '7'},
|
||||
{0x3C, '8'},
|
||||
{0x3D, '9'},
|
||||
{0x3E, '!'},
|
||||
{0x3F, '?'},
|
||||
{0x40, '-'},
|
||||
{0x41, '.'},
|
||||
{0x42, ','},
|
||||
{0x44, '>'},
|
||||
{0x45, '('},
|
||||
{0x46, ')'},
|
||||
{0x4C, '"'},
|
||||
{0x51, '\''},
|
||||
{0x59, ' '},
|
||||
{0x5A, '<'},
|
||||
// {0x5F, '¡'}, {0x60, '¡'}, {0x61, '¡'}, {0x62, ' '}, {0x63, ' '}, {0x64,
|
||||
// ' '},
|
||||
{0x65, ' '},
|
||||
{0x66, '_'},
|
||||
};
|
||||
|
||||
static TextElement DictionaryElement =
|
||||
TextElement(0x80, DICTIONARYTOKEN, true, "Dictionary");
|
||||
|
||||
class MessageEditor : public Editor, public SharedRom {
|
||||
public:
|
||||
struct DictionaryEntry {
|
||||
uint8_t ID;
|
||||
std::string Contents;
|
||||
std::vector<uint8_t> Data;
|
||||
int Length;
|
||||
std::string Token;
|
||||
|
||||
DictionaryEntry() = default;
|
||||
DictionaryEntry(uint8_t i, std::string s)
|
||||
: Contents(s), ID(i), Length(s.length()) {
|
||||
Token = absl::StrFormat("[%s:%00X]", DICTIONARYTOKEN, ID);
|
||||
Data = ParseMessageToData(Contents);
|
||||
}
|
||||
|
||||
bool ContainedInString(std::string s) {
|
||||
return s.find(Contents) != std::string::npos;
|
||||
}
|
||||
|
||||
std::string ReplaceInstancesOfIn(std::string s) {
|
||||
std::string replacedString = s;
|
||||
size_t pos = replacedString.find(Contents);
|
||||
while (pos != std::string::npos) {
|
||||
replacedString.replace(pos, Contents.length(), Token);
|
||||
pos = replacedString.find(Contents, pos + Token.length());
|
||||
}
|
||||
return replacedString;
|
||||
}
|
||||
};
|
||||
|
||||
MessageEditor() { type_ = EditorType::kMessage; }
|
||||
|
||||
absl::Status Initialize();
|
||||
absl::Status Update() override;
|
||||
void DrawMessageList();
|
||||
void DrawCurrentMessage();
|
||||
void DrawTextCommands();
|
||||
void DrawDictionary();
|
||||
|
||||
absl::Status Initialize();
|
||||
void ReadAllTextData();
|
||||
void BuildDictionaryEntries();
|
||||
void ReadAllTextDataV2();
|
||||
[[deprecated]] void ReadAllTextData();
|
||||
|
||||
absl::Status Cut() override;
|
||||
absl::Status Copy() override;
|
||||
@@ -238,67 +63,47 @@ class MessageEditor : public Editor, public SharedRom {
|
||||
absl::Status Save();
|
||||
void Delete();
|
||||
void SelectAll();
|
||||
// void RegisterTests(ImGuiTestEngine* e) override;
|
||||
|
||||
TextElement FindMatchingCommand(uint8_t byte);
|
||||
TextElement FindMatchingSpecial(uint8_t value);
|
||||
string ParseTextDataByte(uint8_t value);
|
||||
DictionaryEntry GetDictionaryFromID(uint8_t value);
|
||||
|
||||
static uint8_t FindDictionaryEntry(uint8_t value);
|
||||
static uint8_t FindMatchingCharacter(char value);
|
||||
void DrawTileToPreview(int x, int y, int srcx, int srcy, int pal,
|
||||
int sizex = 1, int sizey = 1);
|
||||
void DrawCharacterToPreview(char c);
|
||||
void DrawCharacterToPreview(const std::vector<uint8_t>& text);
|
||||
|
||||
void DrawStringToPreview(string str);
|
||||
void DrawStringToPreview(std::string str);
|
||||
void DrawMessagePreview();
|
||||
std::string DisplayTextOverflowError(int pos, bool bank);
|
||||
|
||||
void InsertCommandButton_Click_1();
|
||||
void InsertSpecialButton_Click();
|
||||
void InsertSelectedText(string str);
|
||||
|
||||
static const std::vector<DictionaryEntry> AllDicts;
|
||||
|
||||
uint8_t width_array[100];
|
||||
string romname = "";
|
||||
|
||||
int text_line = 0;
|
||||
int text_pos = 0;
|
||||
int shown_lines = 0;
|
||||
int selected_tile = 0;
|
||||
|
||||
bool skip_next = false;
|
||||
bool from_form = false;
|
||||
|
||||
std::vector<MessageData> ListOfTexts;
|
||||
std::vector<MessageData> DisplayedMessages;
|
||||
std::vector<std::string> ParsedMessages;
|
||||
|
||||
MessageData CurrentMessage;
|
||||
|
||||
private:
|
||||
static const TextElement DictionaryElement;
|
||||
|
||||
bool skip_next = false;
|
||||
bool data_loaded_ = false;
|
||||
int current_message_id_ = 0;
|
||||
|
||||
int text_line_ = 0;
|
||||
int text_position_ = 0;
|
||||
int shown_lines_ = 0;
|
||||
|
||||
uint8_t width_array[kWidthArraySize];
|
||||
|
||||
std::string search_text_ = "";
|
||||
|
||||
std::vector<uint8_t> font_gfx16_data_;
|
||||
std::vector<uint8_t> current_font_gfx16_data_;
|
||||
std::vector<std::string> parsed_messages_;
|
||||
|
||||
std::vector<MessageData> list_of_texts_;
|
||||
|
||||
std::vector<DictionaryEntry> all_dictionaries_;
|
||||
|
||||
MessageData current_message_;
|
||||
|
||||
gfx::Bitmap font_gfx_bitmap_;
|
||||
gfx::Bitmap current_font_gfx16_bitmap_;
|
||||
gfx::SnesPalette font_preview_colors_;
|
||||
|
||||
gui::Canvas font_gfx_canvas_{"##FontGfxCanvas", ImVec2(128, 128)};
|
||||
gui::Canvas current_font_gfx16_canvas_{"##CurrentMessageGfx",
|
||||
ImVec2(172, 4096)};
|
||||
|
||||
gfx::Bitmap font_gfx_bitmap_;
|
||||
gfx::Bitmap current_font_gfx16_bitmap_;
|
||||
|
||||
Bytes font_gfx16_data;
|
||||
Bytes current_font_gfx16_data_;
|
||||
|
||||
gfx::SnesPalette font_preview_colors_;
|
||||
|
||||
struct TextBox {
|
||||
std::string text;
|
||||
std::string buffer;
|
||||
@@ -357,8 +162,6 @@ class MessageEditor : public Editor, public SharedRom {
|
||||
TextBox message_text_box_;
|
||||
};
|
||||
|
||||
static std::vector<MessageEditor::DictionaryEntry> AllDictionaries;
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#include "message_editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -1,19 +1,15 @@
|
||||
#ifndef YAZE_APP_EDITOR_MUSIC_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_MUSIC_EDITOR_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/editor/code/assembly_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/music/tracker.h"
|
||||
// #include "snes_spc/demo/demo_util.h"
|
||||
// #include "snes_spc/demo/wave_writer.h"
|
||||
// #include "snes_spc/snes_spc/spc.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -81,20 +77,11 @@ class MusicEditor : public SharedRom, public Editor {
|
||||
|
||||
zelda3::music::Tracker music_tracker_;
|
||||
|
||||
// Mix_Music* current_song_ = NULL;
|
||||
|
||||
AssemblyEditor assembly_editor_;
|
||||
ImGuiTableFlags toolset_table_flags_ = ImGuiTableFlags_SizingFixedFit;
|
||||
ImGuiTableFlags music_editor_flags_ = ImGuiTableFlags_SizingFixedFit |
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_Reorderable;
|
||||
|
||||
ImGuiTableFlags channel_table_flags_ =
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable |
|
||||
ImGuiTableFlags_SortMulti | ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
|
||||
ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_ScrollY;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "app/editor/overworld/entity.h"
|
||||
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/style.h"
|
||||
|
||||
@@ -8,7 +9,6 @@ namespace app {
|
||||
namespace editor {
|
||||
|
||||
using ImGui::BeginChild;
|
||||
using ImGui::BeginGroup;
|
||||
using ImGui::Button;
|
||||
using ImGui::Checkbox;
|
||||
using ImGui::EndChild;
|
||||
@@ -16,7 +16,9 @@ using ImGui::SameLine;
|
||||
using ImGui::Selectable;
|
||||
using ImGui::Text;
|
||||
|
||||
bool IsMouseHoveringOverEntity(const zelda3::OverworldEntity &entity,
|
||||
constexpr float kInputFieldSize = 30.f;
|
||||
|
||||
bool IsMouseHoveringOverEntity(const zelda3::GameEntity &entity,
|
||||
ImVec2 canvas_p0, ImVec2 scrolling) {
|
||||
// Get the mouse position relative to the canvas
|
||||
const ImGuiIO &io = ImGui::GetIO();
|
||||
@@ -31,7 +33,7 @@ bool IsMouseHoveringOverEntity(const zelda3::OverworldEntity &entity,
|
||||
return false;
|
||||
}
|
||||
|
||||
void MoveEntityOnGrid(zelda3::OverworldEntity *entity, ImVec2 canvas_p0,
|
||||
void MoveEntityOnGrid(zelda3::GameEntity *entity, ImVec2 canvas_p0,
|
||||
ImVec2 scrolling, bool free_movement) {
|
||||
// Get the mouse position relative to the canvas
|
||||
const ImGuiIO &io = ImGui::GetIO();
|
||||
@@ -51,19 +53,20 @@ void MoveEntityOnGrid(zelda3::OverworldEntity *entity, ImVec2 canvas_p0,
|
||||
entity->set_y(new_y);
|
||||
}
|
||||
|
||||
void HandleEntityDragging(zelda3::OverworldEntity *entity, ImVec2 canvas_p0,
|
||||
void HandleEntityDragging(zelda3::GameEntity *entity, ImVec2 canvas_p0,
|
||||
ImVec2 scrolling, bool &is_dragging_entity,
|
||||
zelda3::OverworldEntity *&dragged_entity,
|
||||
zelda3::OverworldEntity *¤t_entity,
|
||||
zelda3::GameEntity *&dragged_entity,
|
||||
zelda3::GameEntity *¤t_entity,
|
||||
bool free_movement) {
|
||||
std::string entity_type = "Entity";
|
||||
if (entity->type_ == zelda3::OverworldEntity::EntityType::kExit) {
|
||||
if (entity->entity_type_ == zelda3::GameEntity::EntityType::kExit) {
|
||||
entity_type = "Exit";
|
||||
} else if (entity->type_ == zelda3::OverworldEntity::EntityType::kEntrance) {
|
||||
} else if (entity->entity_type_ ==
|
||||
zelda3::GameEntity::EntityType::kEntrance) {
|
||||
entity_type = "Entrance";
|
||||
} else if (entity->type_ == zelda3::OverworldEntity::EntityType::kSprite) {
|
||||
} else if (entity->entity_type_ == zelda3::GameEntity::EntityType::kSprite) {
|
||||
entity_type = "Sprite";
|
||||
} else if (entity->type_ == zelda3::OverworldEntity::EntityType::kItem) {
|
||||
} else if (entity->entity_type_ == zelda3::GameEntity::EntityType::kItem) {
|
||||
entity_type = "Item";
|
||||
}
|
||||
const auto is_hovering =
|
||||
@@ -87,7 +90,7 @@ void HandleEntityDragging(zelda3::OverworldEntity *entity, ImVec2 canvas_p0,
|
||||
} else if (is_dragging_entity && dragged_entity == entity) {
|
||||
if (ImGui::BeginDragDropSource()) {
|
||||
ImGui::SetDragDropPayload("ENTITY_PAYLOAD", &entity,
|
||||
sizeof(zelda3::OverworldEntity));
|
||||
sizeof(zelda3::GameEntity));
|
||||
Text("Moving %s ID: %s", entity_type.c_str(),
|
||||
core::UppercaseHexByte(entity->entity_id_).c_str());
|
||||
ImGui::EndDragDropSource();
|
||||
@@ -132,7 +135,7 @@ bool DrawOverworldEntrancePopup(
|
||||
}
|
||||
if (ImGui::BeginPopupModal("Entrance editor", NULL,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
gui::InputHex("Map ID", &entrance.map_id_);
|
||||
gui::InputHexWord("Map ID", &entrance.map_id_);
|
||||
gui::InputHexByte("Entrance ID", &entrance.entrance_id_,
|
||||
kInputFieldSize + 20);
|
||||
gui::InputHex("X", &entrance.x_);
|
||||
@@ -209,7 +212,7 @@ bool DrawExitEditorPopup(zelda3::overworld::OverworldExit &exit) {
|
||||
gui::InputHexWord("Room", &exit.room_id_);
|
||||
SameLine();
|
||||
gui::InputHex("Entity ID", &exit.entity_id_, 4);
|
||||
gui::InputHex("Map", &exit.map_id_);
|
||||
gui::InputHexWord("Map", &exit.map_id_);
|
||||
SameLine();
|
||||
Checkbox("Automatic", &exit.is_automatic_);
|
||||
|
||||
@@ -310,11 +313,11 @@ bool DrawExitEditorPopup(zelda3::overworld::OverworldExit &exit) {
|
||||
void DrawItemInsertPopup() {
|
||||
// Contents of the Context Menu
|
||||
if (ImGui::BeginPopup("Item Inserter")) {
|
||||
static int new_item_id = 0;
|
||||
static size_t new_item_id = 0;
|
||||
Text("Add Item");
|
||||
BeginChild("ScrollRegion", ImVec2(150, 150), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||
for (int i = 0; i < zelda3::overworld::kSecretItemNames.size(); i++) {
|
||||
for (size_t i = 0; i < zelda3::overworld::kSecretItemNames.size(); i++) {
|
||||
if (Selectable(zelda3::overworld::kSecretItemNames[i].c_str(),
|
||||
i == new_item_id)) {
|
||||
new_item_id = i;
|
||||
@@ -348,10 +351,10 @@ bool DrawItemEditorPopup(zelda3::overworld::OverworldItem &item) {
|
||||
BeginChild("ScrollRegion", ImVec2(150, 150), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||
ImGui::BeginGroup();
|
||||
for (int i = 0; i < zelda3::overworld::kSecretItemNames.size(); i++) {
|
||||
for (size_t i = 0; i < zelda3::overworld::kSecretItemNames.size(); i++) {
|
||||
if (Selectable(zelda3::overworld::kSecretItemNames[i].c_str(),
|
||||
item.id == i)) {
|
||||
item.id = i;
|
||||
item.id_ == i)) {
|
||||
item.id_ = i;
|
||||
}
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
@@ -384,7 +387,7 @@ void DrawSpriteTable(std::function<void(int)> onSpriteSelect) {
|
||||
// Initialize items if empty
|
||||
if (items.empty()) {
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
items.push_back(SpriteItem{i, core::kSpriteDefaultNames[i].data()});
|
||||
items.push_back(SpriteItem{i, zelda3::kSpriteDefaultNames[i].data()});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "app/editor/overworld_editor.h"
|
||||
#include "app/zelda3/common.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
|
||||
@@ -11,16 +10,16 @@ namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
bool IsMouseHoveringOverEntity(const zelda3::OverworldEntity &entity,
|
||||
bool IsMouseHoveringOverEntity(const zelda3::GameEntity &entity,
|
||||
ImVec2 canvas_p0, ImVec2 scrolling);
|
||||
|
||||
void MoveEntityOnGrid(zelda3::OverworldEntity *entity, ImVec2 canvas_p0,
|
||||
void MoveEntityOnGrid(zelda3::GameEntity *entity, ImVec2 canvas_p0,
|
||||
ImVec2 scrolling, bool free_movement = false);
|
||||
|
||||
void HandleEntityDragging(zelda3::OverworldEntity *entity, ImVec2 canvas_p0,
|
||||
void HandleEntityDragging(zelda3::GameEntity *entity, ImVec2 canvas_p0,
|
||||
ImVec2 scrolling, bool &is_dragging_entity,
|
||||
zelda3::OverworldEntity *&dragged_entity,
|
||||
zelda3::OverworldEntity *¤t_entity,
|
||||
zelda3::GameEntity *&dragged_entity,
|
||||
zelda3::GameEntity *¤t_entity,
|
||||
bool free_movement = false);
|
||||
|
||||
bool DrawEntranceInserterPopup();
|
||||
@@ -85,4 +84,4 @@ bool DrawSpriteEditorPopup(zelda3::Sprite &sprite);
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_OVERWORLD_ENTITY_H
|
||||
#endif // YAZE_APP_EDITOR_OVERWORLD_ENTITY_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,6 @@
|
||||
#ifndef YAZE_APP_EDITOR_OVERWORLDEDITOR_H
|
||||
#define YAZE_APP_EDITOR_OVERWORLDEDITOR_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -10,13 +8,11 @@
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_format.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/graphics/tile16_editor.h"
|
||||
#include "app/editor/overworld/entity.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/utils/gfx_context.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
@@ -25,24 +21,24 @@
|
||||
#include "app/gui/zeml.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
static constexpr uint k4BPP = 4;
|
||||
static constexpr uint kByteSize = 3;
|
||||
static constexpr uint kMessageIdSize = 5;
|
||||
static constexpr uint kNumSheetsToLoad = 223;
|
||||
static constexpr uint kTile8DisplayHeight = 64;
|
||||
static constexpr float kInputFieldSize = 30.f;
|
||||
|
||||
static constexpr absl::string_view kToolsetColumnNames[] = {
|
||||
"#undoTool", "#redoTool", "#separator2", "#zoomOutTool",
|
||||
"#zoomInTool", "#separator", "#drawTool", "#history",
|
||||
"#entranceTool", "#exitTool", "#itemTool", "#spriteTool",
|
||||
"#transportTool", "#musicTool", "#separator3", "#tilemapTool",
|
||||
"propertiesTool"};
|
||||
constexpr uint k4BPP = 4;
|
||||
constexpr uint kByteSize = 3;
|
||||
constexpr uint kMessageIdSize = 5;
|
||||
constexpr uint kNumSheetsToLoad = 223;
|
||||
constexpr uint kTile8DisplayHeight = 64;
|
||||
constexpr uint kOverworldMapSize = 0x200;
|
||||
constexpr float kInputFieldSize = 30.f;
|
||||
constexpr ImVec2 kOverworldCanvasSize(kOverworldMapSize * 8,
|
||||
kOverworldMapSize * 8);
|
||||
constexpr ImVec2 kCurrentGfxCanvasSize(0x100 + 1, 0x10 * 0x40 + 1);
|
||||
constexpr ImVec2 kBlocksetCanvasSize(0x100 + 1, 0x4000 + 1);
|
||||
constexpr ImVec2 kGraphicsBinCanvasSize(0x100 + 1, kNumSheetsToLoad * 0x40 + 1);
|
||||
|
||||
constexpr ImGuiTableFlags kOWMapFlags =
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable;
|
||||
@@ -52,6 +48,14 @@ constexpr ImGuiTableFlags kOWEditFlags =
|
||||
ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
|
||||
ImGuiTableFlags_BordersV;
|
||||
|
||||
static constexpr absl::string_view kToolsetColumnNames[] = {
|
||||
"#undoTool", "#redoTool", "#separator2", "#zoomOutTool",
|
||||
"#zoomInTool", "#separator", "#drawTool", "#history",
|
||||
"#entranceTool", "#exitTool", "#itemTool", "#spriteTool",
|
||||
"#transportTool", "#musicTool", "#separator3", "#tilemapTool",
|
||||
"propertiesTool", "#separator4", "#experimentalTool", "#properties",
|
||||
"#separator5"};
|
||||
|
||||
constexpr absl::string_view kWorldList =
|
||||
"Light World\0Dark World\0Extra World\0";
|
||||
|
||||
@@ -61,13 +65,17 @@ constexpr absl::string_view kTileSelectorTab = "##TileSelectorTabBar";
|
||||
constexpr absl::string_view kOWEditTable = "##OWEditTable";
|
||||
constexpr absl::string_view kOWMapTable = "#MapSettingsTable";
|
||||
|
||||
constexpr int kEntranceTileTypePtrLow = 0xDB8BF;
|
||||
constexpr int kEntranceTileTypePtrHigh = 0xDB917;
|
||||
constexpr int kNumEntranceTileTypes = 0x2C;
|
||||
|
||||
class EntranceContext {
|
||||
public:
|
||||
absl::Status LoadEntranceTileTypes(Rom& rom) {
|
||||
int offset_low = 0xDB8BF;
|
||||
int offset_high = 0xDB917;
|
||||
int offset_low = kEntranceTileTypePtrLow;
|
||||
int offset_high = kEntranceTileTypePtrHigh;
|
||||
|
||||
for (int i = 0; i < 0x2C; i++) {
|
||||
for (int i = 0; i < kNumEntranceTileTypes; i++) {
|
||||
// Load entrance tile types
|
||||
ASSIGN_OR_RETURN(auto value_low, rom.ReadWord(offset_low + i));
|
||||
entrance_tile_types_low_.push_back(value_low);
|
||||
@@ -101,8 +109,8 @@ class EntranceContext {
|
||||
*/
|
||||
class OverworldEditor : public Editor,
|
||||
public SharedRom,
|
||||
public context::GfxContext,
|
||||
public EntranceContext,
|
||||
public GfxContext,
|
||||
public core::ExperimentFlags {
|
||||
public:
|
||||
OverworldEditor() { type_ = EditorType::kOverworld; }
|
||||
@@ -110,12 +118,15 @@ class OverworldEditor : public Editor,
|
||||
void InitializeZeml();
|
||||
|
||||
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"); }
|
||||
absl::Status Find() { return absl::UnimplementedError("Find Unused Tiles"); }
|
||||
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
|
||||
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
|
||||
absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
|
||||
absl::Status Copy() override { return absl::UnimplementedError("Copy"); }
|
||||
absl::Status Paste() override { return absl::UnimplementedError("Paste"); }
|
||||
absl::Status Find() override {
|
||||
return absl::UnimplementedError("Find Unused Tiles");
|
||||
}
|
||||
absl::Status Save();
|
||||
|
||||
auto overworld() { return &overworld_; }
|
||||
|
||||
@@ -135,11 +146,26 @@ class OverworldEditor : public Editor,
|
||||
absl::Status LoadGraphics();
|
||||
|
||||
private:
|
||||
absl::Status UpdateFullscreenCanvas();
|
||||
/**
|
||||
* @brief Draws the canvas, tile16 selector, and toolset in fullscreen
|
||||
*/
|
||||
void DrawFullscreenCanvas();
|
||||
|
||||
absl::Status DrawToolset();
|
||||
/**
|
||||
* @brief Toolset for entrances, exits, items, sprites, and transports.
|
||||
*/
|
||||
void DrawToolset();
|
||||
|
||||
/**
|
||||
* @brief Draws the overworld map settings. Graphics, palettes, etc.
|
||||
*/
|
||||
void DrawOverworldMapSettings();
|
||||
|
||||
/**
|
||||
* @brief Draw the overworld settings for ZSCustomOverworld.
|
||||
*/
|
||||
void DrawCustomOverworldMapSettings();
|
||||
|
||||
void RefreshChildMap(int i);
|
||||
void RefreshOverworldMap();
|
||||
absl::Status RefreshMapPalette();
|
||||
@@ -155,9 +181,28 @@ class OverworldEditor : public Editor,
|
||||
void DrawOverworldMaps();
|
||||
void DrawOverworldEdits();
|
||||
void RenderUpdatedMapBitmap(const ImVec2& click_position,
|
||||
const Bytes& tile_data);
|
||||
const std::vector<uint8_t>& tile_data);
|
||||
|
||||
/**
|
||||
* @brief Check for changes to the overworld map.
|
||||
*
|
||||
* This function either draws the tile painter with the current tile16 or
|
||||
* group of tile16 data with ow_map_canvas_ and DrawOverworldEdits or it
|
||||
* checks for left mouse button click/drag to select a tile16 or group of
|
||||
* tile16 data from the overworld map canvas. Similar to ZScream selection.
|
||||
*/
|
||||
void CheckForOverworldEdits();
|
||||
|
||||
/**
|
||||
* @brief Draw and create the tile16 IDs that are currently selected.
|
||||
*/
|
||||
void CheckForSelectRectangle();
|
||||
|
||||
/**
|
||||
* @brief Check for changes to the overworld map. Calls RefreshOverworldMap
|
||||
* and RefreshTile16Blockset on the current map if it is modified and is
|
||||
* actively being edited.
|
||||
*/
|
||||
absl::Status CheckForCurrentMap();
|
||||
void CheckForMousePan();
|
||||
|
||||
@@ -175,17 +220,10 @@ class OverworldEditor : public Editor,
|
||||
|
||||
void DrawOverworldProperties();
|
||||
|
||||
absl::Status DrawExperimentalModal();
|
||||
|
||||
absl::Status UpdateUsageStats();
|
||||
void DrawUsageGrid();
|
||||
void CalculateUsageStats();
|
||||
|
||||
absl::Status LoadAnimatedMaps();
|
||||
void DrawDebugWindow();
|
||||
|
||||
auto gfx_group_editor() const { return gfx_group_editor_; }
|
||||
|
||||
enum class EditingMode {
|
||||
DRAW_TILE,
|
||||
ENTRANCES,
|
||||
@@ -200,84 +238,55 @@ class OverworldEditor : public Editor,
|
||||
EditingMode current_mode = EditingMode::DRAW_TILE;
|
||||
EditingMode previous_mode = EditingMode::DRAW_TILE;
|
||||
|
||||
enum OverworldProperty {
|
||||
LW_AREA_GFX,
|
||||
DW_AREA_GFX,
|
||||
LW_AREA_PAL,
|
||||
DW_AREA_PAL,
|
||||
LW_SPR_GFX_PART1,
|
||||
LW_SPR_GFX_PART2,
|
||||
DW_SPR_GFX_PART1,
|
||||
DW_SPR_GFX_PART2,
|
||||
LW_SPR_PAL_PART1,
|
||||
LW_SPR_PAL_PART2,
|
||||
DW_SPR_PAL_PART1,
|
||||
DW_SPR_PAL_PART2,
|
||||
};
|
||||
|
||||
int current_world_ = 0;
|
||||
int current_map_ = 0;
|
||||
int current_parent_ = 0;
|
||||
int current_entrance_id_ = 0;
|
||||
int current_exit_id_ = 0;
|
||||
int current_item_id_ = 0;
|
||||
int current_sprite_id_ = 0;
|
||||
int current_blockset_ = 0;
|
||||
int game_state_ = 1;
|
||||
int current_tile16_ = 0;
|
||||
int selected_tile_ = 0;
|
||||
|
||||
int current_blockset_ = 0;
|
||||
|
||||
int selected_entrance_ = 0;
|
||||
int selected_usage_map_ = 0xFFFF;
|
||||
|
||||
char map_gfx_[3] = "";
|
||||
char map_palette_[3] = "";
|
||||
char spr_gfx_[3] = "";
|
||||
char spr_palette_[3] = "";
|
||||
char message_id_[5] = "";
|
||||
char staticgfx[16];
|
||||
|
||||
uint32_t tilemap_file_offset_high_ = 0;
|
||||
uint32_t tilemap_file_offset_low_ = 0;
|
||||
uint32_t light_maps_to_load_ = 0x51;
|
||||
uint32_t dark_maps_to_load_ = 0x2A;
|
||||
uint32_t sp_maps_to_load_ = 0x07;
|
||||
|
||||
bool opt_enable_grid = true;
|
||||
bool all_gfx_loaded_ = false;
|
||||
bool map_blockset_loaded_ = false;
|
||||
bool selected_tile_loaded_ = false;
|
||||
bool update_selected_tile_ = true;
|
||||
bool is_dragging_entrance_ = false;
|
||||
bool show_tile16_editor_ = false;
|
||||
bool show_gfx_group_editor_ = false;
|
||||
bool overworld_canvas_fullscreen_ = false;
|
||||
bool middle_mouse_dragging_ = false;
|
||||
|
||||
bool is_dragging_entity_ = false;
|
||||
zelda3::OverworldEntity* dragged_entity_;
|
||||
zelda3::OverworldEntity* current_entity_;
|
||||
|
||||
int current_entrance_id_ = 0;
|
||||
zelda3::overworld::OverworldEntrance current_entrance_;
|
||||
int current_exit_id_ = 0;
|
||||
zelda3::overworld::OverworldExit current_exit_;
|
||||
int current_item_id_ = 0;
|
||||
zelda3::overworld::OverworldItem current_item_;
|
||||
int current_sprite_id_ = 0;
|
||||
zelda3::Sprite current_sprite_;
|
||||
|
||||
bool show_experimental = false;
|
||||
std::string ow_tilemap_filename_ = "";
|
||||
std::string tile32_configuration_filename_ = "";
|
||||
|
||||
Bytes selected_tile_data_;
|
||||
std::vector<Bytes> tile16_individual_data_;
|
||||
std::vector<uint8_t> selected_tile_data_;
|
||||
std::vector<std::vector<uint8_t>> tile16_individual_data_;
|
||||
std::vector<gfx::Bitmap> tile16_individual_;
|
||||
|
||||
std::vector<Bytes> tile8_individual_data_;
|
||||
std::vector<std::vector<uint8_t>> tile8_individual_data_;
|
||||
std::vector<gfx::Bitmap> tile8_individual_;
|
||||
|
||||
Tile16Editor tile16_editor_;
|
||||
GfxGroupEditor gfx_group_editor_;
|
||||
PaletteEditor palette_editor_;
|
||||
zelda3::overworld::Overworld overworld_;
|
||||
|
||||
gui::Canvas ow_map_canvas_{"owMapCanvas", ImVec2(0x200 * 8, 0x200 * 8),
|
||||
gui::CanvasGridSize::k64x64};
|
||||
gui::Canvas current_gfx_canvas_{"customGfxCanvas",
|
||||
ImVec2(0x100 + 1, 0x10 * 0x40 + 1),
|
||||
gui::CanvasGridSize::k32x32};
|
||||
gui::Canvas blockset_canvas_{"blocksetCanvas", ImVec2(0x100 + 1, 0x2000 + 1),
|
||||
gui::CanvasGridSize::k32x32};
|
||||
gui::Canvas graphics_bin_canvas_{
|
||||
"graphicsBinCanvas", ImVec2(0x100 + 1, kNumSheetsToLoad * 0x40 + 1),
|
||||
gui::CanvasGridSize::k16x16};
|
||||
gui::Canvas properties_canvas_;
|
||||
|
||||
gfx::SnesPalette palette_;
|
||||
|
||||
gfx::Bitmap selected_tile_bmp_;
|
||||
gfx::Bitmap tile16_blockset_bmp_;
|
||||
gfx::Bitmap current_gfx_bmp_;
|
||||
@@ -286,10 +295,28 @@ class OverworldEditor : public Editor,
|
||||
gfx::BitmapTable maps_bmp_;
|
||||
gfx::BitmapTable current_graphics_set_;
|
||||
gfx::BitmapTable sprite_previews_;
|
||||
gfx::BitmapTable animated_maps_;
|
||||
|
||||
OWBlockset refresh_blockset_;
|
||||
zelda3::overworld::Overworld overworld_;
|
||||
zelda3::OWBlockset refresh_blockset_;
|
||||
|
||||
zelda3::Sprite current_sprite_;
|
||||
|
||||
zelda3::overworld::OverworldEntrance current_entrance_;
|
||||
zelda3::overworld::OverworldExit current_exit_;
|
||||
zelda3::overworld::OverworldItem current_item_;
|
||||
|
||||
zelda3::GameEntity* current_entity_;
|
||||
zelda3::GameEntity* dragged_entity_;
|
||||
|
||||
gui::Canvas ow_map_canvas_{"OwMap", kOverworldCanvasSize,
|
||||
gui::CanvasGridSize::k64x64};
|
||||
gui::Canvas current_gfx_canvas_{"CurrentGfx", kCurrentGfxCanvasSize,
|
||||
gui::CanvasGridSize::k32x32};
|
||||
gui::Canvas blockset_canvas_{"OwBlockset", kBlocksetCanvasSize,
|
||||
gui::CanvasGridSize::k32x32};
|
||||
gui::Canvas graphics_bin_canvas_{"GraphicsBin", kGraphicsBinCanvasSize,
|
||||
gui::CanvasGridSize::k16x16};
|
||||
gui::Canvas properties_canvas_;
|
||||
gui::zeml::Node layout_node_;
|
||||
absl::Status status_;
|
||||
};
|
||||
@@ -297,4 +324,4 @@ class OverworldEditor : public Editor,
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,157 +0,0 @@
|
||||
#include "app/editor/overworld_editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
void OverworldEditor::RefreshChildMap(int map_index) {
|
||||
overworld_.mutable_overworld_map(map_index)->LoadAreaGraphics();
|
||||
status_ = overworld_.mutable_overworld_map(map_index)->BuildTileset();
|
||||
PRINT_IF_ERROR(status_);
|
||||
status_ = overworld_.mutable_overworld_map(map_index)->BuildTiles16Gfx(
|
||||
overworld_.tiles16().size());
|
||||
PRINT_IF_ERROR(status_);
|
||||
status_ = overworld_.mutable_overworld_map(map_index)->BuildBitmap(
|
||||
overworld_.GetMapTiles(current_world_));
|
||||
maps_bmp_[map_index].set_data(
|
||||
overworld_.mutable_overworld_map(map_index)->bitmap_data());
|
||||
maps_bmp_[map_index].set_modified(true);
|
||||
PRINT_IF_ERROR(status_);
|
||||
}
|
||||
|
||||
void OverworldEditor::RefreshOverworldMap() {
|
||||
std::vector<std::future<void>> futures;
|
||||
int indices[4];
|
||||
|
||||
auto refresh_map_async = [this](int map_index) {
|
||||
RefreshChildMap(map_index);
|
||||
};
|
||||
|
||||
int source_map_id = current_map_;
|
||||
bool is_large = overworld_.overworld_map(current_map_)->is_large_map();
|
||||
if (is_large) {
|
||||
source_map_id = current_parent_;
|
||||
// We need to update the map and its siblings if it's a large map
|
||||
for (int i = 1; i < 4; i++) {
|
||||
int sibling_index = overworld_.overworld_map(source_map_id)->parent() + i;
|
||||
if (i >= 2) sibling_index += 6;
|
||||
futures.push_back(
|
||||
std::async(std::launch::async, refresh_map_async, sibling_index));
|
||||
indices[i] = sibling_index;
|
||||
}
|
||||
}
|
||||
indices[0] = source_map_id;
|
||||
futures.push_back(
|
||||
std::async(std::launch::async, refresh_map_async, source_map_id));
|
||||
|
||||
for (auto &each : futures) {
|
||||
each.get();
|
||||
}
|
||||
int n = is_large ? 4 : 1;
|
||||
// We do texture updating on the main thread
|
||||
for (int i = 0; i < n; ++i) {
|
||||
rom()->UpdateBitmap(&maps_bmp_[indices[i]]);
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status OverworldEditor::RefreshMapPalette() {
|
||||
RETURN_IF_ERROR(
|
||||
overworld_.mutable_overworld_map(current_map_)->LoadPalette());
|
||||
const auto current_map_palette = overworld_.AreaPalette();
|
||||
|
||||
if (overworld_.overworld_map(current_map_)->is_large_map()) {
|
||||
// We need to update the map and its siblings if it's a large map
|
||||
for (int i = 1; i < 4; i++) {
|
||||
int sibling_index = overworld_.overworld_map(current_map_)->parent() + i;
|
||||
if (i >= 2) sibling_index += 6;
|
||||
RETURN_IF_ERROR(
|
||||
overworld_.mutable_overworld_map(sibling_index)->LoadPalette());
|
||||
RETURN_IF_ERROR(
|
||||
maps_bmp_[sibling_index].ApplyPalette(current_map_palette));
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_IF_ERROR(maps_bmp_[current_map_].ApplyPalette(current_map_palette));
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void OverworldEditor::RefreshMapProperties() {
|
||||
auto ¤t_ow_map = *overworld_.mutable_overworld_map(current_map_);
|
||||
if (current_ow_map.is_large_map()) {
|
||||
// We need to copy the properties from the parent map to the children
|
||||
for (int i = 1; i < 4; i++) {
|
||||
int sibling_index = current_ow_map.parent() + i;
|
||||
if (i >= 2) {
|
||||
sibling_index += 6;
|
||||
}
|
||||
auto &map = *overworld_.mutable_overworld_map(sibling_index);
|
||||
map.set_area_graphics(current_ow_map.area_graphics());
|
||||
map.set_area_palette(current_ow_map.area_palette());
|
||||
map.set_sprite_graphics(game_state_,
|
||||
current_ow_map.sprite_graphics(game_state_));
|
||||
map.set_sprite_palette(game_state_,
|
||||
current_ow_map.sprite_palette(game_state_));
|
||||
map.set_message_id(current_ow_map.message_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status OverworldEditor::RefreshTile16Blockset() {
|
||||
if (current_blockset_ ==
|
||||
overworld_.overworld_map(current_map_)->area_graphics()) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
current_blockset_ = overworld_.overworld_map(current_map_)->area_graphics();
|
||||
|
||||
overworld_.set_current_map(current_map_);
|
||||
palette_ = overworld_.AreaPalette();
|
||||
// Create the tile16 blockset image
|
||||
rom()->UpdateBitmap(&tile16_blockset_bmp_);
|
||||
RETURN_IF_ERROR(tile16_blockset_bmp_.ApplyPalette(palette_));
|
||||
|
||||
// Copy the tile16 data into individual tiles.
|
||||
auto tile16_data = overworld_.Tile16Blockset();
|
||||
|
||||
std::vector<std::future<void>> futures;
|
||||
// Loop through the tiles and copy their pixel data into separate vectors
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
futures.push_back(std::async(
|
||||
std::launch::async,
|
||||
[&](int index) {
|
||||
// Create a new vector for the pixel data of the current tile
|
||||
Bytes tile_data(16 * 16, 0x00); // More efficient initialization
|
||||
|
||||
// Copy the pixel data for the current tile into the vector
|
||||
for (int ty = 0; ty < 16; ty++) {
|
||||
for (int tx = 0; tx < 16; tx++) {
|
||||
int position = tx + (ty * 0x10);
|
||||
uint8_t value =
|
||||
tile16_data[(index % 8 * 16) + (index / 8 * 16 * 0x80) +
|
||||
(ty * 0x80) + tx];
|
||||
tile_data[position] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the vector for the current tile to the vector of tile pixel
|
||||
// data
|
||||
tile16_individual_[index].set_data(tile_data);
|
||||
},
|
||||
i));
|
||||
}
|
||||
|
||||
for (auto &future : futures) {
|
||||
future.get();
|
||||
}
|
||||
|
||||
// Render the bitmaps of each tile.
|
||||
for (int id = 0; id < 4096; id++) {
|
||||
RETURN_IF_ERROR(tile16_individual_[id].ApplyPalette(palette_));
|
||||
rom()->UpdateBitmap(&tile16_individual_[id]);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "app/editor/sprite/zsprite.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/zelda3/sprite/sprite.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -71,13 +72,13 @@ void SpriteEditor::DrawVanillaSpriteEditor() {
|
||||
for (int n = 0; n < active_sprites_.Size;) {
|
||||
bool open = true;
|
||||
|
||||
if (active_sprites_[n] > sizeof(core::kSpriteDefaultNames) / 4) {
|
||||
if (active_sprites_[n] > sizeof(zelda3::kSpriteDefaultNames) / 4) {
|
||||
active_sprites_.erase(active_sprites_.Data + n);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem(
|
||||
core::kSpriteDefaultNames[active_sprites_[n]].data(), &open,
|
||||
zelda3::kSpriteDefaultNames[active_sprites_[n]].data(), &open,
|
||||
ImGuiTabItemFlags_None)) {
|
||||
DrawSpriteCanvas();
|
||||
ImGui::EndTabItem();
|
||||
@@ -175,7 +176,7 @@ void SpriteEditor::DrawCurrentSheets() {
|
||||
graphics_sheet_canvas_.DrawTileSelector(32);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
graphics_sheet_canvas_.DrawBitmap(
|
||||
rom()->bitmap_manager()[current_sheets_[i]], 1, (i * 0x40) + 1, 2);
|
||||
rom()->gfx_sheets().at(current_sheets_[i]), 1, (i * 0x40) + 1, 2);
|
||||
}
|
||||
graphics_sheet_canvas_.DrawGrid();
|
||||
graphics_sheet_canvas_.DrawOverlay();
|
||||
@@ -188,10 +189,10 @@ void SpriteEditor::DrawSpritesList() {
|
||||
ImVec2(ImGui::GetContentRegionAvail().x, 0), true,
|
||||
ImGuiWindowFlags_NoDecoration)) {
|
||||
int i = 0;
|
||||
for (const auto each_sprite_name : core::kSpriteDefaultNames) {
|
||||
for (const auto each_sprite_name : zelda3::kSpriteDefaultNames) {
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
current_sprite_id_ == i, "Sprite Names", core::UppercaseHexByte(i),
|
||||
core::kSpriteDefaultNames[i].data());
|
||||
zelda3::kSpriteDefaultNames[i].data());
|
||||
if (ImGui::IsItemClicked()) {
|
||||
current_sprite_id_ = i;
|
||||
if (!active_sprites_.contains(i)) {
|
||||
@@ -243,7 +244,7 @@ void SpriteEditor::DrawCustomSpritesMetadata() {
|
||||
// ZSprite Maker format open file dialog
|
||||
if (ImGui::Button("Open ZSprite")) {
|
||||
// Open ZSprite file
|
||||
std::string file_path = FileDialogWrapper::ShowOpenFileDialog();
|
||||
std::string file_path = core::FileDialogWrapper::ShowOpenFileDialog();
|
||||
if (!file_path.empty()) {
|
||||
zsprite::ZSprite zsprite;
|
||||
status_ = zsprite.Load(file_path);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/editor/sprite/zsprite.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#ifndef YAZE_APP_EDITOR_SPRITE_ZSPRITE_H
|
||||
#define YAZE_APP_EDITOR_SPRITE_ZSPRITE_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "app/core/constants.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -46,7 +46,7 @@ struct OamTile {
|
||||
struct AnimationGroup {
|
||||
AnimationGroup() = default;
|
||||
AnimationGroup(uint8_t fs, uint8_t fe, uint8_t fsp, std::string fn)
|
||||
: frame_start(fs), frame_end(fe), frame_speed(fsp), frame_name(fn) {}
|
||||
: frame_name(fn), frame_start(fs), frame_end(fe), frame_speed(fsp) {}
|
||||
|
||||
std::string frame_name;
|
||||
uint8_t frame_start;
|
||||
|
||||
143
src/app/editor/system/command_manager.cc
Normal file
143
src/app/editor/system/command_manager.cc
Normal file
@@ -0,0 +1,143 @@
|
||||
#include "command_manager.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
ImGuiKey MapKeyToImGuiKey(char key) {
|
||||
switch (key) {
|
||||
case 'A':
|
||||
return ImGuiKey_A;
|
||||
case 'B':
|
||||
return ImGuiKey_B;
|
||||
case 'C':
|
||||
return ImGuiKey_C;
|
||||
case 'D':
|
||||
return ImGuiKey_D;
|
||||
case 'E':
|
||||
return ImGuiKey_E;
|
||||
case 'F':
|
||||
return ImGuiKey_F;
|
||||
case 'G':
|
||||
return ImGuiKey_G;
|
||||
case 'H':
|
||||
return ImGuiKey_H;
|
||||
case 'I':
|
||||
return ImGuiKey_I;
|
||||
case 'J':
|
||||
return ImGuiKey_J;
|
||||
case 'K':
|
||||
return ImGuiKey_K;
|
||||
case 'L':
|
||||
return ImGuiKey_L;
|
||||
case 'M':
|
||||
return ImGuiKey_M;
|
||||
case 'N':
|
||||
return ImGuiKey_N;
|
||||
case 'O':
|
||||
return ImGuiKey_O;
|
||||
case 'P':
|
||||
return ImGuiKey_P;
|
||||
case 'Q':
|
||||
return ImGuiKey_Q;
|
||||
case 'R':
|
||||
return ImGuiKey_R;
|
||||
case 'S':
|
||||
return ImGuiKey_S;
|
||||
case 'T':
|
||||
return ImGuiKey_T;
|
||||
case 'U':
|
||||
return ImGuiKey_U;
|
||||
case 'V':
|
||||
return ImGuiKey_V;
|
||||
case 'W':
|
||||
return ImGuiKey_W;
|
||||
case 'X':
|
||||
return ImGuiKey_X;
|
||||
case 'Y':
|
||||
return ImGuiKey_Y;
|
||||
case 'Z':
|
||||
return ImGuiKey_Z;
|
||||
case '/':
|
||||
return ImGuiKey_Slash;
|
||||
case '-':
|
||||
return ImGuiKey_Minus;
|
||||
default:
|
||||
return ImGuiKey_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
// When the player presses Space, a popup will appear fixed to the bottom of the
|
||||
// ImGui window with a list of the available key commands which can be used.
|
||||
void CommandManager::ShowWhichKey() {
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Space)) {
|
||||
ImGui::OpenPopup("WhichKey");
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(0, ImGui::GetIO().DisplaySize.y - 100),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x, 100),
|
||||
ImGuiCond_Always);
|
||||
if (ImGui::BeginPopup("WhichKey")) {
|
||||
// ESC to close the popup
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
const ImVec4 colors[] = {
|
||||
ImVec4(0.8f, 0.2f, 0.2f, 1.0f), // Soft Red
|
||||
ImVec4(0.2f, 0.8f, 0.2f, 1.0f), // Soft Green
|
||||
ImVec4(0.2f, 0.2f, 0.8f, 1.0f), // Soft Blue
|
||||
ImVec4(0.8f, 0.8f, 0.2f, 1.0f), // Soft Yellow
|
||||
ImVec4(0.8f, 0.2f, 0.8f, 1.0f), // Soft Magenta
|
||||
ImVec4(0.2f, 0.8f, 0.8f, 1.0f) // Soft Cyan
|
||||
};
|
||||
const int numColors = sizeof(colors) / sizeof(colors[0]);
|
||||
int colorIndex = 0;
|
||||
|
||||
if (ImGui::BeginTable("CommandsTable", commands_.size(),
|
||||
ImGuiTableFlags_SizingStretchProp)) {
|
||||
for (const auto &[shortcut, info] : commands_) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(colors[colorIndex], "%c: %s",
|
||||
info.command_info.mnemonic,
|
||||
info.command_info.name.c_str());
|
||||
colorIndex = (colorIndex + 1) % numColors;
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void CommandManager::SaveKeybindings(const std::string &filepath) {
|
||||
std::ofstream out(filepath);
|
||||
if (out.is_open()) {
|
||||
for (const auto &[shortcut, info] : commands_) {
|
||||
out << shortcut << " " << info.command_info.mnemonic << " "
|
||||
<< info.command_info.name << " " << info.command_info.desc << "\n";
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
void CommandManager::LoadKeybindings(const std::string &filepath) {
|
||||
std::ifstream in(filepath);
|
||||
if (in.is_open()) {
|
||||
commands_.clear();
|
||||
std::string shortcut, name, desc;
|
||||
char mnemonic;
|
||||
while (in >> shortcut >> mnemonic >> name >> desc) {
|
||||
commands_[shortcut].command_info = {nullptr, mnemonic, name, desc};
|
||||
}
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
83
src/app/editor/system/command_manager.h
Normal file
83
src/app/editor/system/command_manager.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef YAZE_APP_EDITOR_SYSTEM_COMMAND_MANAGER_H
|
||||
#define YAZE_APP_EDITOR_SYSTEM_COMMAND_MANAGER_H
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
ImGuiKey MapKeyToImGuiKey(char key);
|
||||
|
||||
class CommandManager {
|
||||
public:
|
||||
CommandManager() = default;
|
||||
~CommandManager() = default;
|
||||
|
||||
using Command = std::function<void()>;
|
||||
|
||||
struct CommandInfo {
|
||||
Command command;
|
||||
char mnemonic;
|
||||
std::string name;
|
||||
std::string desc;
|
||||
CommandInfo(Command command, char mnemonic, const std::string &name,
|
||||
const std::string &desc)
|
||||
: command(std::move(command)), mnemonic(mnemonic), name(name),
|
||||
desc(desc) {}
|
||||
CommandInfo() = default;
|
||||
};
|
||||
|
||||
// New command info which supports subsections of commands
|
||||
struct CommandInfoOrPrefix {
|
||||
CommandInfo command_info;
|
||||
std::unordered_map<std::string, CommandInfoOrPrefix> subcommands;
|
||||
CommandInfoOrPrefix(CommandInfo command_info)
|
||||
: command_info(std::move(command_info)) {}
|
||||
CommandInfoOrPrefix() = default;
|
||||
};
|
||||
|
||||
void RegisterPrefix(const std::string &group_name, const char prefix,
|
||||
const std::string &name, const std::string &desc) {
|
||||
commands_[group_name].command_info = {nullptr, prefix, name, desc};
|
||||
}
|
||||
|
||||
void RegisterSubcommand(const std::string &group_name,
|
||||
const std::string &shortcut, const char mnemonic,
|
||||
const std::string &name, const std::string &desc,
|
||||
Command command) {
|
||||
commands_[group_name].subcommands[shortcut].command_info = {
|
||||
command, mnemonic, name, desc};
|
||||
}
|
||||
|
||||
void RegisterCommand(const std::string &shortcut, Command command,
|
||||
char mnemonic, const std::string &name,
|
||||
const std::string &desc) {
|
||||
commands_[shortcut].command_info = {std::move(command), mnemonic, name,
|
||||
desc};
|
||||
}
|
||||
|
||||
void ExecuteCommand(const std::string &shortcut) {
|
||||
if (commands_.find(shortcut) != commands_.end()) {
|
||||
commands_[shortcut].command_info.command();
|
||||
}
|
||||
}
|
||||
|
||||
void ShowWhichKey();
|
||||
|
||||
void SaveKeybindings(const std::string &filepath);
|
||||
void LoadKeybindings(const std::string &filepath);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, CommandInfoOrPrefix> commands_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_SYSTEM_COMMAND_MANAGER_H
|
||||
77
src/app/editor/system/constant_manager.h
Normal file
77
src/app/editor/system/constant_manager.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef YAZE_APP_EDITOR_SYSTEM_CONSTANT_MANAGER_H
|
||||
#define YAZE_APP_EDITOR_SYSTEM_CONSTANT_MANAGER_H
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "app/zelda3/overworld/overworld_map.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
class ConstantManager {
|
||||
public:
|
||||
void ShowConstantManager() {
|
||||
ImGui::Begin("Constant Manager");
|
||||
|
||||
// Tab bar for the different types of constants
|
||||
// Overworld, dungeon, graphics, expanded
|
||||
ImGui::TextWrapped(
|
||||
"This is the constant manager. It allows you to view and edit "
|
||||
"constants in the ROM. You should only edit these if you know what "
|
||||
"you're doing.");
|
||||
|
||||
if (ImGui::BeginTabBar("Constant Manager Tabs")) {
|
||||
if (ImGui::BeginTabItem("Overworld")) {
|
||||
ImGui::Text("Overworld constants");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("OverworldCustomASMHasBeenApplied: %d",
|
||||
zelda3::overworld::OverworldCustomASMHasBeenApplied);
|
||||
ImGui::Text("OverworldCustomAreaSpecificBGPalette: %d",
|
||||
zelda3::overworld::OverworldCustomAreaSpecificBGPalette);
|
||||
ImGui::Text("OverworldCustomAreaSpecificBGEnabled: %d",
|
||||
zelda3::overworld::OverworldCustomAreaSpecificBGEnabled);
|
||||
ImGui::Text("OverworldCustomMainPaletteArray: %d",
|
||||
zelda3::overworld::OverworldCustomMainPaletteArray);
|
||||
ImGui::Text("OverworldCustomMainPaletteEnabled: %d",
|
||||
zelda3::overworld::OverworldCustomMainPaletteEnabled);
|
||||
ImGui::Text("OverworldCustomMosaicArray: %d",
|
||||
zelda3::overworld::OverworldCustomMosaicArray);
|
||||
ImGui::Text("OverworldCustomMosaicEnabled: %d",
|
||||
zelda3::overworld::OverworldCustomMosaicEnabled);
|
||||
ImGui::Text("OverworldCustomAnimatedGFXArray: %d",
|
||||
zelda3::overworld::OverworldCustomAnimatedGFXArray);
|
||||
ImGui::Text("OverworldCustomAnimatedGFXEnabled: %d",
|
||||
zelda3::overworld::OverworldCustomAnimatedGFXEnabled);
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Dungeon")) {
|
||||
ImGui::Text("Dungeon constants");
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Graphics")) {
|
||||
ImGui::Text("Graphics constants");
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Expanded")) {
|
||||
ImGui::Text("Expanded constants");
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_SYSTEM_CONSTANT_MANAGER_H
|
||||
85
src/app/editor/system/extension_manager.cc
Normal file
85
src/app/editor/system/extension_manager.cc
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "extension_manager.h"
|
||||
|
||||
#if defined(__unix__) || defined(__unix) || defined(unix) || \
|
||||
defined(__APPLE__) && defined(__MACH__)
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <system/extension.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
void ExtensionManager::LoadExtension(const std::string& filename,
|
||||
yaze_editor_context* context) {
|
||||
#if defined(__unix__) || defined(__unix) || defined(unix) || \
|
||||
defined(__APPLE__) && defined(__MACH__)
|
||||
auto extension_path = filename.c_str();
|
||||
void* handle = dlopen(extension_path, RTLD_LAZY);
|
||||
if (!handle) {
|
||||
std::cerr << "Cannot open extension: " << dlerror() << std::endl;
|
||||
return;
|
||||
}
|
||||
dlerror(); // Clear any existing error
|
||||
|
||||
// Load the symbol to retrieve the extension
|
||||
auto get_extension = reinterpret_cast<yaze_extension* (*)()>(
|
||||
dlsym(handle, "get_yaze_extension"));
|
||||
const char* dlsym_error = dlerror();
|
||||
if (dlsym_error) {
|
||||
std::cerr << "Cannot load symbol 'get_yaze_extension': " << dlsym_error
|
||||
<< std::endl;
|
||||
dlclose(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
yaze_extension* extension = get_extension();
|
||||
if (extension && extension->initialize) {
|
||||
extension->initialize(context);
|
||||
} else {
|
||||
std::cerr << "Failed to initialize the extension." << std::endl;
|
||||
dlclose(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
extensions_.push_back(extension);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ExtensionManager::RegisterExtension(yaze_extension* extension) {
|
||||
extensions_.push_back(extension);
|
||||
}
|
||||
|
||||
void ExtensionManager::InitializeExtensions(yaze_editor_context* context) {
|
||||
for (auto& extension : extensions_) {
|
||||
extension->initialize(context);
|
||||
}
|
||||
}
|
||||
|
||||
void ExtensionManager::ShutdownExtensions() {
|
||||
for (auto& extension : extensions_) {
|
||||
extension->cleanup();
|
||||
}
|
||||
|
||||
// if (handle) {
|
||||
// dlclose(handle);
|
||||
// handle = nullptr;
|
||||
// extension = nullptr;
|
||||
// }
|
||||
}
|
||||
|
||||
void ExtensionManager::ExecuteExtensionUI(yaze_editor_context* context) {
|
||||
for (auto& extension : extensions_) {
|
||||
if (extension->extend_ui) {
|
||||
extension->extend_ui(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
29
src/app/editor/system/extension_manager.h
Normal file
29
src/app/editor/system/extension_manager.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef YAZE_APP_EDITOR_SYSTEM_EXTENSION_MANAGER_H
|
||||
#define YAZE_APP_EDITOR_SYSTEM_EXTENSION_MANAGER_H
|
||||
|
||||
#include <system/extension.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
class ExtensionManager {
|
||||
public:
|
||||
void LoadExtension(const std::string& filename, yaze_editor_context* context);
|
||||
void RegisterExtension(yaze_extension* extension);
|
||||
void InitializeExtensions(yaze_editor_context* context);
|
||||
void ShutdownExtensions();
|
||||
void ExecuteExtensionUI(yaze_editor_context* context);
|
||||
|
||||
private:
|
||||
std::vector<yaze_extension*> extensions_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_SYSTEM_EXTENSION_MANAGER_H
|
||||
@@ -1,9 +1,8 @@
|
||||
#ifndef YAZE_APP_EDITOR_UTILS_FLAGS_H
|
||||
#define YAZE_APP_EDITOR_UTILS_FLAGS_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "core/common.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -31,6 +30,8 @@ struct FlagsMenu : public core::ExperimentFlags {
|
||||
&mutable_flags()->overworld.kSaveOverworldItems);
|
||||
Checkbox("Save Overworld Properties",
|
||||
&mutable_flags()->overworld.kSaveOverworldProperties);
|
||||
Checkbox("Load Custom Overworld",
|
||||
&mutable_flags()->overworld.kLoadCustomOverworld);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
@@ -42,21 +43,16 @@ struct FlagsMenu : public core::ExperimentFlags {
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (BeginMenu("Emulator Flags")) {
|
||||
Checkbox("Load Audio Device", &mutable_flags()->kLoadAudioDevice);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
Checkbox("Use built-in file dialog",
|
||||
&mutable_flags()->kNewFileDialogWrapper);
|
||||
Checkbox("Enable Console Logging", &mutable_flags()->kLogToConsole);
|
||||
Checkbox("Enable Texture Streaming",
|
||||
&mutable_flags()->kLoadTexturesAsStreaming);
|
||||
Checkbox("Use Bitmap Manager", &mutable_flags()->kUseBitmapManager);
|
||||
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);
|
||||
Checkbox("Use New ImGui Input", &mutable_flags()->kUseNewImGuiInput);
|
||||
}
|
||||
};
|
||||
32
src/app/editor/system/history_manager.h
Normal file
32
src/app/editor/system/history_manager.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef YAZE_APP_EDITOR_SYSTEM_HISTORY_MANAGER_H
|
||||
#define YAZE_APP_EDITOR_SYSTEM_HISTORY_MANAGER_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
// System history manager, undo and redo.
|
||||
class HistoryManager {
|
||||
public:
|
||||
HistoryManager() = default;
|
||||
~HistoryManager() = default;
|
||||
|
||||
void Add(const char* data);
|
||||
void Undo();
|
||||
void Redo();
|
||||
|
||||
private:
|
||||
std::vector<const char*> history_;
|
||||
std::stack<const char*> undo_;
|
||||
std::stack<const char*> redo_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_SYSTEM_HISTORY_MANAGER_H
|
||||
19
src/app/editor/system/popup_manager.cc
Normal file
19
src/app/editor/system/popup_manager.cc
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "popup_manager.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
PopupManager::PopupManager() {
|
||||
}
|
||||
|
||||
PopupManager::~PopupManager() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
21
src/app/editor/system/popup_manager.h
Normal file
21
src/app/editor/system/popup_manager.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef YAZE_APP_EDITOR_POPUP_MANAGER_H
|
||||
#define YAZE_APP_EDITOR_POPUP_MANAGER_H
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
// ImGui popup manager.
|
||||
class PopupManager {
|
||||
public:
|
||||
PopupManager();
|
||||
~PopupManager();
|
||||
|
||||
void Show(const char* name);
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_POPUP_MANAGER_H
|
||||
30
src/app/editor/system/resource_manager.h
Normal file
30
src/app/editor/system/resource_manager.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef YAZE_APP_EDITOR_SYSTEM_RESOURCE_MANAGER_H
|
||||
#define YAZE_APP_EDITOR_SYSTEM_RESOURCE_MANAGER_H
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
// System resource manager.
|
||||
class ResourceManager {
|
||||
public:
|
||||
ResourceManager() : count_(0) {}
|
||||
~ResourceManager() = default;
|
||||
|
||||
void Load(const char* path);
|
||||
void Unload(const char* path);
|
||||
void UnloadAll();
|
||||
|
||||
size_t Count() const;
|
||||
|
||||
private:
|
||||
size_t count_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_SYSTEM_RESOURCE_MANAGER_H
|
||||
@@ -1,11 +1,9 @@
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include "app/editor/system/settings_editor.h"
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/editor/utils/flags.h"
|
||||
#include "app/editor/utils/keyboard_shortcuts.h"
|
||||
#include "app/editor/settings_editor.h"
|
||||
#include "app/editor/utils/flags.h"
|
||||
#include "app/editor/system/flags.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/editor/editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -1,26 +0,0 @@
|
||||
#include "app/editor/utils/gfx_context.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
namespace context {
|
||||
|
||||
std::unordered_map<uint8_t, gfx::Paletteset> GfxContext::palettesets_;
|
||||
|
||||
}
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
@@ -1,36 +0,0 @@
|
||||
#ifndef YAZE_APP_EDITOR_VRAM_CONTEXT_H
|
||||
#define YAZE_APP_EDITOR_VRAM_CONTEXT_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
#include "app/editor/utils/editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
namespace context {
|
||||
|
||||
/**
|
||||
* @brief Shared graphical context across editors.
|
||||
*/
|
||||
class GfxContext {
|
||||
protected:
|
||||
// Palettesets for the tile16 individual tiles
|
||||
static std::unordered_map<uint8_t, gfx::Paletteset> palettesets_;
|
||||
};
|
||||
|
||||
} // namespace context
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_VRAM_CONTEXT_H
|
||||
@@ -1,25 +0,0 @@
|
||||
#ifndef YAZE_APP_EDITOR_UTILS_KEYBOARD_SHORTCUTS_H
|
||||
#define YAZE_APP_EDITOR_UTILS_KEYBOARD_SHORTCUTS_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
struct KeyboardShortcuts {
|
||||
enum class ShortcutType {
|
||||
kCut,
|
||||
kCopy,
|
||||
kPaste,
|
||||
kUndo,
|
||||
kRedo,
|
||||
kFind,
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_UTILS_KEYBOARD_SHORTCUTS_H_
|
||||
@@ -1,64 +0,0 @@
|
||||
#ifndef YAZE_APP_EDITOR_UTILS_RECENT_FILES_H
|
||||
#define YAZE_APP_EDITOR_UTILS_RECENT_FILES_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace editor {
|
||||
|
||||
class RecentFilesManager {
|
||||
public:
|
||||
RecentFilesManager(const std::string& filename) : filename_(filename) {}
|
||||
|
||||
void AddFile(const std::string& filePath) {
|
||||
// Add a file to the list, avoiding duplicates
|
||||
auto it = std::find(recentFiles_.begin(), recentFiles_.end(), filePath);
|
||||
if (it == recentFiles_.end()) {
|
||||
recentFiles_.push_back(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
void Save() {
|
||||
std::ofstream file(filename_);
|
||||
if (!file.is_open()) {
|
||||
return; // Handle the error appropriately
|
||||
}
|
||||
|
||||
for (const auto& filePath : recentFiles_) {
|
||||
file << filePath << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Load() {
|
||||
std::ifstream file(filename_);
|
||||
if (!file.is_open()) {
|
||||
return; // Handle the error appropriately
|
||||
}
|
||||
|
||||
recentFiles_.clear();
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
if (!line.empty()) {
|
||||
recentFiles_.push_back(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::string>& GetRecentFiles() const {
|
||||
return recentFiles_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string filename_;
|
||||
std::vector<std::string> recentFiles_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_UTILS_RECENT_FILES_H
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
#include "app/emu/audio/dsp.h"
|
||||
#include "app/emu/audio/spc700.h"
|
||||
|
||||
@@ -56,7 +56,6 @@ void Spc700::RunOpcode() {
|
||||
}
|
||||
|
||||
void Spc700::ExecuteInstructions(uint8_t opcode) {
|
||||
uint16_t initialPC = PC;
|
||||
switch (opcode) {
|
||||
case 0x00: { // nop imp
|
||||
read(PC);
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "app/core/common.h"
|
||||
#include "app/emu/cpu/clock.h"
|
||||
#include "app/emu/cpu/internal/opcodes.h"
|
||||
#include "app/emu/debug/log.h"
|
||||
#include "app/emu/memory/memory.h"
|
||||
|
||||
namespace yaze {
|
||||
@@ -36,7 +35,7 @@ class InstructionEntry {
|
||||
std::string instruction; // Human-readable instruction text
|
||||
};
|
||||
|
||||
class Cpu : public Loggable, public core::ExperimentFlags {
|
||||
class Cpu : public core::ExperimentFlags {
|
||||
public:
|
||||
explicit Cpu(memory::Memory& mem, Clock& vclock,
|
||||
memory::CpuCallbacks& callbacks)
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
#ifndef YAZE_APP_EMU_DEBUG_DEBUGGER_H_
|
||||
#define YAZE_APP_EMU_DEBUG_DEBUGGER_H_
|
||||
|
||||
#include "app/emu/audio/apu.h"
|
||||
#include "app/emu/cpu/cpu.h"
|
||||
#include "app/emu/video/ppu.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace emu {
|
||||
|
||||
class Debugger {
|
||||
public:
|
||||
Debugger() = default;
|
||||
// Attach the debugger to the emulator
|
||||
// Debugger(CPU &cpu, PPU &ppu, Apu &apu);
|
||||
|
||||
// Set a breakpoint
|
||||
void SetBreakpoint(uint16_t address);
|
||||
|
||||
// Remove a breakpoint
|
||||
void RemoveBreakpoint(uint16_t address);
|
||||
|
||||
// Step through the code
|
||||
void Step();
|
||||
|
||||
// Inspect memory
|
||||
uint8_t InspectMemory(uint16_t address);
|
||||
|
||||
// Modify memory
|
||||
void ModifyMemory(uint16_t address, uint8_t value);
|
||||
|
||||
// Inspect registers
|
||||
uint8_t InspectRegister(uint8_t reg);
|
||||
|
||||
// Modify registers
|
||||
void ModifyRegister(uint8_t reg, uint8_t value);
|
||||
|
||||
// Handle other debugger tasks
|
||||
// ...
|
||||
|
||||
private:
|
||||
// References to the emulator's components
|
||||
// CPU &cpu;
|
||||
// PPU &ppu;
|
||||
// Apu &apu;
|
||||
|
||||
// Breakpoints, watchpoints, etc.
|
||||
// ...
|
||||
};
|
||||
|
||||
} // namespace emu
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EMU_DBG_H_
|
||||
@@ -1,43 +0,0 @@
|
||||
#ifndef YAZE_APP_EMU_LOG_H_
|
||||
#define YAZE_APP_EMU_LOG_H_
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace emu {
|
||||
|
||||
// Logger.h
|
||||
class Logger {
|
||||
public:
|
||||
static Logger& GetInstance() {
|
||||
static Logger instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void Log(const std::string& message) const {
|
||||
// Write log messages to a file or console
|
||||
std::cout << message << std::endl;
|
||||
}
|
||||
|
||||
private:
|
||||
Logger() = default;
|
||||
Logger(const Logger&) = delete;
|
||||
Logger& operator=(const Logger&) = delete;
|
||||
};
|
||||
|
||||
// Loggable.h
|
||||
class Loggable {
|
||||
protected:
|
||||
Logger& logger_ = Logger::GetInstance();
|
||||
|
||||
virtual ~Loggable() = default;
|
||||
virtual void LogMessage(const std::string& message) { logger_.Log(message); }
|
||||
};
|
||||
|
||||
} // namespace emu
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EMU_LOG_H_
|
||||
@@ -5,10 +5,6 @@
|
||||
#endif
|
||||
|
||||
#include <SDL.h>
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -25,24 +21,14 @@
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/core/utils/sdl_deleter.h"
|
||||
#include "app/emu/snes.h"
|
||||
#include "app/rom.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
using namespace yaze::app;
|
||||
|
||||
struct sdl_deleter {
|
||||
void operator()(SDL_Window *p) const {
|
||||
if (p) {
|
||||
SDL_DestroyWindow(p);
|
||||
}
|
||||
}
|
||||
void operator()(SDL_Renderer *p) const {
|
||||
if (p) {
|
||||
SDL_DestroyRenderer(p);
|
||||
}
|
||||
}
|
||||
void operator()(SDL_Texture *p) const { SDL_DestroyTexture(p); }
|
||||
};
|
||||
using yaze::app::core::SDL_Deleter;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
absl::InitializeSymbolizer(argv[0]);
|
||||
@@ -52,33 +38,30 @@ int main(int argc, char **argv) {
|
||||
options.alarm_on_failure_secs = true;
|
||||
absl::InstallFailureSignalHandler(options);
|
||||
|
||||
std::unique_ptr<SDL_Window, sdl_deleter> window_;
|
||||
std::unique_ptr<SDL_Renderer, sdl_deleter> renderer_;
|
||||
std::unique_ptr<SDL_Window, SDL_Deleter> window_;
|
||||
std::unique_ptr<SDL_Renderer, SDL_Deleter> renderer_;
|
||||
if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
|
||||
return EXIT_FAILURE;
|
||||
} else {
|
||||
SDL_DisplayMode displayMode;
|
||||
SDL_GetCurrentDisplayMode(0, &displayMode);
|
||||
int screenWidth = displayMode.w * 0.8;
|
||||
int screenHeight = displayMode.h * 0.8;
|
||||
|
||||
window_ = std::unique_ptr<SDL_Window, sdl_deleter>(
|
||||
window_ = std::unique_ptr<SDL_Window, SDL_Deleter>(
|
||||
SDL_CreateWindow("Yaze Emulator", // window title
|
||||
SDL_WINDOWPOS_UNDEFINED, // initial x position
|
||||
SDL_WINDOWPOS_UNDEFINED, // initial y position
|
||||
512, // width, in pixels
|
||||
480, // height, in pixels
|
||||
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI),
|
||||
sdl_deleter());
|
||||
SDL_Deleter());
|
||||
if (window_ == nullptr) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
renderer_ = std::unique_ptr<SDL_Renderer, sdl_deleter>(
|
||||
renderer_ = std::unique_ptr<SDL_Renderer, SDL_Deleter>(
|
||||
SDL_CreateRenderer(window_.get(), -1,
|
||||
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC),
|
||||
sdl_deleter());
|
||||
SDL_Deleter());
|
||||
if (renderer_ == nullptr) {
|
||||
return EXIT_FAILURE;
|
||||
} else {
|
||||
@@ -102,7 +85,7 @@ int main(int argc, char **argv) {
|
||||
SDL_PauseAudioDevice(audio_device_, 0);
|
||||
|
||||
#ifdef __APPLE__
|
||||
InitializeCocoa();
|
||||
yaze_initialize_cocoa();
|
||||
#endif
|
||||
|
||||
auto ppu_texture_ =
|
||||
@@ -115,7 +98,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
Rom rom_;
|
||||
emu::SNES snes_;
|
||||
Bytes rom_data_;
|
||||
std::vector<uint8_t> rom_data_;
|
||||
|
||||
bool running = true;
|
||||
bool loaded = false;
|
||||
@@ -126,7 +109,10 @@ int main(int argc, char **argv) {
|
||||
int wanted_samples_ = 0;
|
||||
SDL_Event event;
|
||||
|
||||
rom_.LoadFromFile("inidisp_hammer_0f00.sfc");
|
||||
if (!rom_.LoadFromFile("inidisp_hammer_0f00.sfc").ok()) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (rom_.is_loaded()) {
|
||||
rom_data_ = rom_.vector();
|
||||
snes_.Init(rom_data_);
|
||||
@@ -1,7 +1,7 @@
|
||||
add_executable(
|
||||
yaze_emu
|
||||
app/rom.cc
|
||||
app/emu/debug/emu.cc
|
||||
app/emu/emu.cc
|
||||
${YAZE_APP_EMU_SRC}
|
||||
${YAZE_APP_CORE_SRC}
|
||||
${YAZE_APP_EDITOR_SRC}
|
||||
@@ -11,13 +11,27 @@ add_executable(
|
||||
${IMGUI_SRC}
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
list(APPEND YAZE_APP_CORE_SRC
|
||||
app/core/platform/app_delegate.mm)
|
||||
endif()
|
||||
|
||||
target_include_directories(
|
||||
yaze_emu PUBLIC
|
||||
lib/
|
||||
app/
|
||||
${CMAKE_SOURCE_DIR}/incl/
|
||||
${CMAKE_SOURCE_DIR}/src/
|
||||
${PNG_INCLUDE_DIRS}
|
||||
${SDL2_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(yaze_emu PUBLIC ${ABSL_TARGETS} ${SDL_TARGETS} ${PNG_LIBRARIES} ${CMAKE_DL_LIBS} ImGui)
|
||||
target_link_libraries(
|
||||
yaze_emu PUBLIC
|
||||
${ABSL_TARGETS}
|
||||
${SDL_TARGETS}
|
||||
${PNG_LIBRARIES}
|
||||
${CMAKE_DL_LIBS}
|
||||
ImGui
|
||||
ImGuiTestEngine
|
||||
)
|
||||
@@ -1,18 +1,18 @@
|
||||
#include "app/emu/emulator.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "app/core/constants.h"
|
||||
#include "app/core/platform/file_dialog.h"
|
||||
#include "app/core/platform/renderer.h"
|
||||
#include "app/emu/snes.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/zeml.h"
|
||||
#include "app/rom.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -52,9 +52,9 @@ using ImGui::Text;
|
||||
void Emulator::Run() {
|
||||
static bool loaded = false;
|
||||
if (!snes_.running() && rom()->is_loaded()) {
|
||||
ppu_texture_ =
|
||||
SDL_CreateTexture(rom()->renderer().get(), SDL_PIXELFORMAT_ARGB8888,
|
||||
SDL_TEXTUREACCESS_STREAMING, 512, 480);
|
||||
ppu_texture_ = SDL_CreateTexture(core::Renderer::GetInstance().renderer(),
|
||||
SDL_PIXELFORMAT_ARGB8888,
|
||||
SDL_TEXTUREACCESS_STREAMING, 512, 480);
|
||||
if (ppu_texture_ == NULL) {
|
||||
printf("Failed to create texture: %s\n", SDL_GetError());
|
||||
return;
|
||||
@@ -118,7 +118,8 @@ void Emulator::RenderSnesPpu() {
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
|
||||
ImGui::SetCursorPosX((ImGui::GetWindowSize().x - size.x) * 0.5f);
|
||||
ImGui::SetCursorPosY((ImGui::GetWindowSize().y - size.y) * 0.5f);
|
||||
ImGui::Image((void*)ppu_texture_, size, ImVec2(0, 0), ImVec2(1, 1));
|
||||
ImGui::Image((ImTextureID)(intptr_t)ppu_texture_, size, ImVec2(0, 0),
|
||||
ImVec2(1, 1));
|
||||
ImGui::EndChild();
|
||||
|
||||
} else {
|
||||
@@ -246,7 +247,7 @@ void Emulator::RenderNavBar() {
|
||||
}
|
||||
|
||||
if (open_file) {
|
||||
auto file_name = FileDialogWrapper::ShowOpenFileDialog();
|
||||
auto file_name = core::FileDialogWrapper::ShowOpenFileDialog();
|
||||
if (!file_name.empty()) {
|
||||
std::ifstream file(file_name, std::ios::binary);
|
||||
// Load the data directly into rom_data
|
||||
@@ -260,7 +261,101 @@ void Emulator::RenderNavBar() {
|
||||
|
||||
void Emulator::HandleEvents() {
|
||||
// Handle user input events
|
||||
// ...
|
||||
if (ImGui::IsKeyPressed(keybindings_.a_button)) {
|
||||
snes_.SetButtonState(1, 0, true);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(keybindings_.b_button)) {
|
||||
snes_.SetButtonState(1, 1, true);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(keybindings_.select_button)) {
|
||||
snes_.SetButtonState(1, 2, true);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(keybindings_.start_button)) {
|
||||
snes_.SetButtonState(1, 3, true);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(keybindings_.up_button)) {
|
||||
snes_.SetButtonState(1, 4, true);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(keybindings_.down_button)) {
|
||||
snes_.SetButtonState(1, 5, true);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(keybindings_.left_button)) {
|
||||
snes_.SetButtonState(1, 6, true);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(keybindings_.right_button)) {
|
||||
snes_.SetButtonState(1, 7, true);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(keybindings_.x_button)) {
|
||||
snes_.SetButtonState(1, 8, true);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(keybindings_.y_button)) {
|
||||
snes_.SetButtonState(1, 9, true);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(keybindings_.l_button)) {
|
||||
snes_.SetButtonState(1, 10, true);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(keybindings_.r_button)) {
|
||||
snes_.SetButtonState(1, 11, true);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyReleased(keybindings_.a_button)) {
|
||||
snes_.SetButtonState(1, 0, false);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyReleased(keybindings_.b_button)) {
|
||||
snes_.SetButtonState(1, 1, false);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyReleased(keybindings_.select_button)) {
|
||||
snes_.SetButtonState(1, 2, false);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyReleased(keybindings_.start_button)) {
|
||||
snes_.SetButtonState(1, 3, false);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyReleased(keybindings_.up_button)) {
|
||||
snes_.SetButtonState(1, 4, false);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyReleased(keybindings_.down_button)) {
|
||||
snes_.SetButtonState(1, 5, false);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyReleased(keybindings_.left_button)) {
|
||||
snes_.SetButtonState(1, 6, false);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyReleased(keybindings_.right_button)) {
|
||||
snes_.SetButtonState(1, 7, false);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyReleased(keybindings_.x_button)) {
|
||||
snes_.SetButtonState(1, 8, false);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyReleased(keybindings_.y_button)) {
|
||||
snes_.SetButtonState(1, 9, false);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyReleased(keybindings_.l_button)) {
|
||||
snes_.SetButtonState(1, 10, false);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyReleased(keybindings_.r_button)) {
|
||||
snes_.SetButtonState(1, 11, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Emulator::RenderBreakpointList() {
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
#ifndef YAZE_APP_CORE_EMULATOR_H
|
||||
#define YAZE_APP_CORE_EMULATOR_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "app/emu/snes.h"
|
||||
#include "app/gui/zeml.h"
|
||||
#include "app/rom.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -19,6 +18,46 @@ namespace app {
|
||||
*/
|
||||
namespace emu {
|
||||
|
||||
// case SDLK_z:
|
||||
// editor.emulator().snes().SetButtonState(1, 0, false);
|
||||
// case SDLK_a:
|
||||
// editor.emulator().snes().SetButtonState(1, 1, false);
|
||||
// case SDLK_RSHIFT:
|
||||
// editor.emulator().snes().SetButtonState(1, 2, false);
|
||||
// case SDLK_RETURN:
|
||||
// editor.emulator().snes().SetButtonState(1, 3, false);
|
||||
// case SDLK_UP:
|
||||
// editor.emulator().snes().SetButtonState(1, 4, false);
|
||||
// case SDLK_DOWN:
|
||||
// editor.emulator().snes().SetButtonState(1, 5, false);
|
||||
// case SDLK_LEFT:
|
||||
// editor.emulator().snes().SetButtonState(1, 6, false);
|
||||
// case SDLK_RIGHT:
|
||||
// editor.emulator().snes().SetButtonState(1, 7, false);
|
||||
// case SDLK_x:
|
||||
// editor.emulator().snes().SetButtonState(1, 8, false);
|
||||
// case SDLK_s:
|
||||
// editor.emulator().snes().SetButtonState(1, 9, false);
|
||||
// case SDLK_d:
|
||||
// editor.emulator().snes().SetButtonState(1, 10, false);
|
||||
// case SDLK_c:
|
||||
// editor.emulator().snes().SetButtonState(1, 11, false);
|
||||
|
||||
struct EmulatorKeybindings {
|
||||
ImGuiKey a_button = ImGuiKey_Z;
|
||||
ImGuiKey b_button = ImGuiKey_A;
|
||||
ImGuiKey x_button = ImGuiKey_S;
|
||||
ImGuiKey y_button = ImGuiKey_X;
|
||||
ImGuiKey l_button = ImGuiKey_Q;
|
||||
ImGuiKey r_button = ImGuiKey_W;
|
||||
ImGuiKey start_button = ImGuiKey_Enter;
|
||||
ImGuiKey select_button = ImGuiKey_Backspace;
|
||||
ImGuiKey up_button = ImGuiKey_UpArrow;
|
||||
ImGuiKey down_button = ImGuiKey_DownArrow;
|
||||
ImGuiKey left_button = ImGuiKey_LeftArrow;
|
||||
ImGuiKey right_button = ImGuiKey_RightArrow;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Emulator
|
||||
* @brief A class for emulating and debugging SNES games.
|
||||
@@ -145,6 +184,8 @@ class Emulator : public SharedRom {
|
||||
|
||||
std::vector<uint8_t> rom_data_;
|
||||
|
||||
EmulatorKeybindings keybindings_;
|
||||
|
||||
gui::zeml::Node emulator_node_;
|
||||
};
|
||||
|
||||
|
||||
@@ -17,23 +17,23 @@ static const int transferLength[8] = {1, 2, 2, 4, 4, 4, 2, 4};
|
||||
void Reset(MemoryImpl* memory) {
|
||||
auto channel = memory->dma_channels();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
channel[i].bAdr = 0xff;
|
||||
channel[i].aAdr = 0xffff;
|
||||
channel[i].aBank = 0xff;
|
||||
channel[i].b_addr = 0xff;
|
||||
channel[i].a_addr = 0xffff;
|
||||
channel[i].a_bank = 0xff;
|
||||
channel[i].size = 0xffff;
|
||||
channel[i].indBank = 0xff;
|
||||
channel[i].tableAdr = 0xffff;
|
||||
channel[i].repCount = 0xff;
|
||||
channel[i].ind_bank = 0xff;
|
||||
channel[i].table_addr = 0xffff;
|
||||
channel[i].rep_count = 0xff;
|
||||
channel[i].unusedByte = 0xff;
|
||||
channel[i].dmaActive = false;
|
||||
channel[i].hdmaActive = false;
|
||||
channel[i].dma_active = false;
|
||||
channel[i].hdma_active = false;
|
||||
channel[i].mode = 7;
|
||||
channel[i].fixed = true;
|
||||
channel[i].decrement = true;
|
||||
channel[i].indirect = true;
|
||||
channel[i].fromB = true;
|
||||
channel[i].from_b = true;
|
||||
channel[i].unusedBit = true;
|
||||
channel[i].doTransfer = false;
|
||||
channel[i].do_transfer = false;
|
||||
channel[i].terminated = false;
|
||||
}
|
||||
memory->set_dma_state(0);
|
||||
@@ -51,20 +51,20 @@ uint8_t Read(MemoryImpl* memory, uint16_t adr) {
|
||||
val |= channel[c].decrement << 4;
|
||||
val |= channel[c].unusedBit << 5;
|
||||
val |= channel[c].indirect << 6;
|
||||
val |= channel[c].fromB << 7;
|
||||
val |= channel[c].from_b << 7;
|
||||
return val;
|
||||
}
|
||||
case 0x1: {
|
||||
return channel[c].bAdr;
|
||||
return channel[c].b_addr;
|
||||
}
|
||||
case 0x2: {
|
||||
return channel[c].aAdr & 0xff;
|
||||
return channel[c].a_addr & 0xff;
|
||||
}
|
||||
case 0x3: {
|
||||
return channel[c].aAdr >> 8;
|
||||
return channel[c].a_addr >> 8;
|
||||
}
|
||||
case 0x4: {
|
||||
return channel[c].aBank;
|
||||
return channel[c].a_bank;
|
||||
}
|
||||
case 0x5: {
|
||||
return channel[c].size & 0xff;
|
||||
@@ -73,16 +73,16 @@ uint8_t Read(MemoryImpl* memory, uint16_t adr) {
|
||||
return channel[c].size >> 8;
|
||||
}
|
||||
case 0x7: {
|
||||
return channel[c].indBank;
|
||||
return channel[c].ind_bank;
|
||||
}
|
||||
case 0x8: {
|
||||
return channel[c].tableAdr & 0xff;
|
||||
return channel[c].table_addr & 0xff;
|
||||
}
|
||||
case 0x9: {
|
||||
return channel[c].tableAdr >> 8;
|
||||
return channel[c].table_addr >> 8;
|
||||
}
|
||||
case 0xa: {
|
||||
return channel[c].repCount;
|
||||
return channel[c].rep_count;
|
||||
}
|
||||
case 0xb:
|
||||
case 0xf: {
|
||||
@@ -104,23 +104,23 @@ void Write(MemoryImpl* memory, uint16_t adr, uint8_t val) {
|
||||
channel[c].decrement = val & 0x10;
|
||||
channel[c].unusedBit = val & 0x20;
|
||||
channel[c].indirect = val & 0x40;
|
||||
channel[c].fromB = val & 0x80;
|
||||
channel[c].from_b = val & 0x80;
|
||||
break;
|
||||
}
|
||||
case 0x1: {
|
||||
channel[c].bAdr = val;
|
||||
channel[c].b_addr = val;
|
||||
break;
|
||||
}
|
||||
case 0x2: {
|
||||
channel[c].aAdr = (channel[c].aAdr & 0xff00) | val;
|
||||
channel[c].a_addr = (channel[c].a_addr & 0xff00) | val;
|
||||
break;
|
||||
}
|
||||
case 0x3: {
|
||||
channel[c].aAdr = (channel[c].aAdr & 0xff) | (val << 8);
|
||||
channel[c].a_addr = (channel[c].a_addr & 0xff) | (val << 8);
|
||||
break;
|
||||
}
|
||||
case 0x4: {
|
||||
channel[c].aBank = val;
|
||||
channel[c].a_bank = val;
|
||||
break;
|
||||
}
|
||||
case 0x5: {
|
||||
@@ -132,19 +132,19 @@ void Write(MemoryImpl* memory, uint16_t adr, uint8_t val) {
|
||||
break;
|
||||
}
|
||||
case 0x7: {
|
||||
channel[c].indBank = val;
|
||||
channel[c].ind_bank = val;
|
||||
break;
|
||||
}
|
||||
case 0x8: {
|
||||
channel[c].tableAdr = (channel[c].tableAdr & 0xff00) | val;
|
||||
channel[c].table_addr = (channel[c].table_addr & 0xff00) | val;
|
||||
break;
|
||||
}
|
||||
case 0x9: {
|
||||
channel[c].tableAdr = (channel[c].tableAdr & 0xff) | (val << 8);
|
||||
channel[c].table_addr = (channel[c].table_addr & 0xff) | (val << 8);
|
||||
break;
|
||||
}
|
||||
case 0xa: {
|
||||
channel[c].repCount = val;
|
||||
channel[c].rep_count = val;
|
||||
break;
|
||||
}
|
||||
case 0xb:
|
||||
@@ -168,22 +168,22 @@ void DoDma(SNES* snes, MemoryImpl* memory, int cpuCycles) {
|
||||
// full transfer overhead
|
||||
WaitCycle(snes, memory);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (!channel[i].dmaActive) continue;
|
||||
if (!channel[i].dma_active) continue;
|
||||
// do channel i
|
||||
WaitCycle(snes, memory); // overhead per channel
|
||||
int offIndex = 0;
|
||||
while (channel[i].dmaActive) {
|
||||
while (channel[i].dma_active) {
|
||||
WaitCycle(snes, memory);
|
||||
TransferByte(snes, memory, channel[i].aAdr, channel[i].aBank,
|
||||
channel[i].bAdr + bAdrOffsets[channel[i].mode][offIndex++],
|
||||
channel[i].fromB);
|
||||
TransferByte(snes, memory, channel[i].a_addr, channel[i].a_bank,
|
||||
channel[i].b_addr + bAdrOffsets[channel[i].mode][offIndex++],
|
||||
channel[i].from_b);
|
||||
offIndex &= 3;
|
||||
if (!channel[i].fixed) {
|
||||
channel[i].aAdr += channel[i].decrement ? -1 : 1;
|
||||
channel[i].a_addr += channel[i].decrement ? -1 : 1;
|
||||
}
|
||||
channel[i].size--;
|
||||
if (channel[i].size == 0) {
|
||||
channel[i].dmaActive = false;
|
||||
channel[i].dma_active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,8 +224,8 @@ void InitHdma(SNES* snes, MemoryImpl* memory, bool do_sync, int cpu_cycles) {
|
||||
bool hdmaEnabled = false;
|
||||
// check if a channel is enabled, and do reset
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (channel[i].hdmaActive) hdmaEnabled = true;
|
||||
channel[i].doTransfer = false;
|
||||
if (channel[i].hdma_active) hdmaEnabled = true;
|
||||
channel[i].do_transfer = false;
|
||||
channel[i].terminated = false;
|
||||
}
|
||||
if (!hdmaEnabled) return;
|
||||
@@ -235,24 +235,24 @@ void InitHdma(SNES* snes, MemoryImpl* memory, bool do_sync, int cpu_cycles) {
|
||||
// full transfer overhead
|
||||
snes->RunCycles(8);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (channel[i].hdmaActive) {
|
||||
if (channel[i].hdma_active) {
|
||||
// terminate any dma
|
||||
channel[i].dmaActive = false;
|
||||
channel[i].dma_active = false;
|
||||
// load address, repCount, and indirect address if needed
|
||||
snes->RunCycles(8);
|
||||
channel[i].tableAdr = channel[i].aAdr;
|
||||
channel[i].repCount =
|
||||
snes->Read((channel[i].aBank << 16) | channel[i].tableAdr++);
|
||||
if (channel[i].repCount == 0) channel[i].terminated = true;
|
||||
channel[i].table_addr = channel[i].a_addr;
|
||||
channel[i].rep_count =
|
||||
snes->Read((channel[i].a_bank << 16) | channel[i].table_addr++);
|
||||
if (channel[i].rep_count == 0) channel[i].terminated = true;
|
||||
if (channel[i].indirect) {
|
||||
snes->RunCycles(8);
|
||||
channel[i].size =
|
||||
snes->Read((channel[i].aBank << 16) | channel[i].tableAdr++);
|
||||
snes->Read((channel[i].a_bank << 16) | channel[i].table_addr++);
|
||||
snes->RunCycles(8);
|
||||
channel[i].size |=
|
||||
snes->Read((channel[i].aBank << 16) | channel[i].tableAdr++) << 8;
|
||||
snes->Read((channel[i].a_bank << 16) | channel[i].table_addr++) << 8;
|
||||
}
|
||||
channel[i].doTransfer = true;
|
||||
channel[i].do_transfer = true;
|
||||
}
|
||||
}
|
||||
if (do_sync) snes->SyncCycles(false, cpu_cycles);
|
||||
@@ -264,7 +264,7 @@ void DoHdma(SNES* snes, MemoryImpl* memory, bool do_sync, int cycles) {
|
||||
bool hdmaActive = false;
|
||||
int lastActive = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (channel[i].hdmaActive) {
|
||||
if (channel[i].hdma_active) {
|
||||
hdmaActive = true;
|
||||
if (!channel[i].terminated) lastActive = i;
|
||||
}
|
||||
@@ -280,20 +280,20 @@ void DoHdma(SNES* snes, MemoryImpl* memory, bool do_sync, int cycles) {
|
||||
// do all copies
|
||||
for (int i = 0; i < 8; i++) {
|
||||
// terminate any dma
|
||||
if (channel[i].hdmaActive) channel[i].dmaActive = false;
|
||||
if (channel[i].hdmaActive && !channel[i].terminated) {
|
||||
if (channel[i].hdma_active) channel[i].dma_active = false;
|
||||
if (channel[i].hdma_active && !channel[i].terminated) {
|
||||
// do the hdma
|
||||
if (channel[i].doTransfer) {
|
||||
if (channel[i].do_transfer) {
|
||||
for (int j = 0; j < transferLength[channel[i].mode]; j++) {
|
||||
snes->RunCycles(8);
|
||||
if (channel[i].indirect) {
|
||||
TransferByte(snes, memory, channel[i].size++, channel[i].indBank,
|
||||
channel[i].bAdr + bAdrOffsets[channel[i].mode][j],
|
||||
channel[i].fromB);
|
||||
TransferByte(snes, memory, channel[i].size++, channel[i].ind_bank,
|
||||
channel[i].b_addr + bAdrOffsets[channel[i].mode][j],
|
||||
channel[i].from_b);
|
||||
} else {
|
||||
TransferByte(snes, memory, channel[i].tableAdr++, channel[i].aBank,
|
||||
channel[i].bAdr + bAdrOffsets[channel[i].mode][j],
|
||||
channel[i].fromB);
|
||||
TransferByte(snes, memory, channel[i].table_addr++, channel[i].a_bank,
|
||||
channel[i].b_addr + bAdrOffsets[channel[i].mode][j],
|
||||
channel[i].from_b);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,31 +301,31 @@ void DoHdma(SNES* snes, MemoryImpl* memory, bool do_sync, int cycles) {
|
||||
}
|
||||
// do all updates
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (channel[i].hdmaActive && !channel[i].terminated) {
|
||||
channel[i].repCount--;
|
||||
channel[i].doTransfer = channel[i].repCount & 0x80;
|
||||
if (channel[i].hdma_active && !channel[i].terminated) {
|
||||
channel[i].rep_count--;
|
||||
channel[i].do_transfer = channel[i].rep_count & 0x80;
|
||||
snes->RunCycles(8);
|
||||
uint8_t newRepCount =
|
||||
snes->Read((channel[i].aBank << 16) | channel[i].tableAdr);
|
||||
if ((channel[i].repCount & 0x7f) == 0) {
|
||||
channel[i].repCount = newRepCount;
|
||||
channel[i].tableAdr++;
|
||||
snes->Read((channel[i].a_bank << 16) | channel[i].table_addr);
|
||||
if ((channel[i].rep_count & 0x7f) == 0) {
|
||||
channel[i].rep_count = newRepCount;
|
||||
channel[i].table_addr++;
|
||||
if (channel[i].indirect) {
|
||||
if (channel[i].repCount == 0 && i == lastActive) {
|
||||
if (channel[i].rep_count == 0 && i == lastActive) {
|
||||
// if this is the last active channel, only fetch high, and use 0
|
||||
// for low
|
||||
channel[i].size = 0;
|
||||
} else {
|
||||
snes->RunCycles(8);
|
||||
channel[i].size =
|
||||
snes->Read((channel[i].aBank << 16) | channel[i].tableAdr++);
|
||||
snes->Read((channel[i].a_bank << 16) | channel[i].table_addr++);
|
||||
}
|
||||
snes->RunCycles(8);
|
||||
channel[i].size |=
|
||||
snes->Read((channel[i].aBank << 16) | channel[i].tableAdr++) << 8;
|
||||
snes->Read((channel[i].a_bank << 16) | channel[i].table_addr++) << 8;
|
||||
}
|
||||
if (channel[i].repCount == 0) channel[i].terminated = true;
|
||||
channel[i].doTransfer = true;
|
||||
if (channel[i].rep_count == 0) channel[i].terminated = true;
|
||||
channel[i].do_transfer = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -359,9 +359,9 @@ void StartDma(MemoryImpl* memory, uint8_t val, bool hdma) {
|
||||
auto channel = memory->dma_channels();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (hdma) {
|
||||
channel[i].hdmaActive = val & (1 << i);
|
||||
channel[i].hdma_active = val & (1 << i);
|
||||
} else {
|
||||
channel[i].dmaActive = val & (1 << i);
|
||||
channel[i].dma_active = val & (1 << i);
|
||||
}
|
||||
}
|
||||
if (!hdma) {
|
||||
|
||||
@@ -9,24 +9,24 @@ namespace emu {
|
||||
namespace memory {
|
||||
|
||||
typedef struct DmaChannel {
|
||||
uint8_t bAdr;
|
||||
uint16_t aAdr;
|
||||
uint8_t aBank;
|
||||
uint16_t size; // also indirect hdma adr
|
||||
uint8_t indBank; // hdma
|
||||
uint16_t tableAdr; // hdma
|
||||
uint8_t repCount; // hdma
|
||||
uint8_t b_addr;
|
||||
uint16_t a_addr;
|
||||
uint8_t a_bank;
|
||||
uint16_t size; // also indirect hdma adr
|
||||
uint8_t ind_bank; // hdma
|
||||
uint16_t table_addr; // hdma
|
||||
uint8_t rep_count; // hdma
|
||||
uint8_t unusedByte;
|
||||
bool dmaActive;
|
||||
bool hdmaActive;
|
||||
bool dma_active;
|
||||
bool hdma_active;
|
||||
uint8_t mode;
|
||||
bool fixed;
|
||||
bool decrement;
|
||||
bool indirect; // hdma
|
||||
bool fromB;
|
||||
bool from_b;
|
||||
bool unusedBit;
|
||||
bool doTransfer; // hdma
|
||||
bool terminated; // hdma
|
||||
bool do_transfer; // hdma
|
||||
bool terminated; // hdma
|
||||
} DmaChannel;
|
||||
|
||||
} // namespace memory
|
||||
|
||||
@@ -1,34 +1,33 @@
|
||||
#include "app/emu/memory/memory.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "app/emu/debug/log.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace emu {
|
||||
namespace memory {
|
||||
|
||||
void MemoryImpl::Initialize(const std::vector<uint8_t>& romData, bool verbose) {
|
||||
void MemoryImpl::Initialize(const std::vector<uint8_t>& rom_data,
|
||||
bool verbose) {
|
||||
verbose_ = verbose;
|
||||
type_ = 1;
|
||||
|
||||
auto location = 0x7FC0; // GetHeaderOffset();
|
||||
romSize = 0x400 << romData[location + 0x17];
|
||||
sramSize = 0x400 << romData[location + 0x18];
|
||||
rom_.resize(romSize);
|
||||
rom_size_ = 0x400 << rom_data[location + 0x17];
|
||||
sram_size_ = 0x400 << rom_data[location + 0x18];
|
||||
rom_.resize(rom_size_);
|
||||
|
||||
// Copy memory into rom_
|
||||
for (size_t i = 0; i < romSize; i++) {
|
||||
rom_[i] = romData[i];
|
||||
for (size_t i = 0; i < rom_size_; i++) {
|
||||
rom_[i] = rom_data[i];
|
||||
}
|
||||
ram_.resize(sramSize);
|
||||
for (size_t i = 0; i < sramSize; i++) {
|
||||
ram_.resize(sram_size_);
|
||||
for (size_t i = 0; i < sram_size_; i++) {
|
||||
ram_[i] = 0;
|
||||
}
|
||||
|
||||
@@ -37,71 +36,19 @@ void MemoryImpl::Initialize(const std::vector<uint8_t>& romData, bool verbose) {
|
||||
std::fill(memory_.begin(), memory_.end(), 0);
|
||||
|
||||
// Load ROM data into memory based on LoROM mapping
|
||||
size_t romSize = romData.size();
|
||||
size_t romAddress = 0;
|
||||
size_t rom_data_size = rom_data.size();
|
||||
size_t rom_address = 0;
|
||||
const size_t ROM_CHUNK_SIZE = 0x8000; // 32 KB
|
||||
for (size_t bank = 0x00; bank <= 0x3F; ++bank) {
|
||||
for (size_t offset = 0x8000; offset <= 0xFFFF; offset += ROM_CHUNK_SIZE) {
|
||||
if (romAddress < romSize) {
|
||||
std::copy(romData.begin() + romAddress,
|
||||
romData.begin() + romAddress + ROM_CHUNK_SIZE,
|
||||
if (rom_address < rom_data_size) {
|
||||
std::copy(rom_data.begin() + rom_address,
|
||||
rom_data.begin() + rom_address + ROM_CHUNK_SIZE,
|
||||
memory_.begin() + (bank << 16) + offset);
|
||||
romAddress += ROM_CHUNK_SIZE;
|
||||
rom_address += ROM_CHUNK_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
memory::RomInfo MemoryImpl::ReadRomHeader() {
|
||||
memory::RomInfo romInfo;
|
||||
|
||||
uint32_t offset = GetHeaderOffset();
|
||||
|
||||
// Read cartridge title
|
||||
char title[22];
|
||||
for (int i = 0; i < 21; ++i) {
|
||||
title[i] = ReadByte(offset + i);
|
||||
}
|
||||
title[21] = '\0'; // Null-terminate the string
|
||||
romInfo.title = std::string(title);
|
||||
|
||||
// Read ROM speed and memory map mode
|
||||
uint8_t romSpeedAndMapMode = ReadByte(offset + 0x15);
|
||||
romInfo.romSpeed = (memory::RomSpeed)(romSpeedAndMapMode & 0x07);
|
||||
romInfo.bankSize = (memory::BankSize)((romSpeedAndMapMode >> 5) & 0x01);
|
||||
|
||||
// Read ROM type
|
||||
romInfo.romType = (memory::RomType)ReadByte(offset + 0x16);
|
||||
|
||||
// Read ROM size
|
||||
romInfo.romSize = (memory::RomSize)ReadByte(offset + 0x17);
|
||||
|
||||
// Read RAM size
|
||||
romInfo.sramSize = (memory::SramSize)ReadByte(offset + 0x18);
|
||||
|
||||
// Read country code
|
||||
romInfo.countryCode = (memory::CountryCode)ReadByte(offset + 0x19);
|
||||
|
||||
// Read license
|
||||
romInfo.license = (memory::License)ReadByte(offset + 0x1A);
|
||||
|
||||
// Read ROM version
|
||||
romInfo.version = ReadByte(offset + 0x1B);
|
||||
|
||||
// Read checksum complement
|
||||
romInfo.checksumComplement = ReadWord(offset + 0x1E);
|
||||
|
||||
// Read checksum
|
||||
romInfo.checksum = ReadWord(offset + 0x1C);
|
||||
|
||||
// Read NMI VBL vector
|
||||
romInfo.nmiVblVector = ReadWord(offset + 0x3E);
|
||||
|
||||
// Read reset vector
|
||||
romInfo.resetVector = ReadWord(offset + 0x3C);
|
||||
|
||||
return romInfo;
|
||||
}
|
||||
|
||||
uint8_t MemoryImpl::cart_read(uint8_t bank, uint16_t adr) {
|
||||
@@ -136,59 +83,59 @@ void MemoryImpl::cart_write(uint8_t bank, uint16_t adr, uint8_t val) {
|
||||
|
||||
uint8_t MemoryImpl::cart_readLorom(uint8_t bank, uint16_t adr) {
|
||||
if (((bank >= 0x70 && bank < 0x7e) || bank >= 0xf0) && adr < 0x8000 &&
|
||||
sramSize > 0) {
|
||||
sram_size_ > 0) {
|
||||
// banks 70-7e and f0-ff, adr 0000-7fff
|
||||
return ram_[(((bank & 0xf) << 15) | adr) & (sramSize - 1)];
|
||||
return ram_[(((bank & 0xf) << 15) | adr) & (sram_size_ - 1)];
|
||||
}
|
||||
bank &= 0x7f;
|
||||
if (adr >= 0x8000 || bank >= 0x40) {
|
||||
// adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff
|
||||
return rom_[((bank << 15) | (adr & 0x7fff)) & (romSize - 1)];
|
||||
return rom_[((bank << 15) | (adr & 0x7fff)) & (rom_size_ - 1)];
|
||||
}
|
||||
return open_bus_;
|
||||
}
|
||||
|
||||
void MemoryImpl::cart_writeLorom(uint8_t bank, uint16_t adr, uint8_t val) {
|
||||
if (((bank >= 0x70 && bank < 0x7e) || bank > 0xf0) && adr < 0x8000 &&
|
||||
sramSize > 0) {
|
||||
sram_size_ > 0) {
|
||||
// banks 70-7e and f0-ff, adr 0000-7fff
|
||||
ram_[(((bank & 0xf) << 15) | adr) & (sramSize - 1)] = val;
|
||||
ram_[(((bank & 0xf) << 15) | adr) & (sram_size_ - 1)] = val;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t MemoryImpl::cart_readHirom(uint8_t bank, uint16_t adr) {
|
||||
bank &= 0x7f;
|
||||
if (bank < 0x40 && adr >= 0x6000 && adr < 0x8000 && sramSize > 0) {
|
||||
if (bank < 0x40 && adr >= 0x6000 && adr < 0x8000 && sram_size_ > 0) {
|
||||
// banks 00-3f and 80-bf, adr 6000-7fff
|
||||
return ram_[(((bank & 0x3f) << 13) | (adr & 0x1fff)) & (sramSize - 1)];
|
||||
return ram_[(((bank & 0x3f) << 13) | (adr & 0x1fff)) & (sram_size_ - 1)];
|
||||
}
|
||||
if (adr >= 0x8000 || bank >= 0x40) {
|
||||
// adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff
|
||||
return rom_[(((bank & 0x3f) << 16) | adr) & (romSize - 1)];
|
||||
return rom_[(((bank & 0x3f) << 16) | adr) & (rom_size_ - 1)];
|
||||
}
|
||||
return open_bus_;
|
||||
}
|
||||
|
||||
uint8_t MemoryImpl::cart_readExHirom(uint8_t bank, uint16_t adr) {
|
||||
if ((bank & 0x7f) < 0x40 && adr >= 0x6000 && adr < 0x8000 && sramSize > 0) {
|
||||
if ((bank & 0x7f) < 0x40 && adr >= 0x6000 && adr < 0x8000 && sram_size_ > 0) {
|
||||
// banks 00-3f and 80-bf, adr 6000-7fff
|
||||
return ram_[(((bank & 0x3f) << 13) | (adr & 0x1fff)) & (sramSize - 1)];
|
||||
return ram_[(((bank & 0x3f) << 13) | (adr & 0x1fff)) & (sram_size_ - 1)];
|
||||
}
|
||||
bool secondHalf = bank < 0x80;
|
||||
bank &= 0x7f;
|
||||
if (adr >= 0x8000 || bank >= 0x40) {
|
||||
// adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff
|
||||
return rom_[(((bank & 0x3f) << 16) | (secondHalf ? 0x400000 : 0) | adr) &
|
||||
(romSize - 1)];
|
||||
(rom_size_ - 1)];
|
||||
}
|
||||
return open_bus_;
|
||||
}
|
||||
|
||||
void MemoryImpl::cart_writeHirom(uint8_t bank, uint16_t adr, uint8_t val) {
|
||||
bank &= 0x7f;
|
||||
if (bank < 0x40 && adr >= 0x6000 && adr < 0x8000 && sramSize > 0) {
|
||||
if (bank < 0x40 && adr >= 0x6000 && adr < 0x8000 && sram_size_ > 0) {
|
||||
// banks 00-3f and 80-bf, adr 6000-7fff
|
||||
ram_[(((bank & 0x3f) << 13) | (adr & 0x1fff)) & (sramSize - 1)] = val;
|
||||
ram_[(((bank & 0x3f) << 13) | (adr & 0x1fff)) & (sram_size_ - 1)] = val;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,70 +165,6 @@ uint32_t MemoryImpl::GetMappedAddress(uint32_t address) const {
|
||||
return address; // Return the original address if no mapping is defined
|
||||
}
|
||||
|
||||
void DrawSnesMemoryMapping(const MemoryImpl& memory) {
|
||||
// Using those as a base value to create width/height that are factor of the
|
||||
// size of our font
|
||||
const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
|
||||
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
|
||||
const char* column_names[] = {
|
||||
"Offset", "0x00", "0x01", "0x02", "0x03", "0x04", "0x05", "0x06", "0x07",
|
||||
"0x08", "0x09", "0x0A", "0x0B", "0x0C", "0x0D", "0x0E", "0x0F", "0x10",
|
||||
"0x11", "0x12", "0x13", "0x14", "0x15", "0x16", "0x17", "0x18", "0x19",
|
||||
"0x1A", "0x1B", "0x1C", "0x1D", "0x1E", "0x1F"};
|
||||
const int columns_count = IM_ARRAYSIZE(column_names);
|
||||
const int rows_count = 16;
|
||||
|
||||
static ImGuiTableFlags table_flags =
|
||||
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX |
|
||||
ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter |
|
||||
ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable |
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_HighlightHoveredColumn;
|
||||
static bool bools[columns_count * rows_count] = {};
|
||||
static int frozen_cols = 1;
|
||||
static int frozen_rows = 2;
|
||||
ImGui::CheckboxFlags("_ScrollX", &table_flags, ImGuiTableFlags_ScrollX);
|
||||
ImGui::CheckboxFlags("_ScrollY", &table_flags, ImGuiTableFlags_ScrollY);
|
||||
ImGui::CheckboxFlags("_NoBordersInBody", &table_flags,
|
||||
ImGuiTableFlags_NoBordersInBody);
|
||||
ImGui::CheckboxFlags("_HighlightHoveredColumn", &table_flags,
|
||||
ImGuiTableFlags_HighlightHoveredColumn);
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
|
||||
ImGui::SliderInt("Frozen columns", &frozen_cols, 0, 2);
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
|
||||
ImGui::SliderInt("Frozen rows", &frozen_rows, 0, 2);
|
||||
|
||||
if (ImGui::BeginTable("table_angled_headers", columns_count, table_flags,
|
||||
ImVec2(0.0f, TEXT_BASE_HEIGHT * 12))) {
|
||||
ImGui::TableSetupColumn(
|
||||
column_names[0],
|
||||
ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoReorder);
|
||||
for (int n = 1; n < columns_count; n++)
|
||||
ImGui::TableSetupColumn(column_names[n],
|
||||
ImGuiTableColumnFlags_AngledHeader |
|
||||
ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupScrollFreeze(frozen_cols, frozen_rows);
|
||||
|
||||
ImGui::TableAngledHeadersRow();
|
||||
ImGui::TableHeadersRow();
|
||||
for (int row = 0; row < rows_count; row++) {
|
||||
ImGui::PushID(row);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("Offset 0x%04X", row);
|
||||
for (int column = 1; column < columns_count; column++)
|
||||
if (ImGui::TableSetColumnIndex(column)) {
|
||||
ImGui::PushID(column);
|
||||
ImGui::Checkbox("", &bools[row * columns_count + column]);
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace memory
|
||||
} // namespace emu
|
||||
} // namespace app
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "app/emu/debug/log.h"
|
||||
#include "app/emu/memory/dma_channel.h"
|
||||
|
||||
// LoROM (Mode 20):
|
||||
@@ -32,67 +31,6 @@ namespace app {
|
||||
namespace emu {
|
||||
namespace memory {
|
||||
|
||||
enum RomSpeed { SLOW_ROM = 0x00, FAST_ROM = 0x07 };
|
||||
|
||||
enum BankSize { LOW_ROM = 0x00, HI_ROM = 0x01 };
|
||||
|
||||
enum RomType {
|
||||
ROM_DEFAULT = 0x00,
|
||||
ROM_RAM = 0x01,
|
||||
ROM_SRAM = 0x02,
|
||||
ROM_DSP1 = 0x03,
|
||||
ROM_DSP1_RAM = 0x04,
|
||||
ROM_DSP1_SRAM = 0x05,
|
||||
FX = 0x06
|
||||
};
|
||||
|
||||
enum RomSize {
|
||||
SIZE_2_MBIT = 0x08,
|
||||
SIZE_4_MBIT = 0x09,
|
||||
SIZE_8_MBIT = 0x0A,
|
||||
SIZE_16_MBIT = 0x0B,
|
||||
SIZE_32_MBIT = 0x0C
|
||||
};
|
||||
|
||||
enum SramSize {
|
||||
NO_SRAM = 0x00,
|
||||
SRAM_16_KBIT = 0x01,
|
||||
SRAM_32_KBIT = 0x02,
|
||||
SRAM_64_KBIT = 0x03
|
||||
};
|
||||
|
||||
enum CountryCode {
|
||||
JAPAN = 0x00,
|
||||
USA = 0x01,
|
||||
EUROPE_OCEANIA_ASIA = 0x02,
|
||||
// ... and other countries
|
||||
};
|
||||
|
||||
enum License {
|
||||
INVALID = 0,
|
||||
NINTENDO = 1,
|
||||
ZAMUSE = 5,
|
||||
CAPCOM = 8,
|
||||
// ... and other licenses
|
||||
};
|
||||
|
||||
class RomInfo {
|
||||
public:
|
||||
std::string title;
|
||||
RomSpeed romSpeed;
|
||||
BankSize bankSize;
|
||||
RomType romType;
|
||||
RomSize romSize;
|
||||
SramSize sramSize;
|
||||
CountryCode countryCode;
|
||||
License license;
|
||||
uint8_t version;
|
||||
uint16_t checksumComplement;
|
||||
uint16_t checksum;
|
||||
uint16_t nmiVblVector;
|
||||
uint16_t resetVector;
|
||||
};
|
||||
|
||||
typedef struct CpuCallbacks {
|
||||
std::function<uint8_t(uint32_t)> read_byte;
|
||||
std::function<void(uint32_t, uint8_t)> write_byte;
|
||||
@@ -161,17 +99,13 @@ class Memory {
|
||||
* system.
|
||||
*
|
||||
*/
|
||||
class MemoryImpl : public Memory, public Loggable {
|
||||
class MemoryImpl : public Memory {
|
||||
public:
|
||||
uint32_t romSize;
|
||||
uint32_t sramSize;
|
||||
void Initialize(const std::vector<uint8_t>& romData, bool verbose = false);
|
||||
|
||||
uint16_t GetHeaderOffset() {
|
||||
uint8_t mapMode = memory_[(0x00 << 16) + 0xFFD5];
|
||||
uint16_t offset;
|
||||
|
||||
switch (mapMode & 0x07) {
|
||||
switch (memory_[(0x00 << 16) + 0xFFD5] & 0x07) {
|
||||
case 0: // LoROM
|
||||
offset = 0x7FC0;
|
||||
break;
|
||||
@@ -190,8 +124,6 @@ class MemoryImpl : public Memory, public Loggable {
|
||||
return offset;
|
||||
}
|
||||
|
||||
memory::RomInfo ReadRomHeader();
|
||||
|
||||
uint8_t cart_read(uint8_t bank, uint16_t adr);
|
||||
void cart_write(uint8_t bank, uint16_t adr, uint8_t val);
|
||||
|
||||
@@ -348,6 +280,10 @@ class MemoryImpl : public Memory, public Loggable {
|
||||
|
||||
bool pal_timing_ = false;
|
||||
|
||||
// Memory regions
|
||||
uint32_t rom_size_;
|
||||
uint32_t sram_size_;
|
||||
|
||||
// Frame timing
|
||||
uint16_t h_pos_ = 0;
|
||||
uint16_t v_pos_ = 0;
|
||||
@@ -355,9 +291,6 @@ class MemoryImpl : public Memory, public Loggable {
|
||||
// Dma State
|
||||
uint8_t dma_state_ = 0;
|
||||
|
||||
// Dma Channels
|
||||
DmaChannel channel[8];
|
||||
|
||||
// Open bus
|
||||
uint8_t open_bus_ = 0;
|
||||
|
||||
@@ -367,12 +300,13 @@ class MemoryImpl : public Memory, public Loggable {
|
||||
// Cart Type
|
||||
uint8_t type_ = 1;
|
||||
|
||||
// Dma Channels
|
||||
DmaChannel channel[8];
|
||||
|
||||
// Memory (64KB)
|
||||
std::vector<uint8_t> memory_;
|
||||
};
|
||||
|
||||
void DrawSnesMemoryMapping(const MemoryImpl& memory);
|
||||
|
||||
} // namespace memory
|
||||
} // namespace emu
|
||||
} // namespace app
|
||||
|
||||
@@ -1,229 +0,0 @@
|
||||
#ifndef YAZE_TEST_MOCK_MOCK_MEMORY_H
|
||||
#define YAZE_TEST_MOCK_MOCK_MEMORY_H
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "app/emu/cpu/clock.h"
|
||||
#include "app/emu/cpu/cpu.h"
|
||||
#include "app/emu/memory/memory.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace emu {
|
||||
namespace memory {
|
||||
|
||||
/**
|
||||
* @brief Mock CPU class for testing
|
||||
*/
|
||||
class MockClock : public Clock {
|
||||
public:
|
||||
MOCK_METHOD(void, UpdateClock, (double delta), (override));
|
||||
MOCK_METHOD(unsigned long long, GetCycleCount, (), (const, override));
|
||||
MOCK_METHOD(void, ResetAccumulatedTime, (), (override));
|
||||
MOCK_METHOD(void, SetFrequency, (float new_frequency), (override));
|
||||
MOCK_METHOD(float, GetFrequency, (), (const, override));
|
||||
};
|
||||
|
||||
/**
|
||||
* @class MockMemory
|
||||
* @brief A mock implementation of the Memory class.
|
||||
*
|
||||
* This class is used for testing purposes and provides a mock implementation of
|
||||
* the Memory class. It allows for reading and writing bytes, words, and longs
|
||||
* to memory, as well as pushing and popping values onto and from the stack. It
|
||||
* also provides methods for setting and getting the stack pointer, initializing
|
||||
* memory with ROM data, and clearing memory.
|
||||
*
|
||||
* The mock memory is represented by a vector of uint8_t values, where each
|
||||
* element represents a byte of memory. The memory can be accessed using the []
|
||||
* operator, and its contents can be set using the SetMemoryContents() method.
|
||||
*
|
||||
* @note This class is intended for testing purposes only and should not be used
|
||||
* in production code.
|
||||
*/
|
||||
class MockMemory : public Memory {
|
||||
public:
|
||||
MOCK_CONST_METHOD1(ReadByte, uint8_t(uint32_t address));
|
||||
MOCK_CONST_METHOD1(ReadWord, uint16_t(uint32_t address));
|
||||
MOCK_CONST_METHOD1(ReadWordLong, uint32_t(uint32_t address));
|
||||
MOCK_METHOD(std::vector<uint8_t>, ReadByteVector,
|
||||
(uint32_t address, uint16_t length), (const, override));
|
||||
|
||||
MOCK_METHOD2(WriteByte, void(uint32_t address, uint8_t value));
|
||||
MOCK_METHOD2(WriteWord, void(uint32_t address, uint16_t value));
|
||||
MOCK_METHOD2(WriteLong, void(uint32_t address, uint32_t value));
|
||||
|
||||
MOCK_METHOD1(PushByte, void(uint8_t value));
|
||||
MOCK_METHOD0(PopByte, uint8_t());
|
||||
MOCK_METHOD1(PushWord, void(uint16_t value));
|
||||
MOCK_METHOD0(PopWord, uint16_t());
|
||||
MOCK_METHOD1(PushLong, void(uint32_t value));
|
||||
MOCK_METHOD0(PopLong, uint32_t());
|
||||
|
||||
MOCK_CONST_METHOD0(SP, uint16_t());
|
||||
MOCK_METHOD1(SetSP, void(uint16_t value));
|
||||
|
||||
MOCK_METHOD1(SetMemory, void(const std::vector<uint8_t>& data));
|
||||
MOCK_METHOD1(LoadData, void(const std::vector<uint8_t>& data));
|
||||
|
||||
MOCK_METHOD0(ClearMemory, void());
|
||||
|
||||
MOCK_CONST_METHOD1(at, uint8_t(int i));
|
||||
uint8_t operator[](int i) const override { return memory_[i]; }
|
||||
|
||||
MOCK_METHOD0(init_hdma_request, void());
|
||||
MOCK_METHOD0(run_hdma_request, void());
|
||||
MOCK_METHOD1(set_hdma_run_requested, void(bool value));
|
||||
MOCK_METHOD1(set_hdma_init_requested, void(bool value));
|
||||
MOCK_CONST_METHOD0(hdma_init_requested, bool());
|
||||
MOCK_CONST_METHOD0(hdma_run_requested, bool());
|
||||
MOCK_METHOD1(set_pal_timing, void(bool value));
|
||||
MOCK_CONST_METHOD0(pal_timing, bool());
|
||||
MOCK_CONST_METHOD0(h_pos, uint16_t());
|
||||
MOCK_CONST_METHOD0(v_pos, uint16_t());
|
||||
MOCK_METHOD1(set_h_pos, void(uint16_t value));
|
||||
MOCK_METHOD1(set_v_pos, void(uint16_t value));
|
||||
MOCK_METHOD1(set_open_bus, void(uint8_t value));
|
||||
MOCK_CONST_METHOD0(open_bus, uint8_t());
|
||||
|
||||
void SetMemoryContents(const std::vector<uint8_t>& data) {
|
||||
if (data.size() > memory_.size()) {
|
||||
memory_.resize(data.size());
|
||||
}
|
||||
std::copy(data.begin(), data.end(), memory_.begin());
|
||||
}
|
||||
|
||||
void SetMemoryContents(const std::vector<uint16_t>& data) {
|
||||
if (data.size() > memory_.size()) {
|
||||
memory_.resize(data.size());
|
||||
}
|
||||
int i = 0;
|
||||
for (const auto& each : data) {
|
||||
memory_[i] = each & 0xFF;
|
||||
memory_[i + 1] = (each >> 8) & 0xFF;
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void InsertMemory(const uint64_t address, const std::vector<uint8_t>& data) {
|
||||
if (address > memory_.size()) {
|
||||
memory_.resize(address + data.size());
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (const auto& each : data) {
|
||||
memory_[address + i] = each;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// 16MB = 0x1000000
|
||||
// 02MB = 0x200000
|
||||
void Initialize(const std::vector<uint8_t>& romData) {
|
||||
// 16 MB, simplifying the memory layout for testing
|
||||
memory_.resize(0x1000000);
|
||||
|
||||
// Clear memory
|
||||
std::fill(memory_.begin(), memory_.end(), 0);
|
||||
|
||||
// Load ROM data into mock memory
|
||||
size_t romSize = romData.size();
|
||||
size_t romAddress = 0;
|
||||
const size_t ROM_CHUNK_SIZE = 0x8000; // 32 KB
|
||||
for (size_t bank = 0x00; bank <= 0xBF; bank += 0x80) {
|
||||
for (size_t offset = 0x8000; offset <= 0xFFFF; offset += ROM_CHUNK_SIZE) {
|
||||
if (romAddress < romSize) {
|
||||
std::copy(romData.begin() + romAddress,
|
||||
romData.begin() + romAddress + ROM_CHUNK_SIZE,
|
||||
memory_.begin() + (bank << 16) + offset);
|
||||
romAddress += ROM_CHUNK_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Init() {
|
||||
ON_CALL(*this, ReadByte(::testing::_))
|
||||
.WillByDefault(
|
||||
[this](uint32_t address) { return memory_.at(address); });
|
||||
ON_CALL(*this, ReadWord(::testing::_))
|
||||
.WillByDefault([this](uint32_t address) {
|
||||
return static_cast<uint16_t>(memory_.at(address)) |
|
||||
(static_cast<uint16_t>(memory_.at(address + 1)) << 8);
|
||||
});
|
||||
ON_CALL(*this, ReadWordLong(::testing::_))
|
||||
.WillByDefault([this](uint32_t address) {
|
||||
return static_cast<uint32_t>(memory_.at(address)) |
|
||||
(static_cast<uint32_t>(memory_.at(address + 1)) << 8) |
|
||||
(static_cast<uint32_t>(memory_.at(address + 2)) << 16);
|
||||
});
|
||||
ON_CALL(*this, ReadByteVector(::testing::_, ::testing::_))
|
||||
.WillByDefault([this](uint32_t address, uint16_t length) {
|
||||
std::vector<uint8_t> data;
|
||||
for (int i = 0; i < length; i++) {
|
||||
data.push_back(memory_.at(address + i));
|
||||
}
|
||||
return data;
|
||||
});
|
||||
ON_CALL(*this, WriteByte(::testing::_, ::testing::_))
|
||||
.WillByDefault([this](uint32_t address, uint8_t value) {
|
||||
memory_[address] = value;
|
||||
});
|
||||
ON_CALL(*this, WriteWord(::testing::_, ::testing::_))
|
||||
.WillByDefault([this](uint32_t address, uint16_t value) {
|
||||
memory_[address] = value & 0xFF;
|
||||
memory_[address + 1] = (value >> 8) & 0xFF;
|
||||
});
|
||||
ON_CALL(*this, PushByte(::testing::_)).WillByDefault([this](uint8_t value) {
|
||||
memory_.at(SP_--) = value;
|
||||
});
|
||||
ON_CALL(*this, PopByte()).WillByDefault([this]() {
|
||||
uint8_t value = memory_.at(SP_);
|
||||
this->SetSP(SP_ + 1);
|
||||
return value;
|
||||
});
|
||||
ON_CALL(*this, PushWord(::testing::_))
|
||||
.WillByDefault([this](uint16_t value) {
|
||||
memory_.at(SP_) = value & 0xFF;
|
||||
memory_.at(SP_ + 1) = (value >> 8) & 0xFF;
|
||||
this->SetSP(SP_ - 2);
|
||||
});
|
||||
ON_CALL(*this, PopWord()).WillByDefault([this]() {
|
||||
uint16_t value = static_cast<uint16_t>(memory_.at(SP_)) |
|
||||
(static_cast<uint16_t>(memory_.at(SP_ + 1)) << 8);
|
||||
this->SetSP(SP_ + 2);
|
||||
return value;
|
||||
});
|
||||
ON_CALL(*this, PushLong(::testing::_))
|
||||
.WillByDefault([this](uint32_t value) {
|
||||
memory_.at(SP_) = value & 0xFF;
|
||||
memory_.at(SP_ + 1) = (value >> 8) & 0xFF;
|
||||
memory_.at(SP_ + 2) = (value >> 16) & 0xFF;
|
||||
});
|
||||
ON_CALL(*this, PopLong()).WillByDefault([this]() {
|
||||
uint32_t value = static_cast<uint32_t>(memory_.at(SP_)) |
|
||||
(static_cast<uint32_t>(memory_.at(SP_ + 1)) << 8) |
|
||||
(static_cast<uint32_t>(memory_.at(SP_ + 2)) << 16);
|
||||
this->SetSP(SP_ + 3);
|
||||
return value;
|
||||
});
|
||||
ON_CALL(*this, SP()).WillByDefault([this]() { return SP_; });
|
||||
ON_CALL(*this, SetSP(::testing::_)).WillByDefault([this](uint16_t value) {
|
||||
SP_ = value;
|
||||
});
|
||||
ON_CALL(*this, ClearMemory()).WillByDefault([this]() {
|
||||
memory_.resize(64000, 0x00);
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<uint8_t> memory_;
|
||||
uint16_t SP_ = 0x01FF;
|
||||
};
|
||||
|
||||
} // namespace memory
|
||||
} // namespace emu
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_TEST_MOCK_MOCK_MEMORY_H
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "app/emu/audio/spc700.h"
|
||||
#include "app/emu/cpu/clock.h"
|
||||
#include "app/emu/cpu/cpu.h"
|
||||
#include "app/emu/debug/debugger.h"
|
||||
#include "app/emu/memory/dma.h"
|
||||
#include "app/emu/memory/memory.h"
|
||||
#include "app/emu/video/ppu.h"
|
||||
|
||||
@@ -2,18 +2,12 @@
|
||||
#define YAZE_APP_EMU_SNES_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "app/emu/audio/apu.h"
|
||||
#include "app/emu/audio/spc700.h"
|
||||
#include "app/emu/cpu/clock.h"
|
||||
#include "app/emu/cpu/cpu.h"
|
||||
#include "app/emu/debug/debugger.h"
|
||||
#include "app/emu/memory/memory.h"
|
||||
#include "app/emu/video/ppu.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
@@ -80,7 +74,6 @@ class SNES {
|
||||
private:
|
||||
// Components of the SNES
|
||||
ClockImpl clock_;
|
||||
Debugger debugger;
|
||||
memory::MemoryImpl memory_;
|
||||
|
||||
memory::CpuCallbacks cpu_callbacks_ = {
|
||||
@@ -141,4 +134,4 @@ class SNES {
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EMU_SNES_H
|
||||
#endif // YAZE_APP_EMU_SNES_H
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
#include "bitmap.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#if YAZE_LIB_PNG == 1
|
||||
#include <png.h>
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "app/core/constants.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
|
||||
#define SDL_RETURN_IF_ERROR() \
|
||||
if (SDL_GetError() != nullptr) { \
|
||||
return absl::InternalError(SDL_GetError()); \
|
||||
}
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace gfx {
|
||||
|
||||
namespace {
|
||||
void GrayscalePalette(SDL_Palette *palette) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
palette->colors[i].r = i * 31;
|
||||
palette->colors[i].g = i * 31;
|
||||
palette->colors[i].b = i * 31;
|
||||
}
|
||||
}
|
||||
using core::SDL_Surface_Deleter;
|
||||
using core::SDL_Texture_Deleter;
|
||||
|
||||
#if YAZE_LIB_PNG == 1
|
||||
|
||||
namespace png_internal {
|
||||
|
||||
void PngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length) {
|
||||
std::vector<uint8_t> *p = (std::vector<uint8_t> *)png_get_io_ptr(png_ptr);
|
||||
@@ -47,20 +50,7 @@ void PngReadCallback(png_structp png_ptr, png_bytep outBytes,
|
||||
}
|
||||
}
|
||||
|
||||
Uint32 GetSnesPixelFormat(int format) {
|
||||
switch (format) {
|
||||
case 0:
|
||||
return SDL_PIXELFORMAT_INDEX8;
|
||||
case 1:
|
||||
return SNES_PIXELFORMAT_2BPP;
|
||||
case 2:
|
||||
return SNES_PIXELFORMAT_4BPP;
|
||||
case 3:
|
||||
return SNES_PIXELFORMAT_8BPP;
|
||||
}
|
||||
return SDL_PIXELFORMAT_INDEX8;
|
||||
}
|
||||
} // namespace
|
||||
} // namespace png_internal
|
||||
|
||||
bool ConvertSurfaceToPNG(SDL_Surface *surface, std::vector<uint8_t> &buffer) {
|
||||
png_structp png_ptr = png_create_write_struct("1.6.40", NULL, NULL, NULL);
|
||||
@@ -82,7 +72,7 @@ bool ConvertSurfaceToPNG(SDL_Surface *surface, std::vector<uint8_t> &buffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
png_set_write_fn(png_ptr, &buffer, PngWriteCallback, NULL);
|
||||
png_set_write_fn(png_ptr, &buffer, png_internal::PngWriteCallback, NULL);
|
||||
|
||||
png_colorp pal_ptr;
|
||||
|
||||
@@ -154,7 +144,7 @@ void ConvertPngToSurface(const std::vector<uint8_t> &png_data,
|
||||
}
|
||||
|
||||
// Set our custom read function
|
||||
png_set_read_fn(png_ptr, &data, PngReadCallback);
|
||||
png_set_read_fn(png_ptr, &data, png_internal::PngReadCallback);
|
||||
|
||||
// Read the PNG info
|
||||
png_read_info(png_ptr, info_ptr);
|
||||
@@ -197,40 +187,78 @@ void ConvertPngToSurface(const std::vector<uint8_t> &png_data,
|
||||
SDL_Log("Successfully created SDL_Surface from PNG data");
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(int width, int height, int depth, int data_size) {
|
||||
Create(width, height, depth, Bytes(data_size, 0));
|
||||
std::vector<uint8_t> Bitmap::GetPngData() {
|
||||
ConvertSurfaceToPNG(surface_.get(), png_data_);
|
||||
return png_data_;
|
||||
}
|
||||
|
||||
void Bitmap::Create(int width, int height, int depth, const Bytes &data) {
|
||||
active_ = true;
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
depth_ = depth;
|
||||
data_ = data;
|
||||
data_size_ = data.size();
|
||||
pixel_data_ = data_.data();
|
||||
surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
|
||||
SDL_CreateRGBSurfaceWithFormat(0, width_, height_, depth_,
|
||||
SDL_PIXELFORMAT_INDEX8),
|
||||
SDL_Surface_Deleter());
|
||||
surface_->pixels = pixel_data_;
|
||||
GrayscalePalette(surface_->format->palette);
|
||||
active_ = true;
|
||||
#endif // YAZE_LIB_PNG
|
||||
|
||||
namespace {
|
||||
|
||||
void GrayscalePalette(SDL_Palette *palette) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
palette->colors[i].r = i * 31;
|
||||
palette->colors[i].g = i * 31;
|
||||
palette->colors[i].b = i * 31;
|
||||
}
|
||||
}
|
||||
|
||||
Uint32 GetSnesPixelFormat(int format) {
|
||||
switch (format) {
|
||||
case 0:
|
||||
return SDL_PIXELFORMAT_INDEX8;
|
||||
case 1:
|
||||
return SNES_PIXELFORMAT_2BPP;
|
||||
case 2:
|
||||
return SNES_PIXELFORMAT_4BPP;
|
||||
case 3:
|
||||
return SNES_PIXELFORMAT_8BPP;
|
||||
}
|
||||
return SDL_PIXELFORMAT_INDEX8;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Bitmap::SaveSurfaceToFile(std::string_view filename) {
|
||||
SDL_SaveBMP(surface_.get(), filename.data());
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(int width, int height, int depth, int data_size) {
|
||||
Create(width, height, depth, std::vector<uint8_t>(data_size, 0));
|
||||
}
|
||||
|
||||
void Bitmap::Create(int width, int height, int depth,
|
||||
const std::vector<uint8_t> &data) {
|
||||
Create(width, height, depth, kIndexed, data);
|
||||
}
|
||||
|
||||
void Bitmap::Create(int width, int height, int depth, int format,
|
||||
const Bytes &data) {
|
||||
const std::vector<uint8_t> &data) {
|
||||
if (data.empty()) {
|
||||
SDL_Log("Bitmap data is empty\n");
|
||||
active_ = false;
|
||||
return;
|
||||
}
|
||||
active_ = true;
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
depth_ = depth;
|
||||
data_ = data;
|
||||
data_size_ = data.size();
|
||||
if (data_size_ == 0) {
|
||||
SDL_Log("Data provided to Bitmap is empty.\n");
|
||||
return;
|
||||
}
|
||||
pixel_data_ = data_.data();
|
||||
surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
|
||||
surface_ = std::shared_ptr<SDL_Surface>{
|
||||
SDL_CreateRGBSurfaceWithFormat(0, width_, height_, depth_,
|
||||
GetSnesPixelFormat(format)),
|
||||
SDL_Surface_Deleter());
|
||||
SDL_Surface_Deleter{}};
|
||||
if (surface_ == nullptr) {
|
||||
SDL_Log("SDL_CreateRGBSurfaceWithFormat failed: %s\n", SDL_GetError());
|
||||
active_ = false;
|
||||
return;
|
||||
}
|
||||
surface_->pixels = pixel_data_;
|
||||
active_ = true;
|
||||
}
|
||||
@@ -242,6 +270,11 @@ void Bitmap::Reformat(int format) {
|
||||
SDL_Surface_Deleter());
|
||||
surface_->pixels = pixel_data_;
|
||||
active_ = true;
|
||||
auto apply_palette = ApplyPalette(palette_);
|
||||
if (!apply_palette.ok()) {
|
||||
SDL_Log("Failed to apply palette: %s\n", apply_palette.message().data());
|
||||
active_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::CreateTexture(SDL_Renderer *renderer) {
|
||||
@@ -259,6 +292,22 @@ void Bitmap::CreateTexture(SDL_Renderer *renderer) {
|
||||
SDL_Log("SDL_CreateTextureFromSurface failed: %s\n", SDL_GetError());
|
||||
}
|
||||
|
||||
converted_surface_ = std::shared_ptr<SDL_Surface>{
|
||||
SDL_ConvertSurfaceFormat(surface_.get(), SDL_PIXELFORMAT_ARGB8888, 0),
|
||||
SDL_Surface_Deleter{}};
|
||||
if (converted_surface_ == nullptr) {
|
||||
SDL_Log("SDL_ConvertSurfaceFormat failed: %s\n", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_LockTexture(texture_.get(), nullptr, (void **)&texture_pixels,
|
||||
&converted_surface_->pitch);
|
||||
memcpy(texture_pixels, converted_surface_->pixels,
|
||||
converted_surface_->h * converted_surface_->pitch);
|
||||
SDL_UnlockTexture(texture_.get());
|
||||
}
|
||||
|
||||
void Bitmap::UpdateTexture(SDL_Renderer *renderer) {
|
||||
SDL_Surface *converted_surface =
|
||||
SDL_ConvertSurfaceFormat(surface_.get(), SDL_PIXELFORMAT_ARGB8888, 0);
|
||||
if (converted_surface) {
|
||||
@@ -275,92 +324,32 @@ void Bitmap::CreateTexture(SDL_Renderer *renderer) {
|
||||
SDL_UnlockTexture(texture_.get());
|
||||
}
|
||||
|
||||
void Bitmap::UpdateTexture(SDL_Renderer *renderer, bool use_sdl_update) {
|
||||
SDL_Surface *converted_surface =
|
||||
SDL_ConvertSurfaceFormat(surface_.get(), SDL_PIXELFORMAT_ARGB8888, 0);
|
||||
if (converted_surface) {
|
||||
converted_surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
|
||||
converted_surface, SDL_Surface_Deleter());
|
||||
} else {
|
||||
SDL_Log("SDL_ConvertSurfaceFormat failed: %s\n", SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_LockTexture(texture_.get(), nullptr, (void **)&texture_pixels,
|
||||
&converted_surface_->pitch);
|
||||
if (use_sdl_update) {
|
||||
SDL_UpdateTexture(texture_.get(), nullptr, converted_surface_->pixels,
|
||||
converted_surface_->pitch);
|
||||
} else {
|
||||
memcpy(texture_pixels, converted_surface_->pixels,
|
||||
converted_surface_->h * converted_surface_->pitch);
|
||||
}
|
||||
SDL_UnlockTexture(texture_.get());
|
||||
}
|
||||
|
||||
void Bitmap::CreateTexture(std::shared_ptr<SDL_Renderer> renderer) {
|
||||
texture_ = std::shared_ptr<SDL_Texture>{
|
||||
SDL_CreateTextureFromSurface(renderer.get(), surface_.get()),
|
||||
SDL_Texture_Deleter{}};
|
||||
}
|
||||
|
||||
void Bitmap::UpdateTexture(std::shared_ptr<SDL_Renderer> renderer) {
|
||||
texture_ = std::shared_ptr<SDL_Texture>{
|
||||
SDL_CreateTextureFromSurface(renderer.get(), surface_.get()),
|
||||
SDL_Texture_Deleter{}};
|
||||
}
|
||||
|
||||
void Bitmap::SaveSurfaceToFile(std::string_view filename) {
|
||||
SDL_SaveBMP(surface_.get(), filename.data());
|
||||
}
|
||||
|
||||
void Bitmap::SetSurface(SDL_Surface *surface) {
|
||||
surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
|
||||
surface, SDL_Surface_Deleter());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Bitmap::GetPngData() {
|
||||
ConvertSurfaceToPNG(surface_.get(), png_data_);
|
||||
return png_data_;
|
||||
}
|
||||
|
||||
void Bitmap::LoadFromPngData(const std::vector<uint8_t> &png_data, int width,
|
||||
int height) {
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
SDL_Surface *surface = surface_.get();
|
||||
ConvertPngToSurface(png_data, &surface);
|
||||
surface_.reset(surface);
|
||||
}
|
||||
|
||||
// Convert SNESPalette to SDL_Palette for surface.
|
||||
absl::Status Bitmap::ApplyPalette(const SnesPalette &palette) {
|
||||
if (surface_ == nullptr) {
|
||||
return absl::FailedPreconditionError("Surface is null");
|
||||
return absl::FailedPreconditionError(
|
||||
"Surface is null. Palette not applied");
|
||||
}
|
||||
|
||||
if (surface_->format == nullptr || surface_->format->palette == nullptr) {
|
||||
return absl::FailedPreconditionError("Surface format or palette is null");
|
||||
return absl::FailedPreconditionError(
|
||||
"Surface format or palette is null. Palette not applied.");
|
||||
}
|
||||
|
||||
palette_ = palette;
|
||||
|
||||
SDL_Palette *sdlPalette = surface_->format->palette;
|
||||
if (sdlPalette == nullptr) {
|
||||
SDL_Palette *sdl_palette = surface_->format->palette;
|
||||
if (sdl_palette == nullptr) {
|
||||
return absl::InternalError("Failed to get SDL palette");
|
||||
}
|
||||
|
||||
SDL_UnlockSurface(surface_.get());
|
||||
|
||||
for (int i = 0; i < palette.size(); ++i) {
|
||||
for (size_t i = 0; i < palette.size(); ++i) {
|
||||
ASSIGN_OR_RETURN(gfx::SnesColor pal_color, palette.GetColor(i));
|
||||
sdlPalette->colors[i].r = pal_color.rgb().x;
|
||||
sdlPalette->colors[i].g = pal_color.rgb().y;
|
||||
sdlPalette->colors[i].b = pal_color.rgb().z;
|
||||
sdlPalette->colors[i].a = pal_color.rgb().w;
|
||||
sdl_palette->colors[i].r = pal_color.rgb().x;
|
||||
sdl_palette->colors[i].g = pal_color.rgb().y;
|
||||
sdl_palette->colors[i].b = pal_color.rgb().z;
|
||||
sdl_palette->colors[i].a = pal_color.rgb().w;
|
||||
}
|
||||
|
||||
SDL_LockSurface(surface_.get());
|
||||
|
||||
// SDL_RETURN_IF_ERROR()
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -369,7 +358,7 @@ absl::Status Bitmap::ApplyPaletteFromPaletteGroup(const SnesPalette &palette,
|
||||
auto start_index = palette_id * 8;
|
||||
palette_ = palette.sub_palette(start_index, start_index + 8);
|
||||
SDL_UnlockSurface(surface_.get());
|
||||
for (int i = 0; i < palette_.size(); ++i) {
|
||||
for (size_t i = 0; i < palette_.size(); ++i) {
|
||||
ASSIGN_OR_RETURN(auto pal_color, palette_.GetColor(i));
|
||||
if (pal_color.is_transparent()) {
|
||||
surface_->format->palette->colors[i].r = 0;
|
||||
@@ -384,11 +373,29 @@ absl::Status Bitmap::ApplyPaletteFromPaletteGroup(const SnesPalette &palette,
|
||||
}
|
||||
}
|
||||
SDL_LockSurface(surface_.get());
|
||||
// SDL_RETURN_IF_ERROR()
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Bitmap::ApplyPaletteWithTransparent(const SnesPalette &palette,
|
||||
int index, int length) {
|
||||
size_t index, int length) {
|
||||
if (index < 0 || index >= palette.size()) {
|
||||
return absl::InvalidArgumentError("Invalid palette index");
|
||||
}
|
||||
|
||||
if (length < 0 || length > palette.size()) {
|
||||
return absl::InvalidArgumentError("Invalid palette length");
|
||||
}
|
||||
|
||||
if (index + length > palette.size()) {
|
||||
return absl::InvalidArgumentError("Palette index + length exceeds size");
|
||||
}
|
||||
|
||||
if (surface_ == nullptr) {
|
||||
return absl::FailedPreconditionError(
|
||||
"Surface is null. Palette not applied");
|
||||
}
|
||||
|
||||
auto start_index = index * 7;
|
||||
palette_ = palette.sub_palette(start_index, start_index + 7);
|
||||
std::vector<ImVec4> colors;
|
||||
@@ -408,12 +415,13 @@ absl::Status Bitmap::ApplyPaletteWithTransparent(const SnesPalette &palette,
|
||||
i++;
|
||||
}
|
||||
SDL_LockSurface(surface_.get());
|
||||
// SDL_RETURN_IF_ERROR()
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void Bitmap::ApplyPalette(const std::vector<SDL_Color> &palette) {
|
||||
SDL_UnlockSurface(surface_.get());
|
||||
for (int i = 0; i < palette.size(); ++i) {
|
||||
for (size_t i = 0; i < palette.size(); ++i) {
|
||||
surface_->format->palette->colors[i].r = palette[i].r;
|
||||
surface_->format->palette->colors[i].g = palette[i].g;
|
||||
surface_->format->palette->colors[i].b = palette[i].b;
|
||||
@@ -422,6 +430,57 @@ void Bitmap::ApplyPalette(const std::vector<SDL_Color> &palette) {
|
||||
SDL_LockSurface(surface_.get());
|
||||
}
|
||||
|
||||
void Bitmap::Get8x8Tile(int tile_index, int x, int y,
|
||||
std::vector<uint8_t> &tile_data,
|
||||
int &tile_data_offset) {
|
||||
int tile_offset = tile_index * (width_ * height_);
|
||||
int tile_x = (x * 8) % width_;
|
||||
int tile_y = (y * 8) % height_;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int row_offset = tile_offset + ((tile_y + i) * width_);
|
||||
for (int j = 0; j < 8; j++) {
|
||||
int pixel_offset = row_offset + (tile_x + j);
|
||||
int pixel_value = data_[pixel_offset];
|
||||
tile_data[tile_data_offset] = pixel_value;
|
||||
tile_data_offset++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::Get16x16Tile(int tile_x, int tile_y,
|
||||
std::vector<uint8_t> &tile_data,
|
||||
int &tile_data_offset) {
|
||||
for (int ty = 0; ty < 16; ty++) {
|
||||
for (int tx = 0; tx < 16; tx++) {
|
||||
// Calculate the pixel position in the bitmap
|
||||
int pixel_x = tile_x + tx;
|
||||
int pixel_y = tile_y + ty;
|
||||
int pixel_offset = (pixel_y * width_) + pixel_x;
|
||||
int pixel_value = data_[pixel_offset];
|
||||
|
||||
// Store the pixel value in the tile data
|
||||
tile_data[tile_data_offset++] = pixel_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::WriteColor(int position, const ImVec4 &color) {
|
||||
// Convert ImVec4 (RGBA) to SDL_Color (RGBA)
|
||||
SDL_Color sdl_color;
|
||||
sdl_color.r = static_cast<Uint8>(color.x * 255);
|
||||
sdl_color.g = static_cast<Uint8>(color.y * 255);
|
||||
sdl_color.b = static_cast<Uint8>(color.z * 255);
|
||||
sdl_color.a = static_cast<Uint8>(color.w * 255);
|
||||
|
||||
// Map SDL_Color to the nearest color index in the surface's palette
|
||||
Uint8 index =
|
||||
SDL_MapRGB(surface_->format, sdl_color.r, sdl_color.g, sdl_color.b);
|
||||
|
||||
// Write the color index to the pixel data
|
||||
pixel_data_[position] = index;
|
||||
modified_ = true;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
@@ -6,11 +6,9 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "app/core/constants.h"
|
||||
#include "app/core/utils/sdl_deleter.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
|
||||
namespace yaze {
|
||||
@@ -22,6 +20,10 @@ namespace app {
|
||||
*/
|
||||
namespace gfx {
|
||||
|
||||
// Same as SDL_PIXELFORMAT_INDEX8 for reference
|
||||
constexpr Uint32 SNES_PIXELFORMAT_INDEXED =
|
||||
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX8, 0, 0, 8, 1);
|
||||
|
||||
constexpr Uint32 SNES_PIXELFORMAT_2BPP = SDL_DEFINE_PIXELFORMAT(
|
||||
/*type=*/SDL_PIXELTYPE_INDEX8, /*order=*/0,
|
||||
/*layouts=*/0, /*bits=*/2, /*bytes=*/1);
|
||||
@@ -34,13 +36,14 @@ constexpr Uint32 SNES_PIXELFORMAT_8BPP = SDL_DEFINE_PIXELFORMAT(
|
||||
/*type=*/SDL_PIXELTYPE_INDEX8, /*order=*/0,
|
||||
/*layouts=*/0, /*bits=*/8, /*bytes=*/1);
|
||||
|
||||
// SDL_PIXELFORMAT_INDEX8 =
|
||||
// SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX8, 0, 0, 8, 1),
|
||||
|
||||
constexpr int kFormat2bppIndexed = 1;
|
||||
constexpr int kFormat4bppIndexed = 2;
|
||||
constexpr int kFormat8bppIndexed = 3;
|
||||
enum BitmapFormat {
|
||||
kIndexed = 0,
|
||||
k2bpp = 1,
|
||||
k4bpp = 2,
|
||||
k8bpp = 3,
|
||||
};
|
||||
|
||||
#if YAZE_LIB_PNG == 1
|
||||
/**
|
||||
* @brief Convert SDL_Surface to PNG image data.
|
||||
*/
|
||||
@@ -51,6 +54,7 @@ bool ConvertSurfaceToPNG(SDL_Surface *surface, std::vector<uint8_t> &buffer);
|
||||
*/
|
||||
void ConvertPngToSurface(const std::vector<uint8_t> &png_data,
|
||||
SDL_Surface **outSurface);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Represents a bitmap image.
|
||||
@@ -65,11 +69,11 @@ class Bitmap {
|
||||
Bitmap() = default;
|
||||
|
||||
Bitmap(int width, int height, int depth, int data_size);
|
||||
Bitmap(int width, int height, int depth, const Bytes &data)
|
||||
Bitmap(int width, int height, int depth, const std::vector<uint8_t> &data)
|
||||
: width_(width), height_(height), depth_(depth), data_(data) {
|
||||
Create(width, height, depth, data);
|
||||
}
|
||||
Bitmap(int width, int height, int depth, const Bytes &data,
|
||||
Bitmap(int width, int height, int depth, const std::vector<uint8_t> &data,
|
||||
const SnesPalette &palette)
|
||||
: width_(width),
|
||||
height_(height),
|
||||
@@ -82,11 +86,19 @@ class Bitmap {
|
||||
}
|
||||
}
|
||||
|
||||
#if YAZE_LIB_PNG == 1
|
||||
std::vector<uint8_t> GetPngData();
|
||||
#endif
|
||||
|
||||
void SaveSurfaceToFile(std::string_view filename);
|
||||
|
||||
/**
|
||||
* @brief Creates a bitmap object with the provided graphical data.
|
||||
*/
|
||||
void Create(int width, int height, int depth, const Bytes &data);
|
||||
void Create(int width, int height, int depth, int format, const Bytes &data);
|
||||
void Create(int width, int height, int depth,
|
||||
const std::vector<uint8_t> &data);
|
||||
void Create(int width, int height, int depth, int format,
|
||||
const std::vector<uint8_t> &data);
|
||||
|
||||
void Reformat(int format);
|
||||
|
||||
@@ -96,31 +108,29 @@ class Bitmap {
|
||||
* Converts the surface from a RGB to ARGB format.
|
||||
* Uses SDL_TEXTUREACCESS_STREAMING to allow for live updates.
|
||||
*/
|
||||
void CreateTexture(std::shared_ptr<SDL_Renderer> renderer);
|
||||
void CreateTexture(SDL_Renderer *renderer);
|
||||
|
||||
/**
|
||||
* @brief Updates the underlying SDL_Texture when it already exists.
|
||||
*/
|
||||
void UpdateTexture(std::shared_ptr<SDL_Renderer> renderer);
|
||||
void CreateTexture(SDL_Renderer *renderer);
|
||||
void UpdateTexture(SDL_Renderer *renderer, bool use_sdl_update = false);
|
||||
|
||||
void SaveSurfaceToFile(std::string_view filename);
|
||||
void SetSurface(SDL_Surface *surface);
|
||||
std::vector<uint8_t> GetPngData();
|
||||
void LoadFromPngData(const std::vector<uint8_t> &png_data, int width,
|
||||
int height);
|
||||
void UpdateTexture(SDL_Renderer *renderer);
|
||||
|
||||
/**
|
||||
* @brief Copy color data from the SnesPalette into the SDL_Palette
|
||||
*/
|
||||
absl::Status ApplyPalette(const SnesPalette &palette);
|
||||
absl::Status ApplyPaletteWithTransparent(const SnesPalette &palette,
|
||||
int index, int length = 7);
|
||||
size_t index, int length = 7);
|
||||
void ApplyPalette(const std::vector<SDL_Color> &palette);
|
||||
absl::Status ApplyPaletteFromPaletteGroup(const SnesPalette &palette,
|
||||
int palette_id);
|
||||
|
||||
void Get8x8Tile(int tile_index, int x, int y, std::vector<uint8_t> &tile_data,
|
||||
int &tile_data_offset);
|
||||
|
||||
void Get16x16Tile(int tile_x, int tile_y, std::vector<uint8_t> &tile_data,
|
||||
int &tile_data_offset);
|
||||
|
||||
void WriteToPixel(int position, uchar value) {
|
||||
if (pixel_data_ == nullptr) {
|
||||
pixel_data_ = data_.data();
|
||||
@@ -138,73 +148,7 @@ class Bitmap {
|
||||
modified_ = true;
|
||||
}
|
||||
|
||||
void Get8x8Tile(int tile_index, int x, int y, std::vector<uint8_t> &tile_data,
|
||||
int &tile_data_offset) {
|
||||
int tile_offset = tile_index * (width_ * height_);
|
||||
int tile_x = (x * 8) % width_;
|
||||
int tile_y = (y * 8) % height_;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int row_offset = tile_offset + ((tile_y + i) * width_);
|
||||
for (int j = 0; j < 8; j++) {
|
||||
int pixel_offset = row_offset + (tile_x + j);
|
||||
int pixel_value = data_[pixel_offset];
|
||||
tile_data[tile_data_offset] = pixel_value;
|
||||
tile_data_offset++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Get16x16Tile(int tile_index, int x, int y,
|
||||
std::vector<uint8_t> &tile_data, int &tile_data_offset) {
|
||||
int tile_offset = tile_index * (width_ * height_);
|
||||
int tile_x = x * 16;
|
||||
int tile_y = y * 16;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int row_offset = tile_offset + ((i / 8) * (width_ * 8));
|
||||
for (int j = 0; j < 16; j++) {
|
||||
int pixel_offset =
|
||||
row_offset + ((j / 8) * 8) + ((i % 8) * width_) + (j % 8);
|
||||
int pixel_value = data_[pixel_offset];
|
||||
tile_data[tile_data_offset] = pixel_value;
|
||||
tile_data_offset++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Get16x16Tile(int tile_x, int tile_y, std::vector<uint8_t> &tile_data,
|
||||
int &tile_data_offset) {
|
||||
// Assuming 'width_' and 'height_' are the dimensions of the bitmap
|
||||
// and 'data_' is the bitmap data.
|
||||
for (int ty = 0; ty < 16; ty++) {
|
||||
for (int tx = 0; tx < 16; tx++) {
|
||||
// Calculate the pixel position in the bitmap
|
||||
int pixel_x = tile_x + tx;
|
||||
int pixel_y = tile_y + ty;
|
||||
int pixel_offset = pixel_y * width_ + pixel_x;
|
||||
int pixel_value = data_[pixel_offset];
|
||||
|
||||
// Store the pixel value in the tile data
|
||||
tile_data[tile_data_offset++] = pixel_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteColor(int position, const ImVec4 &color) {
|
||||
// Convert ImVec4 (RGBA) to SDL_Color (RGBA)
|
||||
SDL_Color sdl_color;
|
||||
sdl_color.r = static_cast<Uint8>(color.x * 255);
|
||||
sdl_color.g = static_cast<Uint8>(color.y * 255);
|
||||
sdl_color.b = static_cast<Uint8>(color.z * 255);
|
||||
sdl_color.a = static_cast<Uint8>(color.w * 255);
|
||||
|
||||
// Map SDL_Color to the nearest color index in the surface's palette
|
||||
Uint8 index =
|
||||
SDL_MapRGB(surface_->format, sdl_color.r, sdl_color.g, sdl_color.b);
|
||||
|
||||
// Write the color index to the pixel data
|
||||
pixel_data_[position] = index;
|
||||
modified_ = true;
|
||||
}
|
||||
void WriteColor(int position, const ImVec4 &color);
|
||||
|
||||
void Cleanup() {
|
||||
active_ = false;
|
||||
@@ -212,7 +156,7 @@ class Bitmap {
|
||||
height_ = 0;
|
||||
depth_ = 0;
|
||||
data_size_ = 0;
|
||||
palette_.Clear();
|
||||
palette_.clear();
|
||||
}
|
||||
|
||||
auto sdl_palette() {
|
||||
@@ -236,45 +180,28 @@ class Bitmap {
|
||||
auto mutable_surface() { return surface_.get(); }
|
||||
auto converted_surface() const { return converted_surface_.get(); }
|
||||
auto mutable_converted_surface() { return converted_surface_.get(); }
|
||||
void set_data(const Bytes &data) { data_ = data; }
|
||||
|
||||
auto vector() const { return data_; }
|
||||
auto at(int i) const { return data_[i]; }
|
||||
auto texture() const { return texture_.get(); }
|
||||
auto modified() const { return modified_; }
|
||||
void set_modified(bool modified) { modified_ = modified; }
|
||||
auto is_active() const { return active_; }
|
||||
auto set_active(bool active) { active_ = active; }
|
||||
void set_active(bool active) { active_ = active; }
|
||||
void set_data(const std::vector<uint8_t> &data) { data_ = data; }
|
||||
void set_modified(bool modified) { modified_ = modified; }
|
||||
|
||||
private:
|
||||
struct SDL_Texture_Deleter {
|
||||
void operator()(SDL_Texture *p) const {
|
||||
if (p != nullptr) {
|
||||
SDL_DestroyTexture(p);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct SDL_Surface_Deleter {
|
||||
void operator()(SDL_Surface *p) const {
|
||||
if (p != nullptr) {
|
||||
SDL_FreeSurface(p);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
int depth_ = 0;
|
||||
int data_size_ = 0;
|
||||
|
||||
bool freed_ = false;
|
||||
bool active_ = false;
|
||||
bool modified_ = false;
|
||||
void *texture_pixels = nullptr;
|
||||
|
||||
uchar *pixel_data_;
|
||||
Bytes data_;
|
||||
uint8_t *pixel_data_ = nullptr;
|
||||
std::vector<uint8_t> data_;
|
||||
|
||||
std::vector<uint8_t> png_data_;
|
||||
|
||||
@@ -286,52 +213,8 @@ class Bitmap {
|
||||
|
||||
using BitmapTable = std::unordered_map<int, gfx::Bitmap>;
|
||||
|
||||
/**
|
||||
* @brief Hash map container of shared pointers to Bitmaps.
|
||||
*/
|
||||
class BitmapManager {
|
||||
private:
|
||||
std::unordered_map<int, gfx::Bitmap> bitmap_cache_;
|
||||
|
||||
public:
|
||||
void LoadBitmap(int id, const Bytes &data, int width, int height, int depth) {
|
||||
bitmap_cache_[id].Create(width, height, depth, data);
|
||||
}
|
||||
|
||||
gfx::Bitmap &operator[](int id) {
|
||||
auto it = bitmap_cache_.find(id);
|
||||
if (it != bitmap_cache_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return bitmap_cache_.begin()->second;
|
||||
}
|
||||
gfx::Bitmap &shared_bitmap(int id) {
|
||||
auto it = bitmap_cache_.find(id);
|
||||
if (it != bitmap_cache_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
throw std::runtime_error(
|
||||
absl::StrCat("Bitmap with id ", id, " not found."));
|
||||
}
|
||||
auto mutable_bitmap(int id) { return &bitmap_cache_[id]; }
|
||||
void clear_cache() { bitmap_cache_.clear(); }
|
||||
auto size() const { return bitmap_cache_.size(); }
|
||||
auto at(int id) const { return bitmap_cache_.at(id); }
|
||||
|
||||
using value_type = std::pair<const int, gfx::Bitmap>;
|
||||
using iterator = std::unordered_map<int, gfx::Bitmap>::iterator;
|
||||
using const_iterator = std::unordered_map<int, gfx::Bitmap>::const_iterator;
|
||||
|
||||
iterator begin() noexcept { return bitmap_cache_.begin(); }
|
||||
iterator end() noexcept { return bitmap_cache_.end(); }
|
||||
const_iterator begin() const noexcept { return bitmap_cache_.begin(); }
|
||||
const_iterator end() const noexcept { return bitmap_cache_.end(); }
|
||||
const_iterator cbegin() const noexcept { return bitmap_cache_.cbegin(); }
|
||||
const_iterator cend() const noexcept { return bitmap_cache_.cend(); }
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GFX_BITMAP_H
|
||||
#endif // YAZE_APP_GFX_BITMAP_H
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user