From 2f3ae744873dfefd639e636d29b906ec7185d47c Mon Sep 17 00:00:00 2001 From: Redcrafter Date: Fri, 24 May 2024 19:12:36 +0200 Subject: [PATCH] major update --- CMakeLists.txt | 2 + src/aes.cpp | 236 ++++++ src/aes.hpp | 284 +------- src/dos_parser.cpp | 280 ++++++++ src/dos_parser.hpp | 12 + src/glStuff.hpp | 3 +- src/main.cpp | 1396 +++++++++++++++++++++++++++++------- src/parsing.hpp | 527 -------------- src/shaders/light.fs | 4 +- src/shaders/light.vs | 4 +- src/structures/ambient.hpp | 17 + src/structures/asset.cpp | 33 + src/structures/asset.hpp | 47 ++ src/structures/entity.hpp | 252 +++++++ src/structures/map.hpp | 154 ++++ src/structures/tile.hpp | 60 ++ 16 files changed, 2259 insertions(+), 1052 deletions(-) create mode 100644 src/aes.cpp create mode 100644 src/dos_parser.cpp create mode 100644 src/dos_parser.hpp delete mode 100644 src/parsing.hpp create mode 100644 src/structures/ambient.hpp create mode 100644 src/structures/asset.cpp create mode 100644 src/structures/asset.hpp create mode 100644 src/structures/entity.hpp create mode 100644 src/structures/map.hpp create mode 100644 src/structures/tile.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5185be7..9d67293 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,10 +33,12 @@ source_group(TREE "${CMAKE_SOURCE_DIR}/src" FILES ${Common_sources}) add_executable(editor ${Common_sources} ${imgui_sources} "./lib/imgui/backends/imgui_impl_opengl3.cpp" "./lib/imgui/backends/imgui_impl_glfw.cpp" + "./lib/imgui/misc/cpp/imgui_stdlib.cpp" "./lib/glad/src/gl.c") target_include_directories(editor PRIVATE "lib/imgui" "lib/stb" "lib/glm" "lib/glad/include" glfw) target_link_libraries(editor PRIVATE glfw ${LIBS}) +set_target_properties(editor PROPERTIES WIN32_EXECUTABLE TRUE) add_custom_command(TARGET editor POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory diff --git a/src/aes.cpp b/src/aes.cpp new file mode 100644 index 0000000..a4da6d4 --- /dev/null +++ b/src/aes.cpp @@ -0,0 +1,236 @@ +#include "aes.hpp" + +#include +#include + +// Encryption: Forward Rijndael S-box +constexpr unsigned char s[256] = { + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 +}; + +// Used in KeyExpansion +constexpr unsigned char rcon[256] = { + 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, + 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, + 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, + 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, + 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, + 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, + 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, + 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, + 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, + 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, + 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, + 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, + 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, + 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, + 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d +}; + +constexpr auto expandKey(const std::array& key) { + std::array out; + + constexpr int v = s[114]; + + out[0] = key[0]; + out[4] = key[1]; + out[8] = key[2]; + out[12] = key[3]; + out[1] = key[4]; + out[5] = key[5]; + out[9] = key[6]; + out[13] = key[7]; + out[2] = key[8]; + out[6] = key[9]; + out[10] = key[10]; + out[14] = key[11]; + out[3] = key[12]; + out[7] = key[13]; + out[11] = key[14]; + out[15] = key[15]; + + uint8_t bVar9 = key[2]; + uint8_t bVar16 = key[4]; + uint8_t bVar12 = key[9]; + uint8_t bVar17 = key[10]; + uint8_t bVar10 = key[11]; + uint8_t bVar11 = key[12]; + uint8_t bVar13 = key[13]; + uint8_t bVar18 = key[14]; + uint8_t bVar14 = key[15]; + + // uint8_t* pbVar8 = out.data() + 31; + + for (size_t lVar15 = 0; lVar15 < 10; lVar15++) { + int local_41 = out[0 + lVar15 * 16]; + local_41 ^= rcon[lVar15 + 1] ^ s[bVar13]; + out[16 + lVar15 * 16] = local_41; + + int local_42 = out[4 + lVar15 * 16]; + local_42 ^= s[bVar18]; + out[20 + lVar15 * 16] = local_42; + + bVar9 ^= s[bVar14]; + out[24 + lVar15 * 16] = bVar9; + + int local_43 = out[12 + lVar15 * 16]; + local_43 ^= s[bVar11]; + out[28 + lVar15 * 16] = local_43; + + bVar16 ^= local_41; + out[17 + lVar15 * 16] = bVar16; + + int local_44 = out[5 + lVar15 * 16]; + local_44 ^= local_42; + out[21 + lVar15 * 16] = local_44; + + int local_45 = out[9 + lVar15 * 16]; + local_45 ^= bVar9; + out[25 + lVar15 * 16] = local_45; + + int local_46 = out[13 + lVar15 * 16]; + local_46 ^= local_43; + out[29 + lVar15 * 16] = local_46; + + int local_47 = out[2 + lVar15 * 16]; + local_47 ^= bVar16; + out[18 + lVar15 * 16] = local_47; + + bVar12 ^= local_44; + out[22 + lVar15 * 16] = bVar12; + + bVar17 ^= local_45; + out[26 + lVar15 * 16] = bVar17; + + bVar10 ^= local_46; + out[30 + lVar15 * 16] = bVar10; + + bVar11 ^= local_47; + out[19 + lVar15 * 16] = bVar11; + + bVar13 ^= bVar12; + out[23 + lVar15 * 16] = bVar13; + + bVar18 ^= bVar17; + out[27 + lVar15 * 16] = bVar18; + + bVar14 ^= bVar10; + out[31 + lVar15 * 16] = bVar14; + } + + return out; +} + +static_assert(expandKey({'G', 'o', 'o', 'd', 'L', 'U', 'c', 'K', 'M', 'y', 'F', 'r', 'i', 'E', 'n', 'd'}) == std::array{ + 0x47, 0x4c, 0x4d, 0x69, 0x6f, 0x55, 0x79, 0x45, 0x6f, 0x63, 0x46, 0x6e, 0x64, 0x4b, 0x72, 0x64, + 0x28, 0x64, 0x29, 0x40, 0xf0, 0xa5, 0xdc, 0x99, 0x2c, 0x4f, 0x09, 0x67, 0x9d, 0xd6, 0xa4, 0xc0, + 0xc4, 0xa0, 0x89, 0xc9, 0x75, 0xd0, 0x0c, 0x95, 0x96, 0xd9, 0xd0, 0xb7, 0x94, 0x42, 0xe6, 0x26, + 0xea, 0x4a, 0xc3, 0x0a, 0xdc, 0x0c, 0x00, 0x95, 0x61, 0xb8, 0x68, 0xdf, 0x49, 0x0b, 0xed, 0xcb, + 0xc8, 0x82, 0x41, 0x4b, 0x42, 0x4e, 0x4e, 0xdb, 0x7e, 0xc6, 0xae, 0x71, 0x2e, 0x25, 0xc8, 0x03, + 0x61, 0xe3, 0xa2, 0xe9, 0xe1, 0xaf, 0xe1, 0x3a, 0x05, 0xc3, 0x6d, 0x1c, 0x9d, 0xb8, 0x70, 0x73, + 0xc1, 0x22, 0x80, 0x69, 0x7d, 0xd2, 0x33, 0x09, 0x8a, 0x49, 0x24, 0x38, 0x83, 0x3b, 0x4b, 0x38, + 0x80, 0xa2, 0x22, 0x4b, 0x7a, 0xa8, 0x9b, 0x92, 0x8d, 0xc4, 0xe0, 0xd8, 0x7a, 0x41, 0x0a, 0x32, + 0x4f, 0xed, 0xcf, 0x84, 0x1b, 0xb3, 0x28, 0xba, 0xae, 0x6a, 0x8a, 0x52, 0xc9, 0x88, 0x82, 0xb0, + 0xa0, 0x4d, 0x82, 0x06, 0x1b, 0xa8, 0x80, 0x3a, 0x49, 0x23, 0xa9, 0xfb, 0x96, 0x1e, 0x9c, 0x2c, + 0x16, 0x5b, 0xd9, 0xdf, 0x14, 0xbc, 0x3c, 0x06, 0x38, 0x1b, 0xb2, 0x49, 0xf9, 0xe7, 0x7b, 0x57 +}); + +#ifdef _MSC_VER +inline __m128i operator^(__m128i a, __m128i b) { + return _mm_xor_si128(a, b); +} +#endif + +inline bool eq(__m128i a, __m128i b) { + auto v = a ^ b; + return _mm_test_all_zeros(v, v); +} + +std::vector encrypt(std::span data, const std::array& key) { + auto expandedKey1 = expandKey(key); + auto _key = (__m128i*)expandedKey1.data(); + + auto length = data.size(); + auto data_ = (__m128i*)data.data(); + + std::vector out((length & (~0xF)) + ((length & 0xF) != 0) * 16 + 0x20); + auto out_ptr = (__m128i*)out.data(); + + constexpr std::array iv = { 0xCE, 0x8F, 0xD3, 0x21, 0xD8, 0xFC, 0x28, 0x8A, 0xA5, 0xBC, 0xAE, 0xE3, 0x13, 0x47, 0xCC, 0x8D }; + // todo: random generate iv + *out_ptr = *(__m128i*)iv.data(); + out_ptr++; + + auto step = [&](__m128i val) { + val = val ^ _key[0]; + for (int i = 1; i < 10; i++) { + val = _mm_aesenc_si128(val, _key[i]); + } + *out_ptr = _mm_aesenclast_si128(val, _key[10]); + out_ptr++; + }; + + step(_key[0] ^ *(out_ptr - 1)); + + for (size_t i = 0; i < (length >> 4); i++) { + step(data_[i] ^ *(out_ptr - 1)); + } + if ((length & 0xF) != 0) { + std::array buf{0}; + for (int i = 0; i < (length & 0xF); ++i) { + buf[i] = data[(length & (~0xF)) + i]; + } + step(*(__m128i*)buf.data() ^ *(out_ptr - 1)); + } + + return out; +} + +bool decrypt(std::span data, const std::array& key, std::vector& out) { + auto expandedKey1 = expandKey(key); + auto _key = (__m128i*)expandedKey1.data(); + + __m128i inv_key[9]; + for (int i = 0; i < 9; ++i) { + inv_key[i] = _mm_aesimc_si128(_key[i + 1]); + } + + auto step = [&](__m128i val) { + val = val ^ _key[10]; + for (int i = 9; i > 0; i--) { + val = _mm_aesdec_si128(val, inv_key[i - 1]); + } + return _mm_aesdeclast_si128(val, _key[0]); + }; + + auto data_ = (__m128i*)data.data(); + // first 16 bytes of decrypted data should be key again + if (!eq((step(data_[1]) ^ data_[0]), _key[0])) + return false; + + out.resize(data.size() - 0x20); + auto out_ptr = (__m128i*)out.data(); + + auto len = ((data.size() & 0xf) != 0) + ((data.size() - 0x10) >> 4); + for (int i = 1; i < len; i += 1) { + out_ptr[i - 1] = step(data_[i + 1]) ^ data_[i]; + } + + return true; +} diff --git a/src/aes.hpp b/src/aes.hpp index 6ff7230..865115a 100644 --- a/src/aes.hpp +++ b/src/aes.hpp @@ -1,283 +1,9 @@ #pragma once -#include -#include -#include +#include +#include +#include -// Encryption: Forward Rijndael S-box -constexpr unsigned char s[256] = { - 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, - 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, - 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, - 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, - 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, - 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, - 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, - 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, - 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, - 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, - 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, - 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, - 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, - 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, - 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, - 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 -}; +std::vector encrypt(std::span data, const std::array& key); -// Used in KeyExpansion -constexpr unsigned char rcon[256] = { - 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, - 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, - 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, - 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, - 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, - 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, - 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, - 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, - 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, - 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, - 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, - 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, - 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, - 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, - 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, - 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d -}; - -constexpr auto expandKey(const std::array& key) { - std::array out; - - constexpr int v = s[114]; - - out[0] = key[0]; - out[4] = key[1]; - out[8] = key[2]; - out[12] = key[3]; - out[1] = key[4]; - out[5] = key[5]; - out[9] = key[6]; - out[13] = key[7]; - out[2] = key[8]; - out[6] = key[9]; - out[10] = key[10]; - out[14] = key[11]; - out[3] = key[12]; - out[7] = key[13]; - out[11] = key[14]; - out[15] = key[15]; - - uint8_t bVar9 = key[2]; - uint8_t bVar16 = key[4]; - uint8_t bVar12 = key[9]; - uint8_t bVar17 = key[10]; - uint8_t bVar10 = key[11]; - uint8_t bVar11 = key[12]; - uint8_t bVar13 = key[13]; - uint8_t bVar18 = key[14]; - uint8_t bVar14 = key[15]; - - // uint8_t* pbVar8 = out.data() + 31; - - for (size_t lVar15 = 0; lVar15 < 10; lVar15++) { - int local_41 = out[0 + lVar15 * 16]; - local_41 ^= rcon[lVar15 + 1] ^ s[bVar13]; - out[16 + lVar15 * 16] = local_41; - - int local_42 = out[4 + lVar15 * 16]; - local_42 ^= s[bVar18]; - out[20 + lVar15 * 16] = local_42; - - bVar9 ^= s[bVar14]; - out[24 + lVar15 * 16] = bVar9; - - int local_43 = out[12 + lVar15 * 16]; - local_43 ^= s[bVar11]; - out[28 + lVar15 * 16] = local_43; - - bVar16 ^= local_41; - out[17 + lVar15 * 16] = bVar16; - - int local_44 = out[5 + lVar15 * 16]; - local_44 ^= local_42; - out[21 + lVar15 * 16] = local_44; - - int local_45 = out[9 + lVar15 * 16]; - local_45 ^= bVar9; - out[25 + lVar15 * 16] = local_45; - - int local_46 = out[13 + lVar15 * 16]; - local_46 ^= local_43; - out[29 + lVar15 * 16] = local_46; - - int local_47 = out[2 + lVar15 * 16]; - local_47 ^= bVar16; - out[18 + lVar15 * 16] = local_47; - - bVar12 ^= local_44; - out[22 + lVar15 * 16] = bVar12; - - bVar17 ^= local_45; - out[26 + lVar15 * 16] = bVar17; - - bVar10 ^= local_46; - out[30 + lVar15 * 16] = bVar10; - - bVar11 ^= local_47; - out[19 + lVar15 * 16] = bVar11; - - bVar13 ^= bVar12; - out[23 + lVar15 * 16] = bVar13; - - bVar18 ^= bVar17; - out[27 + lVar15 * 16] = bVar18; - - bVar14 ^= bVar10; - out[31 + lVar15 * 16] = bVar14; - } - - return out; -} - -#if false // normal aes key exapsnion from https://github.com/ceceww/aes/blob/master/structures.h -// Auxiliary function for KeyExpansion -void KeyExpansionCore(unsigned char* in, unsigned char i) { - // Rotate left by one byte: shift left - unsigned char t = in[0]; - in[0] = in[1]; - in[1] = in[2]; - in[2] = in[3]; - in[3] = t; - - // S-box 4 bytes - in[0] = s[in[0]]; - in[1] = s[in[1]]; - in[2] = s[in[2]]; - in[3] = s[in[3]]; - - // RCon - in[0] ^= rcon[i]; -} - -void KeyExpansion(const char* inputKey, std::array& expandedKeys) { - // The first 128 bits are the original key - for (int i = 0; i < 16; i++) { - expandedKeys[i] = inputKey[i]; - } - - int bytesGenerated = 16; // Bytes we've generated so far - int rconIteration = 1; // Keeps track of rcon value - unsigned char tmpCore[4]; // Temp storage for core - - while (bytesGenerated < 176) { - for (int i = 0; i < 4; i++) { - tmpCore[i] = expandedKeys[i + bytesGenerated - 4]; - } - - // Perform the core once for each 16 byte key - if (bytesGenerated % 16 == 0) { - KeyExpansionCore(tmpCore, rconIteration++); - } - - for (unsigned char a = 0; a < 4; a++) { - expandedKeys[bytesGenerated] = expandedKeys[bytesGenerated - 16] ^ tmpCore[a]; - bytesGenerated++; - } - } -} -#endif - -static_assert(expandKey({'G', 'o', 'o', 'd', 'L', 'U', 'c', 'K', 'M', 'y', 'F', 'r', 'i', 'E', 'n', 'd'}) == std::array{ - 0x47, 0x4c, 0x4d, 0x69, 0x6f, 0x55, 0x79, 0x45, 0x6f, 0x63, 0x46, 0x6e, 0x64, 0x4b, 0x72, 0x64, - 0x28, 0x64, 0x29, 0x40, 0xf0, 0xa5, 0xdc, 0x99, 0x2c, 0x4f, 0x09, 0x67, 0x9d, 0xd6, 0xa4, 0xc0, - 0xc4, 0xa0, 0x89, 0xc9, 0x75, 0xd0, 0x0c, 0x95, 0x96, 0xd9, 0xd0, 0xb7, 0x94, 0x42, 0xe6, 0x26, - 0xea, 0x4a, 0xc3, 0x0a, 0xdc, 0x0c, 0x00, 0x95, 0x61, 0xb8, 0x68, 0xdf, 0x49, 0x0b, 0xed, 0xcb, - 0xc8, 0x82, 0x41, 0x4b, 0x42, 0x4e, 0x4e, 0xdb, 0x7e, 0xc6, 0xae, 0x71, 0x2e, 0x25, 0xc8, 0x03, - 0x61, 0xe3, 0xa2, 0xe9, 0xe1, 0xaf, 0xe1, 0x3a, 0x05, 0xc3, 0x6d, 0x1c, 0x9d, 0xb8, 0x70, 0x73, - 0xc1, 0x22, 0x80, 0x69, 0x7d, 0xd2, 0x33, 0x09, 0x8a, 0x49, 0x24, 0x38, 0x83, 0x3b, 0x4b, 0x38, - 0x80, 0xa2, 0x22, 0x4b, 0x7a, 0xa8, 0x9b, 0x92, 0x8d, 0xc4, 0xe0, 0xd8, 0x7a, 0x41, 0x0a, 0x32, - 0x4f, 0xed, 0xcf, 0x84, 0x1b, 0xb3, 0x28, 0xba, 0xae, 0x6a, 0x8a, 0x52, 0xc9, 0x88, 0x82, 0xb0, - 0xa0, 0x4d, 0x82, 0x06, 0x1b, 0xa8, 0x80, 0x3a, 0x49, 0x23, 0xa9, 0xfb, 0x96, 0x1e, 0x9c, 0x2c, - 0x16, 0x5b, 0xd9, 0xdf, 0x14, 0xbc, 0x3c, 0x06, 0x38, 0x1b, 0xb2, 0x49, 0xf9, 0xe7, 0x7b, 0x57 -}); - -auto aesimc(__m128i p) { - return _mm_aesimc_si128(p); -} - -auto aesdec(__m128i a, __m128i b) { - return _mm_aesdec_si128(a, b); -} - -auto aesdeclast(__m128i& a, __m128i& b) { - return _mm_aesdeclast_si128(a, b); -} - -#ifdef _MSC_VER -__m128i operator^(__m128i a, __m128i b) { - return _mm_xor_si128(a, b); -} -#endif - -std::optional> decrypt(const char* data, int length, const std::array& key) { - auto expandedKey1 = expandKey(key); - auto local_1c8 = (__m128i*)expandedKey1.data(); - - auto auVar9 = aesimc(local_1c8[1]); - auto auVar10 = aesimc(local_1c8[2]); - auto auVar14 = aesimc(local_1c8[3]); - auto auVar15 = aesimc(local_1c8[4]); - auto auVar16 = aesimc(local_1c8[5]); - auto auVar17 = aesimc(local_1c8[6]); - auto auVar18 = aesimc(local_1c8[7]); - auto auVar11 = aesimc(local_1c8[8]); - auto auVar12 = aesimc(local_1c8[9]); - - auto auVar13 = *(__m128i*)(data + 0x10); - auto auVar7 = aesdec(auVar13 ^ local_1c8[10], auVar12); - auVar7 = aesdec(auVar7, auVar11); - auVar7 = aesdec(auVar7, auVar18); - auVar7 = aesdec(auVar7, auVar17); - auVar7 = aesdec(auVar7, auVar16); - auVar7 = aesdec(auVar7, auVar15); - auVar7 = aesdec(auVar7, auVar14); - auVar7 = aesdec(auVar7, auVar10); - auVar7 = aesdec(auVar7, auVar9); - auto auVar8 = aesdeclast(auVar7, local_1c8[0]); - auVar7 = *(__m128i*)data; - auto bVar6 = (auVar8 ^ auVar7) ^ local_1c8[0]; - - if (_mm_test_all_zeros(bVar6, bVar6)) { - std::vector out(length - 0x10); - - auto lVar2 = out.data(); - - auto uVar3 = ((length & 0xf) != 0) + ((length - 0x10) >> 4); - if (uVar3 != 0) { - auto lVar4 = 0; - while (true) { - auVar8 = aesdec(auVar13 ^ local_1c8[10], auVar12); - auVar8 = aesdec(auVar8, auVar11); - auVar8 = aesdec(auVar8, auVar18); - auVar8 = aesdec(auVar8, auVar17); - auVar8 = aesdec(auVar8, auVar16); - auVar8 = aesdec(auVar8, auVar15); - auVar8 = aesdec(auVar8, auVar14); - auVar8 = aesdec(auVar8, auVar10); - auVar8 = aesdec(auVar8, auVar9); - auVar8 = aesdeclast(auVar8, local_1c8[0]); - - *(__m128i*)(lVar2 + lVar4) = auVar8 ^ auVar7; - if (uVar3 * 0x10 + -0x10 == lVar4) break; - - auto lVar1 = lVar4 + 0x20; - lVar4 += 0x10; - auVar7 = auVar13; - auVar13 = *(__m128i*)(data + lVar1); - } - } - - return std::vector(out.begin() + 0x10, out.end()); - } - - return std::nullopt; -} +bool decrypt(std::span data, const std::array& key, std::vector& out); diff --git a/src/dos_parser.cpp b/src/dos_parser.cpp new file mode 100644 index 0000000..ba239ad --- /dev/null +++ b/src/dos_parser.cpp @@ -0,0 +1,280 @@ +#include "dos_parser.hpp" + +#include +#include +#include +#include + +// mostly adapted from the ImHex PE pattern made by WerWolv + +struct DOSHeader { + char signature[2]; + uint16_t extraPageSize; + uint16_t numberOfPages; + uint16_t relocations; + uint16_t headerSizeInParagraphs; + uint16_t minimumAllocatedParagraphs; + uint16_t maximumAllocatedParagraphs; + uint16_t initialSSValue; + uint16_t initialRelativeSPValue; + uint16_t checksum; + uint16_t initialRelativeIPValue; + uint16_t initialCSValue; + uint16_t relocationsTablePointer; + uint16_t overlayNumber; + uint16_t reservedWords[4]; + uint16_t oemIdentifier; + uint16_t oemInformation; + uint16_t otherReservedWords[10]; + uint32_t coffHeaderPointer; +}; +static_assert(sizeof(DOSHeader) == 64); + +enum class ArchitectureType : uint16_t { + Unknown = 0x00, + ALPHAAXPOld = 0x183, + ALPHAAXP = 0x184, + ALPHAAXP64Bit = 0x284, + AM33 = 0x1D3, + AMD64 = 0x8664, + ARM = 0x1C0, + ARM64 = 0xAA64, + ARMNT = 0x1C4, + CLRPureMSIL = 0xC0EE, + EBC = 0xEBC, + I386 = 0x14C, + I860 = 0x14D, + IA64 = 0x200, + LOONGARCH32 = 0x6232, + LOONGARCH64 = 0x6264, + M32R = 0x9041, + MIPS16 = 0x266, + MIPSFPU = 0x366, + MIPSFPU16 = 0x466, + MOTOROLA68000 = 0x268, + POWERPC = 0x1F0, + POWERPCFP = 0x1F1, + POWERPC64 = 0x1F2, + R3000 = 0x162, + R4000 = 0x166, + R10000 = 0x168, + RISCV32 = 0x5032, + RISCV64 = 0x5064, + RISCV128 = 0x5128, + SH3 = 0x1A2, + SH3DSP = 0x1A3, + SH4 = 0x1A6, + SH5 = 0x1A8, + THUMB = 0x1C2, + WCEMIPSV2 = 0x169 +}; + +struct Characteristics { + bool baseRelocationsStripped : 1; + bool executableImage : 1; + bool lineNumbersStripped : 1; + bool symbolsStripped : 1; + bool aggressivelyTrimWorkingSet : 1; + bool largeAddressAware : 1; + bool : 1; // padding + bool bytesReversedLo : 1; + bool machine32Bit : 1; + bool debugInfoStripped : 1; + bool removableRunFromSwap : 1; + bool netRunFromSwap : 1; + bool systemFile : 1; + bool dll : 1; + bool uniprocessorMachineOnly : 1; + bool bytesReversedHi : 1; +}; + +enum class PEFormat : uint16_t { + ROM = 0x107, + PE32 = 0x10B, + PE32Plus = 0x20B +}; + +enum class SubsystemType : uint16_t { + Unknown = 0x00, + Native = 0x01, + WindowsGUI = 0x02, + WindowsCUI = 0x03, + OS2CUI = 0x05, + POSIXCUI = 0x07, + Windows9xNative = 0x08, + WindowsCEGUI = 0x09, + EFIApplication = 0x0A, + EFIBootServiceDriver = 0x0B, + EFIRuntimeDriver = 0x0C, + EFIROM = 0x0D, + Xbox = 0x0E, + WindowsBootApplication = 0x10 +}; + +struct DLLCharacteristics { + bool callWhenLoaded : 1; + bool callWhenThreadTerminates : 1; + bool callWhenThreadStarts : 1; + bool callWhenExiting : 1; + bool padding : 1; + bool highEntropyVA : 1; + bool dynamicBase : 1; + bool forceIntegrity : 1; + bool nxCompatible : 1; + bool noIsolation : 1; + bool noSEH : 1; + bool doNotBind : 1; + bool appContainer : 1; + bool isWDMDriver : 1; + bool supportsControlFlowGuard : 1; + bool terminalServerAware : 1; +}; + +struct LoaderFlags { + bool prestartBreakpoint : 1; + bool postloadingDebugger : 1; + uint32_t padding : 30; +}; + +struct DataDirectory { + uint32_t rva; + uint32_t size; +}; + +struct OptionalHeader { + PEFormat magic; + uint8_t majorLinkerVersion; + uint8_t minorLinkerVersion; + uint32_t sizeOfCode; + uint32_t sizeOfInitializedData; + uint32_t sizeOfUninitializedData; + uint32_t addressOfEntryPoint; + uint32_t baseOfCode; + + // magic == PEFormat::PE32Plus + uint64_t imageBase; + + uint32_t virtualSectionAlignment; + uint32_t rawSectionAlignment; + uint16_t majorOperatingSystemVersion; + uint16_t minorOperatingSystemVersion; + uint16_t majorImageVersion; + uint16_t minorImageVersion; + uint16_t majorSubsystemVersion; + uint16_t minorSubsystemVersion; + uint32_t win32VersionValue; + uint32_t sizeOfImage; + uint32_t sizeOfHeaders; + uint32_t checksum; + SubsystemType subsystem; + DLLCharacteristics dllCharacteristics; + /* + if (magic == PEFormat::PE32Plus) { + u64 sizeOfStackReserve; + u64 sizeOfStackCommit; + u64 sizeOfHeapReserve; + u64 sizeOfHeapCommit; + } + else { + u32 sizeOfStackReserve; + u32 sizeOfStackCommit; + u32 sizeOfHeapReserve; + u32 sizeOfHeapCommit; + } + LoaderFlags loaderFlags; + u32 numberOfRVAsAndSizes [[hex::spec_name("numberOfRvaAndSizes")]]; + DataDirectory directories[numberOfRVAsAndSizes];*/ +}; + +struct COFFHeader { + char signature[4]; + ArchitectureType architecture; + uint16_t numberOfSections; + uint32_t timeDateStamp; + uint32_t pointerToSymbolTable; + uint32_t numberOfSymbols; + uint16_t sizeOfOptionalHeader; + Characteristics characteristics; + + // OptionalHeader optionalHeader; +}; + +struct SectionFlags { + bool : 3; + bool doNotPad : 1; + bool : 1; + bool containsCode : 1; + bool containsInitializedData : 1; + bool containsUninitializedData : 1; + bool linkOther : 1; + bool linkHasInformation : 1; + bool : 1; + bool linkRemove : 1; + bool linkHasCOMDAT : 1; + bool : 1; + bool resetSpeculativeExceptions : 1; + bool globalPointerRelocations : 1; + bool purgeable : 1; + bool is16Bit : 1; + bool locked : 1; + bool preloaded : 1; + bool dataAlignment : 4; + bool linkExtendedRelocations : 1; + bool discardable : 1; + bool notCached : 1; + bool notPageable : 1; + bool shared : 1; + bool executed : 1; + bool read : 1; + bool writtenOn : 1; +}; + +struct SectionHeader { + char name[8]; + uint32_t virtualSize; + uint32_t rva; + uint32_t sizeOfRawData; + uint32_t ptrRawData; + uint32_t ptrRelocations; + uint32_t ptrLineNumbers; + uint16_t numberOfRelocations; + uint16_t numberOfLineNumbers; + SectionFlags characteristics; +}; + +static std::vector readFile(const char* path) { + std::ifstream testFile(path, std::ios::binary); + std::vector fileContents((std::istreambuf_iterator(testFile)), std::istreambuf_iterator()); + return fileContents; +} + +SegmentData getSegmentOffsets(std::span data) { + auto ptr = (uint8_t*)data.data(); + + auto dos_header = (DOSHeader*)ptr; + auto coff_header_ptr = (COFFHeader*)(ptr + dos_header->coffHeaderPointer); + auto coff_optional_header = (OptionalHeader*)(ptr + dos_header->coffHeaderPointer + sizeof(COFFHeader)); + auto section_ptr = (SectionHeader*)(ptr + dos_header->coffHeaderPointer + sizeof(COFFHeader) + coff_header_ptr->sizeOfOptionalHeader); + // assert(coff_header_ptr->optionalHeader.magic == PEFormat::PE32Plus); + auto image_base = coff_optional_header->imageBase; + + std::span dat; + std::span rdata; + uint64_t rdata_offset = -1; + + for (size_t i = 0; i < coff_header_ptr->numberOfSections; i++) { + auto& section = section_ptr[i]; + if (std::strcmp(section.name, ".data") == 0) { + dat = std::span(ptr + section.ptrRawData, section.sizeOfRawData); + } else if (std::strcmp(section.name, ".rdata") == 0) { + rdata = std::span(ptr + section.ptrRawData, section.sizeOfRawData); + rdata_offset = section.rva + image_base; + } + } + + return { + dat, + rdata_offset, + rdata + }; +} diff --git a/src/dos_parser.hpp b/src/dos_parser.hpp new file mode 100644 index 0000000..a3f9df3 --- /dev/null +++ b/src/dos_parser.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +struct SegmentData { + std::span data; + uint64_t rdata_pointer_offset; + std::span rdata; +}; + +SegmentData getSegmentOffsets(std::span data); diff --git a/src/glStuff.hpp b/src/glStuff.hpp index 4059bee..4b26f57 100644 --- a/src/glStuff.hpp +++ b/src/glStuff.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -121,7 +122,7 @@ struct Texture { stbi_image_free(dat); } - void LoadSubImage(int layer, const std::vector& data) { + void LoadSubImage(int layer, std::span data) { int n; auto* dat = stbi_load_from_memory(data.data(), data.size(), &width, &height, &n, 4); if(dat == nullptr) { diff --git a/src/main.cpp b/src/main.cpp index b19a606..3553ad6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,9 +1,14 @@ -#include +#include +#include +#include #include +#include +#include +#include +#include -#include "glStuff.hpp" -#include -#include // Will drag system OpenGL headers +#include "glStuff.hpp" // has to be included before glfw +#include #include #include @@ -11,8 +16,16 @@ #include #include #include +#include +#include -#include "parsing.hpp" +#include "structures/asset.hpp" +#include "structures/entity.hpp" +#include "structures/map.hpp" +#include "structures/tile.hpp" +#include "dos_parser.hpp" + +GLFWwindow* window; float gScale = 1; // Camera matrix @@ -22,12 +35,47 @@ glm::mat4 view = glm::lookAt( glm::vec3(0, 1, 0) // Head is up ); glm::mat4 projection; +glm::mat4 MVP; + glm::vec2 mousePos = glm::vec2(-1); glm::vec2 screenSize; std::vector framebuffers; +std::unique_ptr VBO_fg; +std::unique_ptr VBO_bg; +std::unique_ptr VBO_bg_tex; +std::unique_ptr VBO_light; +std::unique_ptr VBO_selection; + +int selectedMap = 0; + +std::vector rawData; + +constexpr int mapIds[5] = { 300, 193, 52, 222, 157 }; +Map maps[5]{}; +std::unordered_map sprites; +std::vector uvs; + +std::unique_ptr atlas; +std::unique_ptr bg_tex; + +std::vector vertecies_fg; +std::vector vertecies_bg; +std::vector> vertecies_bg_tex; +std::vector> vertecies_light; +std::vector> selectionVerts; + +glm::vec4 bg_color{ 0.8, 0.8, 0.8, 1 }; +glm::vec4 fg_color{ 1, 1, 1, 1 }; +glm::vec4 bg_tex_color{ 0.5, 0.5, 0.5, 1 }; + +bool show_fg = true; +bool show_bg = true; +bool show_bg_tex = true; +bool do_lighting = false; + static void glfw_error_callback(int error, const char* description) { fprintf(stderr, "GLFW Error %d: %s\n", error, description); } @@ -55,6 +103,11 @@ static void cursor_position_callback(GLFWwindow* window, double xpos, double ypo } static void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { + ImGuiIO& io = ImGui::GetIO(); + if(io.WantCaptureMouse) { + return; + } + float scale = 1; if (yoffset > 0) { scale = 1.1; @@ -82,8 +135,8 @@ static void onResize(GLFWwindow* window, int width, int height) { } } -auto renderMap(const Map& map, int layer, std::map& sprites, std::vector& uvs) { - auto atlasSize = glm::vec2(1024, 2048); +auto renderMap(const Map& map, int layer) { + auto atlasSize = glm::vec2(atlas->width, atlas->height); std::vector vert; @@ -355,6 +408,18 @@ auto renderLights(const Map& map, std::vector& uvs) { return verts; } +static void updateRender() { + vertecies_fg = renderMap(maps[selectedMap], 0); + vertecies_bg = renderMap(maps[selectedMap], 1); + vertecies_bg_tex = renderBgs(maps[selectedMap]); + vertecies_light = renderLights(maps[selectedMap], uvs); + + VBO_fg->BufferData(vertecies_fg.data(), vertecies_fg.size() * sizeof(glm::vec4)); + VBO_bg->BufferData(vertecies_bg.data(), vertecies_bg.size() * sizeof(glm::vec4)); + VBO_bg_tex->BufferData(vertecies_bg_tex.data(), vertecies_bg_tex.size() * sizeof(float) * 5); + VBO_light->BufferData(vertecies_light.data(), vertecies_light.size() * sizeof(float) * 5); +} + // helpger function renders a quad over the entire screen with uv fron (0,0) to (1,1) // very useful for processing intermediate frambuffers static void RenderQuad() { @@ -386,8 +451,981 @@ static void RenderQuad() { glBindVertexArray(0); } +static void DrawLine(std::vector>& verts, glm::vec2 p1, glm::vec2 p2, glm::vec4 color, float thickness) { + auto d = glm::normalize(p2 - p1) * (thickness * 0.5f); + + verts.emplace_back(glm::vec2{ p1.x + d.y, p1.y - d.x }, color); // tl + verts.emplace_back(glm::vec2{ p2.x + d.y, p2.y - d.x }, color); // tr + verts.emplace_back(glm::vec2{ p1.x - d.y, p1.y + d.x }, color); // bl + + verts.emplace_back(glm::vec2{ p2.x - d.y, p2.y + d.x }, color); // br + verts.emplace_back(glm::vec2{ p1.x - d.y, p1.y + d.x }, color); // bl + verts.emplace_back(glm::vec2{ p2.x + d.y, p2.y - d.x }, color); // tr +} + +static void DrawRect(std::vector>& verts, glm::vec2 pos, glm::vec2 size, glm::vec4 color, float thickness) { + DrawLine(verts, pos, pos + glm::vec2(size.x, 0), color, thickness); + DrawLine(verts, pos + glm::vec2(size.x, 0), pos + size, color, thickness); + DrawLine(verts, pos + size, pos + glm::vec2(0, size.y), color, thickness); + DrawLine(verts, pos + glm::vec2(0, size.y), pos, color, thickness); +} + +static void DrawCube(std::vector>& verts, glm::vec2 p1, glm::vec2 size, glm::vec4 color) { + verts.emplace_back(p1, color); + verts.emplace_back(glm::vec2{ p1.x + size.x, p1.y }, color); + verts.emplace_back(glm::vec2{ p1.x, p1.y + size.y }, color); + + verts.emplace_back(glm::vec2{ p1.x + size.x, p1.y }, color); + verts.emplace_back(p1 + size, color); + verts.emplace_back(glm::vec2{ p1.x, p1.y + size.y }, color); +} + +static ImVec2 toImVec(const glm::vec2 vec) { + return ImVec2(vec.x, vec.y); +} + +class { + std::string currentPath = "C:/Program Files (x86)/Steam/steamapps/common/Animal Well/Animal Well.exe"; + // open has to be deferred because of imgui issue see https://github.com/ocornut/imgui/issues/331 + bool shouldOpen = false; + +public: + std::string error_msg; + + void draw() { + if(shouldOpen) { + ImGui::OpenPopup("Open File"); + shouldOpen = false; + } + + ImVec2 center = ImGui::GetMainViewport()->GetCenter(); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + + if(ImGui::BeginPopupModal("Open File", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::InputText("path", ¤tPath); + ImGui::Separator(); + + if(ImGui::Button("Open")) { + tryLoad(); + } + ImGui::SameLine(); + if(ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); + } + + if(!error_msg.empty()) { + ImGui::OpenPopup("Open File Error"); + } + if(ImGui::BeginPopupModal("Open File Error")) { + ImGui::Text("Failed to load file: %s", error_msg.c_str()); + if(ImGui::Button("Ok")) { + error_msg.clear(); + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + ImGui::EndPopup(); + } + } + void open() { + shouldOpen = true; + } + + void LoadAtlas(std::span data) { + int width, height, n; + auto* dat = stbi_load_from_memory(data.data(), data.size(), &width, &height, &n, 4); + if(dat == nullptr) { + throw std::runtime_error("invalid texture atlas format"); + } + + atlas = std::make_unique(); + // chroma key cyan and replace with alpha + + auto vptr = (uint32_t*)dat; + for(int i = 0; i < width * height; ++i) { + if(vptr[i] == 0xFFFFFF00) { + vptr[i] = 0; + } + } + + atlas->Bind(); + atlas->width = width; + atlas->height = height; + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, dat); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + stbi_image_free(dat); + } + + bool tryLoad() { + if(!std::filesystem::exists(currentPath)) { + error_msg = "File not found"; + return false; + } + rawData = readFile(currentPath.c_str()); + auto sections = getSegmentOffsets(rawData); + + assert(sections.data.size() >= sizeof(asset_entry) * 676); + auto assets = std::span((asset_entry*)sections.data.data(), 676); + + std::vector decryptBuffer; + + auto get_asset = [&](int id) { + assert(id >= 0 && id < 676); + auto& asset = assets[id]; + assert(sections.rdata.size() >= asset.ptr - sections.rdata_pointer_offset + asset.length); + auto dat = sections.rdata.subspan(asset.ptr - sections.rdata_pointer_offset, asset.length); + + if(tryDecrypt(asset, dat, decryptBuffer)) { + return std::span(decryptBuffer); + } + return dat; + }; + + LoadAtlas(get_asset(255)); + + bg_tex = std::make_unique(); + glBindTexture(GL_TEXTURE_2D_ARRAY, bg_tex->id); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGB, 320, 180, 19, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + bg_tex->LoadSubImage(1 - 1, get_asset(14)); + bg_tex->LoadSubImage(2 - 1, get_asset(22)); + bg_tex->LoadSubImage(3 - 1, get_asset(22)); + bg_tex->LoadSubImage(4 - 1, get_asset(19)); + bg_tex->LoadSubImage(5 - 1, get_asset(19)); + bg_tex->LoadSubImage(6 - 1, get_asset(15)); + bg_tex->LoadSubImage(7 - 1, get_asset(13)); + bg_tex->LoadSubImage(8 - 1, get_asset(13)); + bg_tex->LoadSubImage(9 - 1, get_asset(16)); + bg_tex->LoadSubImage(10 - 1, get_asset(17)); + bg_tex->LoadSubImage(11 - 1, get_asset(16)); + bg_tex->LoadSubImage(12 - 1, get_asset(26)); + bg_tex->LoadSubImage(13 - 1, get_asset(11)); + bg_tex->LoadSubImage(14 - 1, get_asset(12)); + bg_tex->LoadSubImage(15 - 1, get_asset(20)); + bg_tex->LoadSubImage(16 - 1, get_asset(18)); + bg_tex->LoadSubImage(17 - 1, get_asset(23)); + bg_tex->LoadSubImage(18 - 1, get_asset(24)); + bg_tex->LoadSubImage(19 - 1, get_asset(21)); + + for(size_t i = 0; i < 5; i++) { + maps[i] = Map(get_asset(mapIds[i])); + } + + for(auto el : spriteMapping) { + sprites[el.tile_id] = parse_sprite(get_asset(el.asset_id)); + } + + uvs = parse_uvs(get_asset(254)); + + updateRender(); + + ImGui::CloseCurrentPopup(); + return true; + } +} LoadFileDialog; + +static void DrawUvFlags(uv_data& uv) { + if(ImGui::BeginTable("uv_flags_table", 2)) { + uint32_t flags = uv.flags; + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Collides left", &flags, 1 << 0); // correct + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Hidden", &flags, 1 << 10); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Collides right", &flags, 1 << 1); // correct + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Blocks Light", &flags, 1 << 8); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Collides up", &flags, 1 << 2); // correct + ImGui::TableNextColumn(); ImGui::CheckboxFlags("obscures", &flags, 1 << 6); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Collides down", &flags, 1 << 3); // correct + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Contiguous", &flags, 1 << 7); // correct + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Not Placeable", &flags, 1 << 4); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Self Contiguous", &flags, 1 << 9); // correct + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Additive", &flags, 1 << 5); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Dirt", &flags, 1 << 11); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Has Normals", &flags, 1 << 12); // correct + ImGui::TableNextColumn(); ImGui::CheckboxFlags("UV Light", &flags, 1 << 13); // correct + + if(flags != uv.flags) { + uv.flags = flags; + updateRender(); + } + + ImGui::EndTable(); + } +} + +static void DrawSpriteWindow() { + if(!ImGui::Begin("Sprite Viewer")) { + ImGui::End(); + return; + } + + static int selectedSprite = 0; + static int selected_animation = 0; + static bool playing = false; + static int frame_step = 0; + static int selected_composition = 0; + + if(ImGui::InputInt("Id", &selectedSprite)) { + if(selectedSprite >= 158) { + selectedSprite = 0; + } + selected_animation = 0; + selected_composition = 0; + playing = false; + frame_step = 0; + } + + auto tile_id = spriteMapping[selectedSprite].tile_id; + auto& sprite = sprites[tile_id]; + auto& uv = uvs[tile_id]; + + ImGui::InputScalarN("Composite size", ImGuiDataType_U16, &sprite.composite_size, 2); + ImGui::InputScalar("Layer count", ImGuiDataType_U16, &sprite.layer_count); + ImGui::InputScalar("Composite count", ImGuiDataType_U16, &sprite.composition_count); + ImGui::InputScalar("Subsprite count", ImGuiDataType_U16, &sprite.subsprite_count); + ImGui::InputScalar("Animation count", ImGuiDataType_U16, &sprite.animation_count); + + ImGui::NewLine(); + ImGui::SliderInt("animation", &selected_animation, 0, sprite.animation_count - 1); + if(sprite.animation_count != 0) { + auto& anim = sprite.animations[selected_animation]; + ImGui::InputScalar("start", ImGuiDataType_U16, &anim.start); + ImGui::InputScalar("end", ImGuiDataType_U16, &anim.end); + ImGui::InputScalar("frame_delay", ImGuiDataType_U16, &anim.frame_delay); + + if(playing) { + frame_step++; + if(frame_step / 5 > anim.frame_delay) { + selected_composition++; + if(selected_composition > anim.end) { + selected_composition = anim.start; + } + frame_step = 0; + } + + if(ImGui::Button("Pause")) playing = false; + } else { + if(ImGui::Button("Play")) { + playing = true; + selected_composition = anim.start; + frame_step = 0; + } + } + } + ImGui::SliderInt("composition", &selected_composition, 0, sprite.composition_count - 1); + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + const ImVec2 p = ImGui::GetCursorScreenPos(); + auto pos = glm::vec2(p.x, p.y); + + // draw_list->AddCallback([](const ImDrawList* parent_list, const ImDrawCmd* cmd) { }, nullptr); + + auto atlasSize = glm::vec2(atlas->width, atlas->height); + for(int j = 0; j < sprite.layer_count; ++j) { + auto subsprite_id = sprite.compositions[selected_composition * sprite.layer_count + j]; + if(subsprite_id >= sprite.subsprite_count) + continue; + + auto& layer = sprite.layers[j]; + if(layer.is_normals1 || layer.is_normals2 || !layer.is_visible) continue; + + auto& subsprite = sprite.sub_sprites[subsprite_id]; + + auto aUv = glm::vec2(uv.pos + subsprite.atlas_pos); + auto size = glm::vec2(subsprite.size); + auto ap = pos + glm::vec2(subsprite.composite_pos) * 8.0f; + + draw_list->AddImage((ImTextureID)atlas->id.value, toImVec(ap), toImVec(ap + glm::vec2(subsprite.size) * 8.0f), toImVec(aUv / atlasSize), toImVec((aUv + size) / atlasSize)); + } + + // draw_list->AddCallback() + + ImGui::End(); +} + +static void DrawTileWindow() { + if(!ImGui::Begin("Tile Viewer")) { + ImGui::End(); + return; + } + + static int selectedTile = 0; + ImGui::InputInt("Id", &selectedTile); + if(selectedTile >= uvs.size()) { + selectedTile = 0; + } + + auto& uv = uvs[selectedTile]; + + // bool is_sprite = sprites.contains(selectedTile); + bool is_sprite = false; + + if(!is_sprite) { + auto pos = glm::vec2(uv.pos); + auto size = glm::vec2(uv.size); + auto atlas_size = glm::vec2(atlas->width, atlas->height); + + ImGui::Text("preview"); + ImGui::Image((ImTextureID)atlas->id.value, toImVec(size * 8.0f), toImVec(pos / atlas_size), toImVec((pos + size) / atlas_size)); + } else { + /* + ImGuiWindow* window = ImGui::GetCurrentWindow(); + + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size); + ImGui::ItemSize(bb); + if(!ImGui::ItemAdd(bb, 0)) + return; + + // Render + window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1); + */ + } + + DrawUvFlags(uv); + + ImGui::InputScalarN("UV", ImGuiDataType_U16, &uv.pos, 2); + ImGui::InputScalarN("UV Size", ImGuiDataType_U16, &uv.size, 2); + + ImGui::End(); +} + +static void DrawFindWindow() { + static int tile_id; + static std::vector results; + static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable; + static bool show_on_map; + + auto& map = maps[selectedMap]; + + if(ImGui::Begin("Search")) { + ImGui::InputInt("tile_id", &tile_id); + + if(ImGui::Button("Search")) { + results.clear(); + + for(auto& room : map.rooms) { + for(int y2 = 0; y2 < 22; y2++) { + for(int x2 = 0; x2 < 40; x2++) { + auto tile1 = room.tiles[0][y2][x2]; + if(tile1.tile_id == tile_id) { + results.emplace_back(room.x * 40 + x2, room.y * 22 + y2, 0); + } + + auto tile2 = room.tiles[1][y2][x2]; + if(tile2.tile_id == tile_id) { + results.emplace_back(room.x * 40 + x2, room.y * 22 + y2, 1); + } + } + } + } + } + if(ImGui::Button("Clear")) { + results.clear(); + } + ImGui::Checkbox("Highlight", &show_on_map); + + const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing(); + // When using ScrollX or ScrollY we need to specify a size for our table container! + // Otherwise by default the table will fit all available space, like a BeginChild() call. + ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8); + if(ImGui::BeginTable("search_results", 3, flags, outer_size)) { + ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible + ImGui::TableSetupColumn("x", ImGuiTableColumnFlags_None); + ImGui::TableSetupColumn("y", ImGuiTableColumnFlags_None); + ImGui::TableSetupColumn("layer", ImGuiTableColumnFlags_None); + ImGui::TableHeadersRow(); + + // Demonstrate using clipper for large vertical lists + ImGuiListClipper clipper; + clipper.Begin(results.size()); + while(clipper.Step()) { + for(int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++) { + ImGui::TableNextRow(); + + auto el = results[row]; + + ImGui::TableSetColumnIndex(0); + ImGui::Text("%i", el.x); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%i", el.y); + ImGui::TableSetColumnIndex(2); + ImGui::Text("%i", el.z); + } + } + ImGui::EndTable(); + } + } + ImGui::End(); + + if(show_on_map) { + for(auto result : results) { + auto tile = map.getTile(result.z, result.x, result.y); + if(!tile.has_value()) + continue; + + auto uv = uvs[tile->tile_id]; + + DrawRect(selectionVerts, glm::vec2(result.x * 8, result.y * 8), glm::vec2(uv.size), {1, 0, 0, 0.8f}, 8 / gScale); + } + } +} + +static void DumpAssets() { + std::filesystem::create_directory("assets"); + + auto sections = getSegmentOffsets(rawData); + auto assets = std::span((asset_entry*)sections.data.data(), 676); + + std::vector decrypted; + + for(int i = 0; i < assets.size(); ++i) { + auto& item = assets[i]; + + std::string ext = ".bin"; + switch(item.type) { + case AssetType::Text: + ext = ".txt"; + break; + case AssetType::MapData: + case AssetType::Encrypted_MapData: + ext = ".map"; + break; + case AssetType::Png: + case AssetType::Encrypted_Png: + ext = ".png"; + break; + case AssetType::Ogg: + case AssetType::Encrypted_Ogg: + ext = ".ogg"; + break; + case AssetType::SpriteData: + ext = ".sprite"; + break; + case AssetType::Shader: + ext = ".shader"; + break; + case AssetType::Font: + ext = ".font"; + break; + case AssetType::Encrypted_XPS: + ext = ".xps"; + break; + } + + auto dat = sections.rdata.subspan(item.ptr - sections.rdata_pointer_offset, item.length); + + std::ofstream file("assets/" + std::to_string(i) + ext, std::ios::binary); + if(tryDecrypt(item, dat, decrypted)) { + file.write((char*)decrypted.data(), decrypted.size()); + } else { + file.write((char*)dat.data(), dat.size()); + } + } +} + +static void randomize() { + std::vector items; + std::vector locations; + + std::set target; + + // 16 = save_point + // 39 = telephone + target.insert( 40); // = key + target.insert( 41); // = match + // 90 = egg + target.insert(109); // = lamp + target.insert(149); // = stamps + // 161 = cheaters ring + target.insert(162); // = b. wand + target.insert(169); // = flute + target.insert(214); // = map + // 231 = deathless figure + // 284 = disc // can't be randomized + target.insert(323); // = uv light + target.insert(334); // = yoyo + target.insert(382); // = mock disc + // 383 = firecracker + target.insert(417); // = spring + target.insert(442); // = pencil + target.insert(466); // = remote + target.insert(469); // = S.medal + // 550.? = bunny + target.insert(611); // = house key + target.insert(617); // = office key + // 627.0 = seahorse flame + // 627.1 = cat flame + // 627.1 = lizard flame + // 627.3 = ostrich flame + target.insert(634); // = top + target.insert(637); // = b. ball + target.insert(643); // = wheel + target.insert(679); // = E.medal + target.insert(708); // = bb. wand + target.insert(711); // = 65th egg + target.insert(780); // = f.pack + + auto& map = maps[0]; + + for(auto& room : map.rooms) { + for(int y2 = 0; y2 < 22; y2++) { + for(int x2 = 0; x2 < 40; x2++) { + auto& tile = room.tiles[0][y2][x2]; + + if(target.contains(tile.tile_id)) { + items.push_back(tile); + locations.emplace_back(room.x * 40 + x2, room.y * 22 + y2); + } + } + } + } + + std::random_device rd; + std::mt19937 g(rd()); + std::ranges::shuffle(locations, g); + + for(auto item : items) { + auto loc = locations.back(); + locations.pop_back(); + + map.setTile(0, loc.x, loc.y, item); + } + + updateRender(); +} + +static void export_exe(bool patch_renderdoc) { + auto out = rawData; + + auto sections = getSegmentOffsets(out); + auto assets = std::span((asset_entry*)sections.data.data(), 676); + + auto replaceAsset = [&](const std::vector& data, int id) { + auto& asset = assets[id]; + auto ptr = sections.rdata.subspan(asset.ptr - sections.rdata_pointer_offset, asset.length); + + if(((uint8_t)asset.type & 192) == 64) { + int key = 0; // 193, 212, 255, 300 + if(id == 30 || id == 52) key = 1; + else if(id == 222 || id == 277 || id == 377) key = 2; + + auto enc = encrypt(data, key); + + assert(enc.size() == asset.length); + // asset.length = dat.size(); + std::memcpy(ptr.data(), enc.data(), enc.size()); + } else { + assert(data.size() == asset.length); + // asset.length = data.size(); + std::memcpy(ptr.data(), data.data(), data.size()); + } + }; + + for(size_t i = 0; i < 5; i++) { + replaceAsset(maps[i].save(), mapIds[i]); + } + replaceAsset(save_uvs(uvs), 254); + + /*if(patch_steam) { + // patch steam restart + out[0xEFE6] = 0x48; // MOV AL, 0 + out[0xEFE7] = 0xc6; + out[0xEFE8] = 0xc0; + out[0xEFE9] = 0x00; + + out[0xEFEA] = 0x48; // NOP + out[0xEFEB] = 0x90; + }*/ + + if(patch_renderdoc) { + const char renderDocPattern[14] = "renderdoc.dll"; + auto res = std::search(out.begin(), out.end(), std::begin(renderDocPattern), std::end(renderDocPattern)); + + if(res != out.end()) { + *res = 'l'; // replace first letter with 'l' + } + } + + std::ofstream file("Animal Well.exe", std::ios::binary); + file.write(out.data(), out.size()); +} + +// Tips: Use with ImGuiDockNodeFlags_PassthruCentralNode! +// The limitation with this call is that your window won't have a menu bar. +// Even though we could pass window flags, it would also require the user to be able to call BeginMenuBar() somehow meaning we can't Begin/End in a single function. +// But you can also use BeginMainMenuBar(). If you really want a menu bar inside the same window as the one hosting the dockspace, you will need to copy this code somewhere and tweak it. +ImGuiID DockSpaceOverViewport() { + auto viewport = ImGui::GetMainViewport(); + + ImGui::SetNextWindowPos(viewport->WorkPos); + ImGui::SetNextWindowSize(viewport->WorkSize); + ImGui::SetNextWindowViewport(viewport->ID); + + ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_PassthruCentralNode; + + ImGuiWindowFlags host_window_flags = 0; + host_window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking; + host_window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_MenuBar; + if(dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) + host_window_flags |= ImGuiWindowFlags_NoBackground; + + char label[32]; + ImFormatString(label, IM_ARRAYSIZE(label), "DockSpaceViewport_%08X", viewport->ID); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + ImGui::Begin(label, NULL, host_window_flags); + ImGui::PopStyleVar(3); + + if(ImGui::BeginMenuBar()) { + if(ImGui::BeginMenu("File")) { + if(ImGui::MenuItem("Load")) { + LoadFileDialog.open(); + } + if(ImGui::MenuItem("Dump assets")) { + DumpAssets(); + } + /*if(ImGui::MenuItem("Export assets")) { + + }*/ + ImGui::Separator(); + static bool patch_renderdoc = false; + ImGui::Checkbox("patch renderdoc", &patch_renderdoc); + if(ImGui::MenuItem("Export exe")) { + export_exe(patch_renderdoc); + } + ImGui::EndMenu(); + } + + if(ImGui::BeginMenu("Display")) { + static const char* mapNames[5] = { "Overworld", "Space", "Bunny temple", "Time Capsule", "CE temple" }; + if(ImGui::Combo("Map", &selectedMap, mapNames, 5)) { + updateRender(); + } + + ImGui::Checkbox("Foreground Tiles", &show_fg); + ImGui::ColorEdit4("fg tile color", &fg_color.r); + ImGui::Checkbox("Background Tiles", &show_bg); + ImGui::ColorEdit4("bg tile color", &bg_color.r); + ImGui::Checkbox("Background Texture", &show_bg_tex); + ImGui::ColorEdit4("bg Texture color", &bg_tex_color.r); + ImGui::Checkbox("Apply lighting", &do_lighting); + + ImGui::EndMenu(); + } + + if(ImGui::BeginMenu("Tools")) { + if(ImGui::MenuItem("Randomize items")) { + randomize(); + } + if(ImGui::MenuItem("Make everything transparent")) { + for(auto& uv : uvs) { + uv.blocks_light = false; + } + } + + ImGui::EndMenu(); + } + + ImGui::EndMenuBar(); + } + + ImGuiID dockspace_id = ImGui::GetID("DockSpace"); + ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, nullptr); + + static auto first_time = true; + if(first_time) { + first_time = false; + + ImGui::DockBuilderRemoveNode(dockspace_id); // clear any previous layout + ImGui::DockBuilderAddNode(dockspace_id, dockspace_flags | ImGuiDockNodeFlags_DockSpace); + ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size); + + auto right = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, 0.3f, nullptr, &dockspace_id); + + // we now dock our windows into the docking node we made above + ImGui::DockBuilderDockWindow("Sprite Viewer", right); + ImGui::DockBuilderDockWindow("Tile Viewer", right); + ImGui::DockBuilderDockWindow("Search", right); + ImGui::DockBuilderDockWindow("Properties", right); + ImGui::DockBuilderFinish(dockspace_id); + } + + ImGui::End(); + + return dockspace_id; +} + +static void HelpMarker(const char* desc) { + ImGui::TextDisabled("(?)"); + if(ImGui::BeginItemTooltip()) { + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(desc); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } +} + +static void DrawPreviewWindow() { + ImGui::Begin("Properties"); + auto& io = ImGui::GetIO(); + // ImGui::Text("fps %f", ImGui::GetIO().Framerate); + + static const char* modes[] = { "Select", "Place" }; + static int mode = 0; + ImGui::Combo("Mode", &mode, modes, sizeof(modes) / sizeof(char*)); + + auto mp = glm::vec4(((mousePos - screenSize / 2.0f) / screenSize) * 2.0f, 0, 1); + mp.y = -mp.y; + auto mouse_world_pos = glm::ivec2(glm::inverse(MVP) * mp) / 8; + const auto room_size = glm::ivec2(40, 22); + + if(mode == 0) { + ImGui::SameLine(); + HelpMarker("Right click to select a tile.\nEsc to unselect."); + static glm::ivec2 selectedPos = glm::vec2(-1, -1); + if(!io.WantCaptureMouse && glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT)) { + selectedPos = mouse_world_pos; + } + if(!io.WantCaptureKeyboard && glfwGetKey(window, GLFW_KEY_ESCAPE)) { + selectedPos = glm::ivec2(-1, -1); + } + + glm::ivec2 asdasd; + if(selectedPos == glm::ivec2(-1, -1)) { + if(io.WantCaptureMouse) { + asdasd = glm::ivec2(-1, -1); + } else { + asdasd = mouse_world_pos; + } + } else { + asdasd = selectedPos; + } + + auto room_pos = asdasd / room_size; + auto room = maps[selectedMap].getRoom(room_pos.x, room_pos.y); + + ImGui::Text("world pos %i %i", asdasd.x, asdasd.y); + + if(room != nullptr) { + ImGui::SeparatorText("Room Data"); + ImGui::Text("position %i %i", room->x, room->y); + ImGui::InputScalar("water level", ImGuiDataType_U8, &room->waterLevel); + uint8_t bg_min = 0, bg_max = 18; + if(ImGui::SliderScalar("background id", ImGuiDataType_U8, &room->bgId, &bg_min, &bg_max)) { + vertecies_bg_tex = renderBgs(maps[selectedMap]); + VBO_bg_tex->BufferData(vertecies_bg_tex.data(), vertecies_bg_tex.size() * sizeof(float) * 5); + } + + ImGui::InputScalar("pallet_index", ImGuiDataType_U8, &room->pallet_index); + ImGui::InputScalar("idk1", ImGuiDataType_U8, &room->idk1); + ImGui::InputScalar("idk2", ImGuiDataType_U8, &room->idk2); + ImGui::InputScalar("idk3", ImGuiDataType_U8, &room->idk3); + + auto tp = glm::ivec2(glm::mod(glm::vec2(asdasd), glm::vec2(room_size))); + auto tile = room->tiles[0][tp.y][tp.x]; + int tile_layer = 0; + + if(show_fg && tile.tile_id != 0) { + tile_layer = 0; + } else { + tile = room->tiles[1][tp.y][tp.x]; + tile_layer = 1; + if(!show_bg || tile.tile_id == 0) { + tile = {}; + tile_layer = 2; + } + } + + ImGui::SeparatorText("Room Tile Data"); + ImGui::Text("position %i %i %s", tp.x, tp.y, tile_layer == 0 ? "Foreground" : tile_layer == 1 ? "Background" : "N/A"); + ImGui::Text("id %i", tile.tile_id); + ImGui::Text("param %i", tile.param); + + if(ImGui::BeginTable("tile_flags_table", 2)) { + int flags = tile.flags; + + if(tile_layer == 2) ImGui::BeginDisabled(); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("horizontal_mirror", &flags, 1); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("vertical_mirror", &flags, 2); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("rotate_90", &flags, 4); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("rotate_180", &flags, 8); + + if(tile_layer == 2) ImGui::EndDisabled(); + + if(flags != tile.flags) { + room->tiles[tile_layer][tp.y][tp.x].flags = flags; + updateRender(); + } + + ImGui::EndTable(); + } + + auto& uv = uvs[tile.tile_id]; + + ImGui::SeparatorText("Tile Data"); + if(tile_layer == 2) ImGui::BeginDisabled(); + DrawUvFlags(uv); + if(tile_layer == 2) ImGui::EndDisabled(); + + auto pos = glm::vec2(asdasd) * 8.0f; + + if(sprites.contains(tile.tile_id)) { + auto sprite = sprites[tile.tile_id]; + + ImGui::NewLine(); + ImGui::Text("sprite"); + ImGui::Text("composite_size %i %i", sprite.composite_size.x, sprite.composite_size.y); + ImGui::Text("layer_count %i", sprite.layer_count); + ImGui::Text("composition_count %i", sprite.composition_count); + ImGui::Text("subsprite_count %i", sprite.subsprite_count); + ImGui::Text("animation_count %i", sprite.animation_count); + + auto bb_max = pos + glm::vec2(8, 8); + + int composition_id = 0; + for(int j = 0; j < sprite.layer_count; ++j) { + auto subsprite_id = sprite.compositions[composition_id * sprite.layer_count + j]; + if(subsprite_id >= sprite.subsprite_count) + continue; + + auto& subsprite = sprite.sub_sprites[subsprite_id]; + + auto ap = pos + glm::vec2(subsprite.composite_pos); + if(tile.vertical_mirror) { + ap.y = pos.y + (sprite.composite_size.y - (subsprite.composite_pos.y + subsprite.size.y)); + } + if(tile.horizontal_mirror) { + ap.x = pos.x + (sprite.composite_size.x - (subsprite.composite_pos.x + subsprite.size.x)); + } + + auto end = ap + glm::vec2(subsprite.size); + bb_max.x = std::max(bb_max.x, end.x); + bb_max.y = std::max(bb_max.y, end.y); + + auto& layer = sprite.layers[j]; + if(layer.is_normals1 || layer.is_normals2 || !layer.is_visible) continue; + + DrawRect(selectionVerts, ap, glm::vec2(subsprite.size), { 1, 1, 1, 1 }, 0.5f); + // ImGui::Image((ImTextureID)atlas->id.value, toImVec(size), ) + } + + DrawRect(selectionVerts, pos, bb_max - pos, { 1, 1, 1, 0.8 }, 1); + } else { + if(tile.tile_id == 0) { + DrawRect(selectionVerts, pos, glm::vec2(8), { 1, 1, 1, 1 }, 1); + } else { + DrawRect(selectionVerts, pos, glm::vec2(uv.size), { 1, 1, 1, 1 }, 1); + } + } + DrawRect(selectionVerts, room_pos * room_size * 8, glm::ivec2(40, 22) * 8, {1, 1, 1, 0.5}, 1); + } + } else if(mode == 1) { + static MapTile placing; + static bool background; + + ImGui::SameLine(); + HelpMarker("Middle click to copy a tile.\nRight click to place a tile."); + ImGui::Text("world pos %i %i", mouse_world_pos.x, mouse_world_pos.y); + + auto room_pos = mouse_world_pos / room_size; + auto room = maps[selectedMap].getRoom(room_pos.x, room_pos.y); + if(!io.WantCaptureMouse && room != nullptr) { + if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE)) { + auto tp = glm::ivec2(mouse_world_pos.x % room_size.x, mouse_world_pos.y % room_size.y); + auto tile = room->tiles[0][tp.y][tp.x]; + + if(show_fg && tile.tile_id != 0) { + placing = tile; + background = false; + } else if(show_bg) { + tile = room->tiles[1][tp.y][tp.x]; + if(tile.tile_id != 0) { + placing = tile; + background = true; + } else { + placing = {}; + } + } + } + if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT)) { + auto tp = glm::ivec2(mouse_world_pos.x % room_size.x, mouse_world_pos.y % room_size.y); + if(room->tiles[0][tp.y][tp.x] != placing) { + room->tiles[0][tp.y][tp.x] = placing; + updateRender(); + } + } + } + + ImGui::NewLine(); + ImGui::Checkbox("background layer", &background); + ImGui::NewLine(); + ImGui::InputScalar("id", ImGuiDataType_U16, &placing.tile_id); + ImGui::InputScalar("param", ImGuiDataType_U8, &placing.param); + + if(ImGui::BeginTable("tile_flags_table", 2)) { + int flags = placing.flags; + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Horizontal mirror", &flags, 1); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Vertical mirror", &flags, 2); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Rotate 90", &flags, 4); + ImGui::TableNextColumn(); ImGui::CheckboxFlags("Rotate 180", &flags, 8); + + placing.flags = flags; + + ImGui::EndTable(); + } + + auto& uv = uvs[placing.tile_id]; + + auto pos = glm::vec2(uv.pos); + auto size = glm::vec2(uv.size); + auto atlas_size = glm::vec2(atlas->width, atlas->height); + + ImGui::Text("preview"); + ImGui::Image((ImTextureID)atlas->id.value, toImVec(size * 8.0f), toImVec(pos / atlas_size), toImVec((pos + size) / atlas_size)); + + if(room != nullptr) { + DrawRect(selectionVerts, glm::vec2(mouse_world_pos) * 8.0f, glm::vec2(8), { 1, 1, 1, 1 }, 1); + DrawRect(selectionVerts, room_pos * room_size * 8, glm::ivec2(40, 22) * 8, { 1, 1, 1, 0.5 }, 1); + } + } + + ImGui::End(); +} + // Main code -int main(int, char**) { +int runViewer() { #pragma region glfw/opengl setup glfwSetErrorCallback(glfw_error_callback); if (!glfwInit()) @@ -401,7 +1439,7 @@ int main(int, char**) { // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only // Create window with graphics context - GLFWwindow* window = glfwCreateWindow(1280, 720, "Animal Well map viewer", nullptr, nullptr); + window = glfwCreateWindow(1280, 720, "Animal Well map viewer", nullptr, nullptr); if (window == nullptr) return 1; glfwMakeContextCurrent(window); @@ -472,69 +1510,6 @@ int main(int, char**) { // IM_ASSERT(font != nullptr); #pragma endregion -#pragma region map rendering - auto gameData = loadGameAssets(); - - Texture bg_tex; - glBindTexture(GL_TEXTURE_2D_ARRAY, bg_tex.id); - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGB, 320, 180, 19, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - bg_tex.LoadSubImage(1 - 1, gameData[14].data); - bg_tex.LoadSubImage(2 - 1, gameData[22].data); - bg_tex.LoadSubImage(3 - 1, gameData[22].data); - bg_tex.LoadSubImage(4 - 1, gameData[19].data); - bg_tex.LoadSubImage(5 - 1, gameData[19].data); - bg_tex.LoadSubImage(6 - 1, gameData[15].data); - bg_tex.LoadSubImage(7 - 1, gameData[13].data); - bg_tex.LoadSubImage(8 - 1, gameData[13].data); - bg_tex.LoadSubImage(9 - 1, gameData[16].data); - bg_tex.LoadSubImage(10 - 1, gameData[17].data); - bg_tex.LoadSubImage(11 - 1, gameData[16].data); - bg_tex.LoadSubImage(12 - 1, gameData[26].data); - bg_tex.LoadSubImage(13 - 1, gameData[11].data); - bg_tex.LoadSubImage(14 - 1, gameData[12].data); - bg_tex.LoadSubImage(15 - 1, gameData[20].data); - bg_tex.LoadSubImage(16 - 1, gameData[18].data); - bg_tex.LoadSubImage(17 - 1, gameData[23].data); - bg_tex.LoadSubImage(18 - 1, gameData[24].data); - bg_tex.LoadSubImage(19 - 1, gameData[21].data); - - Texture atlas; - atlas.Load(gameData[255].data); - - bool show_fg = true; - bool show_bg = true; - bool show_bg_tex = true; - bool do_lighting = true; - - glm::vec4 bg_color{0.8, 0.8, 0.8, 1}; - glm::vec4 fg_color{1, 1, 1, 1}; - glm::vec4 bg_tex_color{0.5, 0.5, 0.5, 1}; - - int selectedMap = 0; - static int mapIds[5] = {300, 193, 52, 222, 157}; - static const char* mapNames[5] = {"Overworld", "Space", "Bunny temple", "Time Capsule", "CE temple"}; - Map maps[5]{}; - for (size_t i = 0; i < 5; i++) { - maps[i] = parse_map(gameData[mapIds[i]]); - } - - std::map sprites; - for (auto el : spriteMapping) { - sprites[el.tile_id] = parse_sprite(gameData[el.asset_id]); - } - - std::vector uvs = parse_uvs(gameData[254]); - // position.xy/uv.zw - auto vertecies_fg = renderMap(maps[selectedMap], 0, sprites, uvs); - auto vertecies_bg = renderMap(maps[selectedMap], 1, sprites, uvs); - auto vertecies_bg_tex = renderBgs(maps[selectedMap]); - auto vertecies_light = renderLights(maps[selectedMap], uvs); - -#pragma endregion - #pragma region opgenl buffer setup glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -549,32 +1524,32 @@ int main(int, char**) { merge_shader.setInt("lights", 1); VAO VAO_fg{}; - VBO VBO_fg{GL_ARRAY_BUFFER, GL_STATIC_DRAW}; + VBO_fg = std::make_unique( GL_ARRAY_BUFFER, GL_STATIC_DRAW ); VAO_fg.Bind(); - VBO_fg.BufferData(vertecies_fg.data(), vertecies_fg.size() * sizeof(glm::vec4)); + VBO_fg->Bind(); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, nullptr); VAO VAO_bg{}; - VBO VBO_bg{GL_ARRAY_BUFFER, GL_STATIC_DRAW}; + VBO_bg = std::make_unique( GL_ARRAY_BUFFER, GL_STATIC_DRAW ); VAO_bg.Bind(); - VBO_bg.BufferData(vertecies_bg.data(), vertecies_bg.size() * sizeof(glm::vec4)); + VBO_bg->Bind(); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, nullptr); VAO VAO_bg_tex{}; - VBO VBO_bg_tex{ GL_ARRAY_BUFFER, GL_STATIC_DRAW }; + VBO_bg_tex = std::make_unique( GL_ARRAY_BUFFER, GL_STATIC_DRAW ); VAO_bg_tex.Bind(); - VBO_bg_tex.BufferData(vertecies_bg_tex.data(), vertecies_bg_tex.size() * sizeof(float) * 5); + VBO_bg_tex->Bind(); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(2 * sizeof(float))); VAO VAO_light{}; - VBO VBO_light{ GL_ARRAY_BUFFER, GL_STATIC_DRAW }; + VBO_light = std::make_unique( GL_ARRAY_BUFFER, GL_STATIC_DRAW ); VAO_light.Bind(); - VBO_light.BufferData(vertecies_light.data(), vertecies_light.size() * sizeof(float) * 5); + VBO_light->Bind(); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); glEnableVertexAttribArray(1); @@ -586,8 +1561,22 @@ int main(int, char**) { framebuffers.push_back(&light_fb); framebuffers.push_back(&tile_fb); + VAO VAO_selction{}; + VBO_selection = std::make_unique(GL_ARRAY_BUFFER, GL_STATIC_DRAW); + VAO_selction.Bind(); + VBO_selection->Bind(); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(2 * sizeof(float))); + #pragma endregion + if(!LoadFileDialog.tryLoad()) { + LoadFileDialog.error_msg = ""; + LoadFileDialog.open(); + } + // Main loop while (!glfwWindowShouldClose(window)) { // Poll and handle events (inputs, window resize, etc.) @@ -602,184 +1591,96 @@ int main(int, char**) { ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); - auto MVP = projection * view; - - // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). - // if(show_demo_window) ImGui::ShowDemoWindow(&show_demo_window); - - { - ImGui::Begin("Window"); - - if (ImGui::Combo("Map", &selectedMap, mapNames, 5)) { - vertecies_fg = renderMap(maps[selectedMap], 0, sprites, uvs); - vertecies_bg = renderMap(maps[selectedMap], 1, sprites, uvs); - vertecies_bg_tex = renderBgs(maps[selectedMap]); - vertecies_light = renderLights(maps[selectedMap], uvs); - - VBO_fg.BufferData(vertecies_fg.data(), vertecies_fg.size() * sizeof(glm::vec4)); - VBO_bg.BufferData(vertecies_bg.data(), vertecies_bg.size() * sizeof(glm::vec4)); - VBO_bg_tex.BufferData(vertecies_bg_tex.data(), vertecies_bg_tex.size() * sizeof(float) * 5); - VBO_light.BufferData(vertecies_light.data(), vertecies_light.size() * sizeof(float) * 5); + selectionVerts.clear(); + + MVP = projection * view; + + DockSpaceOverViewport(); + LoadFileDialog.draw(); + // ImGui::ShowDemoWindow(); + + // skip rendering if no data is loaded + if(!rawData.empty()) { + DrawTileWindow(); + DrawFindWindow(); + DrawSpriteWindow(); + DrawPreviewWindow(); + + // 1. light pass + if(do_lighting) { + glClearColor(0.0, 0.0, 0.0, 1.0); + light_fb.Bind(); + glClear(GL_COLOR_BUFFER_BIT); + + light_shader.Use(); + light_shader.setMat4("MVP", MVP); + VAO_light.Bind(); + glDrawArrays(GL_TRIANGLES, 0, vertecies_light.size()); + } else { + glClearColor(1.0, 1.0, 1.0, 1.0); + light_fb.Bind(); + glClear(GL_COLOR_BUFFER_BIT); } - ImGui::Checkbox("Foreground Tiles", &show_fg); - ImGui::ColorEdit4("fg tile color", &fg_color.r); - ImGui::Checkbox("Background Tiles", &show_bg); - ImGui::ColorEdit4("bg tile color", &bg_color.r); - ImGui::Checkbox("Background Texture", &show_bg_tex); - ImGui::ColorEdit4("bg Texture color", &bg_tex_color.r); - ImGui::Checkbox("Apply lighting", &do_lighting); + glBindFramebuffer(GL_FRAMEBUFFER, 0); - auto mp = glm::vec4(((mousePos - screenSize / 2.0f) / screenSize) * 2.0f, 0, 1); - mp.y = -mp.y; - auto wp = glm::vec2(glm::inverse(MVP) * mp); - - // ImGui::Text("mouse pos %f %f", mousePos.x, mousePos.y); - ImGui::Text("world pos %f %f", (wp.x / 8), (wp.y / 8)); - - auto room_size = glm::vec2(40 * 8, 22 * 8); - auto rp = glm::ivec2(wp / room_size); - auto room = maps[selectedMap](rp.x, rp.y); - - if (wp.x >= 0 && wp.y >= 0 && room != nullptr) { - ImGui::NewLine(); - ImGui::Text("room"); - ImGui::Text("position %i %i", room->x, room->y); - ImGui::Text("water level %i", room->waterLevel); - ImGui::Text("background id %i", room->bgId); - - ImGui::Text("pallet_index %i", room->pallet_index); - ImGui::Text("idk1 %i", room->idk1); - ImGui::Text("idk2 %i", room->idk2); - ImGui::Text("idk3 %i", room->idk3); + // 2. main pass + tile_fb.Bind(); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); - auto tp = glm::ivec2(glm::mod(wp, room_size) / 8.0f); - auto tile = room->tiles[0][tp.y][tp.x]; - int tile_layer = 0; + if(show_bg_tex) { + bg_shader.Use(); + bg_shader.setMat4("MVP", MVP); + bg_shader.setVec4("color", bg_tex_color); - if (show_fg && tile.tile_id != 0) { - tile_layer = 0; - } else { - tile = room->tiles[1][tp.y][tp.x]; - tile_layer = 1; - if (!show_bg || tile.tile_id == 0) { - tile = {}; - tile_layer = 2; - } - } + glActiveTexture(GL_TEXTURE0); + bg_tex->Bind(); - ImGui::NewLine(); - ImGui::Text("tile"); - ImGui::Text("offset %i %i %s", tp.x, tp.y, tile_layer == 0 ? "Foreground" : tile_layer == 1 ? "Background" : "N/A"); - ImGui::Text("id %i", tile.tile_id); - ImGui::Text("param %i", tile.param); + VAO_bg_tex.Bind(); + glDrawArrays(GL_TRIANGLES, 0, vertecies_bg_tex.size()); + } - ImGui::BeginDisabled(); - int flags = tile.flags; - ImGui::CheckboxFlags("horizontal_mirror", &flags, 1); - ImGui::CheckboxFlags("vertical_mirror", &flags, 2); - ImGui::CheckboxFlags("rotate_90", &flags, 4); - ImGui::CheckboxFlags("rotate_180", &flags, 8); + tile_shader.Use(); + tile_shader.setMat4("MVP", MVP); - int uv_flags = uvs[tile.tile_id].flags; - ImGui::NewLine(); - ImGui::Text("uv flags"); - - ImGui::CheckboxFlags("collides_left", &uv_flags, 1 << 0); // correct - ImGui::CheckboxFlags("collides_right", &uv_flags, 1 << 1); // correct - ImGui::CheckboxFlags("collides_up", &uv_flags, 1 << 2); // correct - ImGui::CheckboxFlags("collides_down", &uv_flags, 1 << 3); // correct - ImGui::CheckboxFlags("not_placeable", &uv_flags, 1 << 4); - ImGui::CheckboxFlags("additive", &uv_flags, 1 << 5); - ImGui::CheckboxFlags("obscures", &uv_flags, 1 << 6); - ImGui::CheckboxFlags("contiguous", &uv_flags, 1 << 7); // correct - ImGui::CheckboxFlags("blocks_light", &uv_flags, 1 << 8); - ImGui::CheckboxFlags("self_contiguous", &uv_flags, 1 << 9); // correct - ImGui::CheckboxFlags("hidden", &uv_flags, 1 << 10); - ImGui::CheckboxFlags("dirt", &uv_flags, 1 << 11); - ImGui::CheckboxFlags("has_normals", &uv_flags, 1 << 12); // correct - ImGui::CheckboxFlags("uv_light", &uv_flags, 1 << 13); // correct - - ImGui::EndDisabled(); + glActiveTexture(GL_TEXTURE0); + atlas->Bind(); - if (sprites.contains(tile.tile_id)) { - auto sprite = sprites[tile.tile_id]; - - ImGui::NewLine(); - ImGui::Text("sprite"); - ImGui::Text("composite_size %i %i", sprite.composite_size.x, sprite.composite_size.y); - ImGui::Text("layer_count %i", sprite.layer_count); - ImGui::Text("composition_count %i", sprite.composition_count); - ImGui::Text("subsprite_count %i", sprite.subsprite_count); - ImGui::Text("animation_count %i", sprite.animation_count); - } + if (show_bg) { + tile_shader.setVec4("color", bg_color); + VAO_bg.Bind(); + glDrawArrays(GL_TRIANGLES, 0, vertecies_bg.size()); + } + if (show_fg) { + tile_shader.setVec4("color", fg_color); + VAO_fg.Bind(); + glDrawArrays(GL_TRIANGLES, 0, vertecies_fg.size()); } - ImGui::End(); - } - - // 1. light pass - if(do_lighting) { - glClearColor(0.0, 0.0, 0.0, 1.0); - light_fb.Bind(); + // 3. final merge pass + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClearColor(0.45f, 0.55f, 0.60f, 1.00f); glClear(GL_COLOR_BUFFER_BIT); + merge_shader.Use(); + glActiveTexture(GL_TEXTURE0); + tile_fb.tex.Bind(); + glActiveTexture(GL_TEXTURE1); + light_fb.tex.Bind(); + + RenderQuad(); + + // 4. draw selection vertices over that light_shader.Use(); light_shader.setMat4("MVP", MVP); - VAO_light.Bind(); - glDrawArrays(GL_TRIANGLES, 0, vertecies_light.size()); + VAO_selction.Bind(); + VBO_selection->BufferData(selectionVerts.data(), selectionVerts.size() * sizeof(float) * 6); + glDrawArrays(GL_TRIANGLES, 0, selectionVerts.size()); } else { - glClearColor(1.0, 1.0, 1.0, 1.0); - light_fb.Bind(); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClearColor(0.45f, 0.55f, 0.60f, 1.00f); glClear(GL_COLOR_BUFFER_BIT); } - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - // 2. main pass - tile_fb.Bind(); - glClearColor(0.0, 0.0, 0.0, 0.0); - glClear(GL_COLOR_BUFFER_BIT); - - if(show_bg_tex) { - bg_shader.Use(); - bg_shader.setMat4("MVP", MVP); - bg_shader.setVec4("color", bg_tex_color); - - glActiveTexture(GL_TEXTURE0); - bg_tex.Bind(); - - VAO_bg_tex.Bind(); - glDrawArrays(GL_TRIANGLES, 0, vertecies_bg_tex.size()); - } - - tile_shader.Use(); - tile_shader.setMat4("MVP", MVP); - - glActiveTexture(GL_TEXTURE0); - atlas.Bind(); - - if (show_bg) { - tile_shader.setVec4("color", bg_color); - VAO_bg.Bind(); - glDrawArrays(GL_TRIANGLES, 0, vertecies_bg.size()); - } - if (show_fg) { - tile_shader.setVec4("color", fg_color); - VAO_fg.Bind(); - glDrawArrays(GL_TRIANGLES, 0, vertecies_fg.size()); - } - - // 3. final merge pass - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glClearColor(0.45f, 0.55f, 0.60f, 1.00f); - glClear(GL_COLOR_BUFFER_BIT); - - merge_shader.Use(); - glActiveTexture(GL_TEXTURE0); - tile_fb.tex.Bind(); - glActiveTexture(GL_TEXTURE1); - light_fb.tex.Bind(); - - RenderQuad(); // Rendering ImGui::Render(); @@ -808,3 +1709,16 @@ int main(int, char**) { return 0; } + +int main(int, char**) { + runViewer(); + return 0; +} + +#ifdef _WIN32 +#include +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + // SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + return main(__argc, __argv); +} +#endif diff --git a/src/parsing.hpp b/src/parsing.hpp deleted file mode 100644 index b6c9f67..0000000 --- a/src/parsing.hpp +++ /dev/null @@ -1,527 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "aes.hpp" - -#include - -// starts at 0x020E9A00 or 1420eb000 in ghidra -struct Asset { - // 0 = txt - // 1 = map data - // 2 = png - // 3 = ogg - // 5 = tile data - // 7 = shaders? - // 8 = BMF font format? - // 64 = xps - // 65 = map data - // 66 = png - // 67 = ogg - // encrypted if (type & 192) == 64 - uint8_t type; - uint8_t unknown1[7]; - - uint64_t ptr; // offset by 0x140001200 - uint32_t length; - - uint32_t unknown2; - uint64_t unknown3; - - uint8_t unknown4[16]; -}; -static_assert(sizeof(Asset) == 0x30); - -enum class AssetType { - Text = 0, - MapData = 1, - Png = 2, - Ogg = 3, - SpriteData = 5, - Shader = 7, - Font = 8, - - XPS = 9, - /* - Encrypted_MapData = 65, - Encrypted_Png = 66, - Encrypted_Ogg = 67 - */ -}; - -struct OutAsset { - AssetType type; - std::vector data; -}; - -struct MapHeader { - uint32_t signature1; - uint16_t roomCount; - uint8_t idk1; - uint8_t idk2; - uint32_t unused; - uint32_t signature2; -}; - -struct MapTile { - uint16_t tile_id; - uint8_t param; // depends on tile_id - - union { - struct { - bool horizontal_mirror : 1; - bool vertical_mirror : 1; - bool rotate_90 : 1; - bool rotate_180 : 1; - }; - uint8_t flags; - }; -}; -static_assert(sizeof(MapTile) == 4); - -struct Room { - uint8_t x; - uint8_t y; - - uint8_t bgId; - uint8_t waterLevel; - - uint8_t pallet_index; - uint8_t idk1; - uint8_t idk2; - uint8_t idk3; - - MapTile tiles[2][22][40]; -}; - -static_assert(sizeof(Room) == 0x1b88); - -class Map { - public: - glm::ivec2 offest, size; - std::vector rooms; - std::unordered_map coordinate_map; - - const Room* operator()(int x, int y) const { - if(x < 0 || x >= 256 || y < 0 || y >= 256) - return nullptr; - if(coordinate_map.contains(x | (y << 8))) { - return &rooms[coordinate_map.at(x | (y << 8))]; - } - return nullptr; - } - - std::optional getTile(int layer, int x, int y) const { - auto rx = x / 40; - auto ry = y / 22; - - if(rx < 0 || rx >= 256 || ry < 0 || ry >= 256) - return std::nullopt; - - if(coordinate_map.contains(rx | (ry << 8))) { - const auto& room = rooms[coordinate_map.at(rx | (ry << 8))]; - return room.tiles[layer][y % 22][x % 40]; - } - - return std::nullopt; - } -}; - -typedef glm::vec<2, uint16_t, glm::defaultp> shortVec2; - -struct SpriteAnimation { - uint16_t start; // First composition in the animation - uint16_t end; // Last composition in the animation - uint16_t frame_delay; // After waiting this many frames, increment the composition number -}; -static_assert(sizeof(SpriteAnimation) == 6); - -struct SubSprite { - shortVec2 atlas_pos; // Atlas coordinates, relative to the entity's base coordinates - shortVec2 composite_pos; // Position of this subsprite on the composited sprite - shortVec2 size; // Size of the subsprite -}; -static_assert(sizeof(SubSprite) == 12); - -struct SpriteLayer { - union { - struct { - uint8_t : 1; - bool is_normals1 : 1; - bool is_normals2 : 1; - bool uv_light : 1; - bool is_conditional : 1; - }; - uint8_t flags; - }; - - uint8_t alpha; - bool is_visible; -}; -static_assert(sizeof(SpriteLayer) == 3); - -struct SpriteData { - shortVec2 composite_size; - uint16_t layer_count; - uint16_t composition_count; - uint8_t subsprite_count; - uint8_t animation_count; - - std::vector animations; - std::vector compositions; - std::vector sub_sprites; - std::vector layers; -}; - -struct uv_data { - shortVec2 pos; - shortVec2 size; - - union { - struct { - bool collides_left : 1; - bool collides_right : 1; - bool collides_up : 1; - bool collides_down : 1; - - bool not_placeable : 1; - bool additive : 1; - - bool obscures : 1; - bool contiguous : 1; - bool blocks_light : 1; - bool self_contiguous : 1; - bool hidden : 1; - bool dirt : 1; - bool has_normals : 1; - bool uv_light : 1; - }; - uint16_t flags; - }; -}; -static_assert(sizeof(uv_data) == 10); - -struct TileMapping { - int internal_id; - int asset_id; - int tile_id; -}; - -// extracted from game code -TileMapping spriteMapping[] = { - {0, 0x47, 0x160}, - {1, 0x8c, 0x161}, - {2, 0xcb, 0x16b}, - {3, 0x79, 0x93}, - {4, 0x19, 0x16d}, - {5, 0x67, 0x16f}, - {6, 0xc6, 0x121}, - {7, 199, 0x120}, - {8, 0x99, 0x17b}, - {9, 0x8f, 0x17d}, - {10, 0x9a, 0x184}, - {0xb, 0x9b, 0x186}, - {0xc, 4, 0x18c}, - {0xd, 0xd3, 0x1a0}, - {0xe, 0x90, 0x19f}, - {0xf, 10, 0x1a7}, - {0x10, 0x7e, 0x1a5}, - {0x11, 0x66, 0x1a6}, - {0x12, 0xa5, 0x1a4}, - {0x13, 0x10b, 0x30b}, - {0x14, 0x28, 0x1a8}, - {0x15, 0xdb, 0x1ac}, - {0x16, 0x5d, 0x1dc}, - {0x17, 0x5c, 5}, - {0x18, 0xf8, 0xdb}, - {0x19, 0x31, 0x40}, - {0x1a, 0xae, 0x56}, - {0x1b, 0x44, 0x43}, - {0x1c, 0x54, 0x6a}, - {0x1d, 0x5f, 0x6b}, - {0x1e, 0x5e, 0xed}, - {0x1f, 0x4b, 0x22}, - {0x20, 0x6e, 0xa3}, - {0x21, 0, 0x124}, - {0x22, 0x39, 0x125}, - {0x23, 0x12e, 0x151}, - {0x24, 0xdd, 0x127}, - {0x25, 5, 0x128}, - {0x26, 0x8e, 0x155}, - {0x27, 0x12d, 0x14b}, - {0x28, 0x97, 0x173}, - {0x29, 0x96, 0x179}, - {0x2a, 0xa8, 0x20f}, - {0x2b, 0x128, 0x213}, - {0x2c, 0x48, 0x21c}, - {0x2d, 0xa7, 0x220}, - {0x2e, 0xba, 0x221}, - {0x2f, 0x77, 0x225}, - {0x30, 0x33, 0x226}, - {0x31, 0xd0, 0x227}, - {0x32, 0x10f, 0x228}, - {0x33, 0x110, 0x229}, - {0x34, 0xbe, 0x22c}, - {0x35, 0xfb, 0x22e}, - {0x36, 0xce, 0x22f}, - {0x37, 0xcd, 0x230}, - {0x38, 0x114, 0x319}, - {0x39, 0xb6, 0x136}, - {0x3a, 0xa4, 0xce}, - {0x3b, 0x3c, 0x137}, - {0x3c, 0x3d, 0xc5}, - {0x3d, 0xd1, 0x234}, - {0x3e, 0xb7, 0x236}, - {0x3f, 0xb8, 0x30a}, - {0x40, 0x23, 0x235}, - {0x41, 0x1b, 0x238}, - {0x42, 0x26, 0x239}, - {0x43, 0x6c, 0x23b}, - {0x44, 0x6d, 0x244}, - {0x45, 0xbd, 0x245}, - {0x46, 0xef, 0x75}, - {0x47, 0xca, 0x105}, - {0x48, 0x10c, 0x247}, - {0x49, 0xa1, 0x249}, - {0x4a, 0x9f, 0x254}, - {0x4b, 0xa0, 0x255}, - {0x4c, 0x22, 0x24a}, - {0x4d, 0xd6, 0x24e}, - {0x4e, 0x55, 0x24b}, - {0x4f, 0x56, 0x24c}, - {0x50, 0x57, 0x24d}, - {0x51, 0x58, 0x25d}, - {0x52, 0x59, 0x2cb}, - {0x53, 0x5a, 0x302}, - {0x54, 0x5b, 0x308}, - {0x55, 0x11a, 0x256}, - {0x56, 0x87, 0x100}, - {0x57, 0x3b, 0x259}, - {0x58, 0xed, 0x25e}, - {0x59, 0x24, 0x25f}, - {0x5a, 0x7b, 0x260}, - {0x5b, 0x7a, 0x309}, - {0x5c, 0x2d, 0x264}, - {0x5d, 8, 0x265}, - {0x5e, 0x53, 0x26b}, - {0x5f, 0x7c, 0x26e}, - {0x60, 6, 0x271}, - {0x61, 0x80, 0x273}, - {0x62, 7, 0x279}, - {99, 0x119, 0x27c}, - {100, 0xda, 0x1bf}, - {0x65, 0x98, 0x1dd}, - {0x66, 0x46, 0x10d}, - {0x67, 200, 0x10c}, - {0x68, 0x9c, 0xfb}, - {0x69, 0x71, 0x142}, - {0x6a, 0x117, 0x28e}, - {0x6b, 0x2f, 0x291}, - {0x6c, 0x68, 0x29c}, - {0x6d, 0xaa, 0x2a2}, - {0x6e, 0x6f, 0x2a9}, - {0x6f, 0xf9, 0x2aa}, - {0x70, 0xa9, 0x2b5}, - {0x71, 0x30, 0x2ca}, - {0x72, 0xfc, 0x29b}, - {0x73, 0x92, 0x2cc}, - {0x74, 0x112, 0x2cd}, - {0x75, 0x11b, 0x2d0}, - {0x76, 0xb2, 0x2d1}, - {0x77, 0x1d, 0x31a}, - {0x78, 0x116, 0x2d2}, - {0x79, 0xc5, 0x2d7}, - {0x7a, 0x36, 0x2d8}, - {0x7b, 0xc3, 10}, - {0x7c, 0xc2, 0x8e}, - {0x7d, 0x94, 0x92}, - {0x7e, 0x93, 0xa8}, - {0x7f, 0x9e, 0x2da}, - {0x80, 0xb0, 0x2e7}, - {0x81, 0xfa, 0x2e8}, - {0x82, 0x3a, 0x2e9}, - {0x83, 0x1f, 0x301}, - {0x84, 0x82, 0x2ea}, - {0x85, 0x83, 0x2eb}, - {0x86, 0x84, 0x2ec}, - {0x87, 0xd7, 0x2ee}, - {0x88, 0xbc, 0x2ef}, - {0x89, 0x37, 0x2ed}, - {0x8a, 0x88, 0x2f0}, - {0x8b, 0x85, 0x2f1}, - {0x8c, 0x78, 0x2ff}, - {0x8d, 0x3f, 0x300}, - {0x8e, 0xbb, 0x303}, - {0x8f, 0xa3, 0x44}, - {0x90, 0x70, 0x306}, - {0x91, 0xc9, 0x314}, - {0x92, 0x21, 0x315}, - {0x93, 0x6a, 0x31c}, - {0x94, 0x69, 0x31d}, - {0x95, 0x95, 0x31e}, - {0x96, 0x27, 799}, - {0x97, 0x35, 0x323}, - {0x98, 0x7f, 0x324}, - {0x99, 0x20, 0x32e}, - {0x9a, 0x1c, 0x330}, - {0x9b, 0xa2, 0x333}, - {0x9c, 0xd9, 0x334}, - {0x9d, 0xaf, 0x33e} -}; - -auto readFile(const char *path) { - std::ifstream testFile(path, std::ios::binary); - std::vector fileContents((std::istreambuf_iterator(testFile)), std::istreambuf_iterator()); - return fileContents; -} - -std::optional> tryDecrypt(const Asset &item, const char *data) { - std::array keys[3] = { - {'G', 'o', 'o', 'd', 'L', 'U', 'c', 'K', 'M', 'y', 'F', 'r', 'i', 'E', 'n', 'd'}, - {0xC9, 0x9B, 0x64, 0x96, 0x5C, 0xCE, 0x04, 0xF0, 0xF5, 0xCB, 0x54, 0xCA, 0xC9, 0xAB, 0x62, 0xC6}, // bunny works for 30/52 - {0x11, 0x14, 0x18, 0x14, 0x88, 0x82, 0x42, 0x82, 0x28, 0x24, 0x88, 0x82, 0x11, 0x18, 0x44, 0x11} // time capsule works for 222/277/377 - }; - - if ((item.type & 192) == 64) { - for (auto &&k : keys) { - auto res = decrypt(data, item.length, k); - if (res.has_value()) - return res; - } - } - - return std::nullopt; -} - -void dumpNormalFiles(const std::vector &input) { - auto assets = (const Asset *)(input.data() + 0x020E9A00); - - for (int i = 0; i < 676; i++) { - auto &item = assets[i]; - - std::string ext = ".bin"; - if (item.type == 0) { - ext = ".txt"; - } else if (item.type == 2 || item.type == 66) { - ext = ".png"; - } else if (item.type == 3 || item.type == 67) { - ext = ".ogg"; - } else if (item.type == 64) { - ext = ".xps"; - } - auto actual_ptr = (const char *)(input.data() + (item.ptr - 0x140001200)); - - std::ofstream file("out/" + std::to_string(i) + ext, std::ios::binary); - if (auto decrypted = tryDecrypt(item, actual_ptr)) { - file.write((char *)decrypted.value().data(), decrypted.value().size()); - } else { - file.write(actual_ptr, item.length); - } - } -} - -static auto loadGameAssets() { - auto input = readFile("C:/Program Files (x86)/Steam/steamapps/common/Animal Well/Animal Well.exe"); - auto assets = (const Asset *)(input.data() + 0x020E9A00); - - std::vector res; - - for (int i = 0; i < 676; i++) { - auto &item = assets[i]; - auto actual_ptr = (const char *)(input.data() + (item.ptr - 0x140001200)); - - std::vector data; - if (auto decrypted = tryDecrypt(item, actual_ptr)) { - data = decrypted.value(); - } else { - data.resize(item.length); - std::memcpy(data.data(), actual_ptr, item.length); - } - res.push_back({(AssetType)item.type, std::move(data)}); - } - - return res; -} - -static std::vector parse_uvs(const OutAsset &asset) { - auto ptr = (uv_data *)(asset.data.data() + 0xC); - - auto magic = *(uint32_t *)asset.data.data(); // proabably magic? - auto count = *(uint32_t *)(asset.data.data() + 4); - auto unused = *(uint32_t *)(asset.data.data() + 8); // null in the given asset - - assert(count == (asset.data.size() - 0xC) / sizeof(uv_data)); - return std::vector(ptr, ptr + count); -} - -static SpriteData parse_sprite(const OutAsset &asset) { - assert(asset.type == AssetType::SpriteData); - - auto ptr = asset.data.data(); - - auto magic = *(uint32_t *)ptr; - assert(magic == 0x0003AC1D); - - SpriteData out; - out.composite_size.x = *(uint16_t *)(ptr + 4); - out.composite_size.y = *(uint16_t *)(ptr + 6); - out.layer_count = *(uint16_t *)(ptr + 8); - out.composition_count = *(uint16_t *)(ptr + 10); - out.subsprite_count = *(uint8_t *)(ptr + 12); - out.animation_count = *(uint8_t *)(ptr + 13); - - ptr += 0x30; - - out.animations = {(SpriteAnimation *)ptr, ((SpriteAnimation *)ptr) + out.animation_count}; - ptr += out.animation_count * sizeof(SpriteAnimation); - - out.compositions = {ptr, ptr + out.layer_count * out.composition_count}; - ptr += out.layer_count * out.composition_count; - - out.sub_sprites = {(SubSprite *)ptr, ((SubSprite *)ptr) + out.subsprite_count}; - ptr += out.subsprite_count * sizeof(SubSprite); - - out.layers = {(SpriteLayer *)ptr, (SpriteLayer *)(ptr + out.layer_count * 3)}; - - return out; -} - -static Map parse_map(const OutAsset &asset) { - auto head = *(MapHeader *)asset.data.data(); - Room *rooms = (Room *)(asset.data.data() + 0x10); - - Map m; - m.rooms = {rooms, rooms + head.roomCount}; - - int x_min = 65535, x_max = 0; - int y_min = 65535, y_max = 0; - - for (int i = 0; i < head.roomCount; i++) { - auto &room = m.rooms[i]; - x_min = std::min(x_min, (int)room.x); - x_max = std::max(x_max, (int)room.x); - y_min = std::min(y_min, (int)room.y); - y_max = std::max(y_max, (int)room.y); - - m.coordinate_map[room.x | (room.y << 8)] = i; - } - - int width = x_max - x_min + 1; - int height = y_max - y_min + 1; - - m.offest = {x_min, y_min}; - m.size = {width, height}; - - return m; -} diff --git a/src/shaders/light.fs b/src/shaders/light.fs index 831fb70..9875c5f 100644 --- a/src/shaders/light.fs +++ b/src/shaders/light.fs @@ -1,9 +1,9 @@ #version 330 core -in vec3 color; +in vec4 color; out vec4 result; void main() { - result = vec4(color, 1); + result = color; } diff --git a/src/shaders/light.vs b/src/shaders/light.vs index e558b3b..a52a213 100644 --- a/src/shaders/light.vs +++ b/src/shaders/light.vs @@ -1,9 +1,9 @@ #version 330 core layout(location = 0) in vec2 vertexPosition; -layout(location = 1) in vec3 vertexColor; +layout(location = 1) in vec4 vertexColor; -out vec3 color; +out vec4 color; uniform mat4 MVP; diff --git a/src/structures/ambient.hpp b/src/structures/ambient.hpp new file mode 100644 index 0000000..a815dc1 --- /dev/null +++ b/src/structures/ambient.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +struct AmbientData { + glm::u8vec4 fg_color; + glm::u8vec4 bg_color; + glm::u8vec4 ambient_light; + uint8_t unk1; + uint8_t unk2; + uint8_t unk3; + uint8_t unk4; + float floats[5]; +}; +static_assert(sizeof(AmbientData) == 36); diff --git a/src/structures/asset.cpp b/src/structures/asset.cpp new file mode 100644 index 0000000..ca89ad8 --- /dev/null +++ b/src/structures/asset.cpp @@ -0,0 +1,33 @@ +#include "asset.hpp" + +#include +#include + +#include "../aes.hpp" +#include "../dos_parser.hpp" + +static std::array keys[3] = { + {'G', 'o', 'o', 'd', 'L', 'U', 'c', 'K', 'M', 'y', 'F', 'r', 'i', 'E', 'n', 'd'}, + {0xC9, 0x9B, 0x64, 0x96, 0x5C, 0xCE, 0x04, 0xF0, 0xF5, 0xCB, 0x54, 0xCA, 0xC9, 0xAB, 0x62, 0xC6}, // bunny works for 30/52 + {0x11, 0x14, 0x18, 0x14, 0x88, 0x82, 0x42, 0x82, 0x28, 0x24, 0x88, 0x82, 0x11, 0x18, 0x44, 0x11} // time capsule works for 222/277/377 +}; + +std::vector readFile(const char *path) { + std::ifstream testFile(path, std::ios::binary); + return std::vector(std::istreambuf_iterator(testFile), std::istreambuf_iterator()); +} + +bool tryDecrypt(const asset_entry &item, std::span data, std::vector& out) { + if (((uint8_t)item.type & 192) == 64) { + for(auto& k : keys) { + if (decrypt(data, k, out)) { + return true; + } + } + } + return false; +} + +std::vector encrypt(std::span data, int keyNum) { + return encrypt(data, keys[keyNum]); +} diff --git a/src/structures/asset.hpp b/src/structures/asset.hpp new file mode 100644 index 0000000..cb8929f --- /dev/null +++ b/src/structures/asset.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include + +enum class AssetType : uint8_t { + Text = 0, + MapData = 1, + Png = 2, + Ogg = 3, + SpriteData = 5, + Shader = 7, + Font = 8, + + Encrypted_XPS = 64, + Encrypted_MapData = 65, + Encrypted_Png = 66, + Encrypted_Ogg = 67 +}; + +// starts at 0x020E9A00 or 1420eb000 in ghidra +struct asset_entry { + AssetType type; + uint8_t unknown1[7]; + + uint64_t ptr; // offset by 0x140001200 + uint32_t length; + + uint32_t unknown2; + uint64_t unknown3; + + uint8_t unknown4[16]; +}; +static_assert(sizeof(asset_entry) == 0x30); + +struct OutAsset { + asset_entry info; + std::vector data; +}; + +std::vector readFile(const char* path); + +bool tryDecrypt(const asset_entry& item, std::span data, std::vector& out); + +std::vector encrypt(std::span data, int keyNum); diff --git a/src/structures/entity.hpp b/src/structures/entity.hpp new file mode 100644 index 0000000..bf7359c --- /dev/null +++ b/src/structures/entity.hpp @@ -0,0 +1,252 @@ +#pragma once + +#include +#include +#include + +struct SpriteAnimation { + uint16_t start; // First composition in the animation + uint16_t end; // Last composition in the animation + uint16_t frame_delay; // After waiting this many frames, increment the composition number +}; +static_assert(sizeof(SpriteAnimation) == 6); + +struct SubSprite { + glm::u16vec2 atlas_pos; // Atlas coordinates, relative to the entity's base coordinates + glm::u16vec2 composite_pos; // Position of this subsprite on the composited sprite + glm::u16vec2 size; // Size of the subsprite +}; +static_assert(sizeof(SubSprite) == 12); + +struct SpriteLayer { + union { + struct { + uint8_t : 1; + bool is_normals1 : 1; + bool is_normals2 : 1; + bool uv_light : 1; + bool is_conditional : 1; + }; + uint8_t flags; + }; + + uint8_t alpha; + bool is_visible; +}; +static_assert(sizeof(SpriteLayer) == 3); + +struct SpriteData { + glm::u16vec2 composite_size; + uint16_t layer_count; + uint16_t composition_count; + uint8_t subsprite_count; + uint8_t animation_count; + + std::vector animations; + std::vector compositions; // are frames + std::vector sub_sprites; + std::vector layers; +}; + + +struct TileMapping { + int internal_id; + int asset_id; + int tile_id; +}; + +// extracted from game code +constexpr TileMapping spriteMapping[] = { + {0, 0x47, 0x160}, + {1, 0x8c, 0x161}, + {2, 0xcb, 0x16b}, + {3, 0x79, 0x93}, + {4, 0x19, 0x16d}, + {5, 0x67, 0x16f}, + {6, 0xc6, 0x121}, + {7, 199, 0x120}, + {8, 0x99, 0x17b}, + {9, 0x8f, 0x17d}, + {10, 0x9a, 0x184}, + {0xb, 0x9b, 0x186}, + {0xc, 4, 0x18c}, + {0xd, 0xd3, 0x1a0}, + {0xe, 0x90, 0x19f}, + {0xf, 10, 0x1a7}, + {0x10, 0x7e, 0x1a5}, + {0x11, 0x66, 0x1a6}, + {0x12, 0xa5, 0x1a4}, + {0x13, 0x10b, 0x30b}, + {0x14, 0x28, 0x1a8}, + {0x15, 0xdb, 0x1ac}, + {0x16, 0x5d, 0x1dc}, + {0x17, 0x5c, 5}, + {0x18, 0xf8, 0xdb}, + {0x19, 0x31, 0x40}, + {0x1a, 0xae, 0x56}, + {0x1b, 0x44, 0x43}, + {0x1c, 0x54, 0x6a}, + {0x1d, 0x5f, 0x6b}, + {0x1e, 0x5e, 0xed}, + {0x1f, 0x4b, 0x22}, + {0x20, 0x6e, 0xa3}, + {0x21, 0, 0x124}, + {0x22, 0x39, 0x125}, + {0x23, 0x12e, 0x151}, + {0x24, 0xdd, 0x127}, + {0x25, 5, 0x128}, + {0x26, 0x8e, 0x155}, + {0x27, 0x12d, 0x14b}, + {0x28, 0x97, 0x173}, + {0x29, 0x96, 0x179}, + {0x2a, 0xa8, 0x20f}, + {0x2b, 0x128, 0x213}, + {0x2c, 0x48, 0x21c}, + {0x2d, 0xa7, 0x220}, + {0x2e, 0xba, 0x221}, + {0x2f, 0x77, 0x225}, + {0x30, 0x33, 0x226}, + {0x31, 0xd0, 0x227}, + {0x32, 0x10f, 0x228}, + {0x33, 0x110, 0x229}, + {0x34, 0xbe, 0x22c}, + {0x35, 0xfb, 0x22e}, + {0x36, 0xce, 0x22f}, + {0x37, 0xcd, 0x230}, + {0x38, 0x114, 0x319}, + {0x39, 0xb6, 0x136}, + {0x3a, 0xa4, 0xce}, + {0x3b, 0x3c, 0x137}, + {0x3c, 0x3d, 0xc5}, + {0x3d, 0xd1, 0x234}, + {0x3e, 0xb7, 0x236}, + {0x3f, 0xb8, 0x30a}, + {0x40, 0x23, 0x235}, + {0x41, 0x1b, 0x238}, + {0x42, 0x26, 0x239}, + {0x43, 0x6c, 0x23b}, + {0x44, 0x6d, 0x244}, + {0x45, 0xbd, 0x245}, + {0x46, 0xef, 0x75}, + {0x47, 0xca, 0x105}, + {0x48, 0x10c, 0x247}, + {0x49, 0xa1, 0x249}, + {0x4a, 0x9f, 0x254}, + {0x4b, 0xa0, 0x255}, + {0x4c, 0x22, 0x24a}, + {0x4d, 0xd6, 0x24e}, + {0x4e, 0x55, 0x24b}, + {0x4f, 0x56, 0x24c}, + {0x50, 0x57, 0x24d}, + {0x51, 0x58, 0x25d}, + {0x52, 0x59, 0x2cb}, + {0x53, 0x5a, 0x302}, + {0x54, 0x5b, 0x308}, + {0x55, 0x11a, 0x256}, + {0x56, 0x87, 0x100}, + {0x57, 0x3b, 0x259}, + {0x58, 0xed, 0x25e}, + {0x59, 0x24, 0x25f}, + {0x5a, 0x7b, 0x260}, + {0x5b, 0x7a, 0x309}, + {0x5c, 0x2d, 0x264}, + {0x5d, 8, 0x265}, + {0x5e, 0x53, 0x26b}, + {0x5f, 0x7c, 0x26e}, + {0x60, 6, 0x271}, + {0x61, 0x80, 0x273}, + {0x62, 7, 0x279}, + {99, 0x119, 0x27c}, + {100, 0xda, 0x1bf}, + {0x65, 0x98, 0x1dd}, + {0x66, 0x46, 0x10d}, + {0x67, 200, 0x10c}, + {0x68, 0x9c, 0xfb}, + {0x69, 0x71, 0x142}, + {0x6a, 0x117, 0x28e}, + {0x6b, 0x2f, 0x291}, + {0x6c, 0x68, 0x29c}, + {0x6d, 0xaa, 0x2a2}, + {0x6e, 0x6f, 0x2a9}, + {0x6f, 0xf9, 0x2aa}, + {0x70, 0xa9, 0x2b5}, + {0x71, 0x30, 0x2ca}, + {0x72, 0xfc, 0x29b}, + {0x73, 0x92, 0x2cc}, + {0x74, 0x112, 0x2cd}, + {0x75, 0x11b, 0x2d0}, + {0x76, 0xb2, 0x2d1}, + {0x77, 0x1d, 0x31a}, + {0x78, 0x116, 0x2d2}, + {0x79, 0xc5, 0x2d7}, + {0x7a, 0x36, 0x2d8}, + {0x7b, 0xc3, 10}, + {0x7c, 0xc2, 0x8e}, + {0x7d, 0x94, 0x92}, + {0x7e, 0x93, 0xa8}, + {0x7f, 0x9e, 0x2da}, + {0x80, 0xb0, 0x2e7}, + {0x81, 0xfa, 0x2e8}, + {0x82, 0x3a, 0x2e9}, + {0x83, 0x1f, 0x301}, + {0x84, 0x82, 0x2ea}, + {0x85, 0x83, 0x2eb}, + {0x86, 0x84, 0x2ec}, + {0x87, 0xd7, 0x2ee}, + {0x88, 0xbc, 0x2ef}, + {0x89, 0x37, 0x2ed}, + {0x8a, 0x88, 0x2f0}, + {0x8b, 0x85, 0x2f1}, + {0x8c, 0x78, 0x2ff}, + {0x8d, 0x3f, 0x300}, + {0x8e, 0xbb, 0x303}, + {0x8f, 0xa3, 0x44}, + {0x90, 0x70, 0x306}, + {0x91, 0xc9, 0x314}, + {0x92, 0x21, 0x315}, + {0x93, 0x6a, 0x31c}, + {0x94, 0x69, 0x31d}, + {0x95, 0x95, 0x31e}, + {0x96, 0x27, 799}, + {0x97, 0x35, 0x323}, + {0x98, 0x7f, 0x324}, + {0x99, 0x20, 0x32e}, + {0x9a, 0x1c, 0x330}, + {0x9b, 0xa2, 0x333}, + {0x9c, 0xd9, 0x334}, + {0x9d, 0xaf, 0x33e} +}; + +SpriteData parse_sprite(std::span data) { + // assert(asset.info.type == AssetType::SpriteData); + assert(data.size() >= 0x30); + auto ptr = (char*)data.data(); + + auto magic = *(uint32_t *)ptr; + assert(magic == 0x0003AC1D); + + SpriteData out; + out.composite_size.x = *(uint16_t *)(ptr + 4); + out.composite_size.y = *(uint16_t *)(ptr + 6); + out.layer_count = *(uint16_t *)(ptr + 8); + out.composition_count = *(uint16_t *)(ptr + 10); + out.subsprite_count = *(uint8_t *)(ptr + 12); + out.animation_count = *(uint8_t *)(ptr + 13); + + assert(data.size() >= 0x30 + out.animation_count * sizeof(SpriteAnimation) + out.layer_count * out.composition_count + out.subsprite_count * sizeof(SubSprite) + out.layer_count * sizeof(SpriteLayer)); + + ptr += 0x30; + + out.animations = {(SpriteAnimation *)ptr, ((SpriteAnimation *)ptr) + out.animation_count}; + ptr += out.animation_count * sizeof(SpriteAnimation); + + out.compositions = {ptr, ptr + out.layer_count * out.composition_count}; + ptr += out.layer_count * out.composition_count; + + out.sub_sprites = {(SubSprite *)ptr, ((SubSprite *)ptr) + out.subsprite_count}; + ptr += out.subsprite_count * sizeof(SubSprite); + + out.layers = {(SpriteLayer *)ptr, (SpriteLayer *)(ptr + out.layer_count * sizeof(SpriteLayer))}; + + return out; +} diff --git a/src/structures/map.hpp b/src/structures/map.hpp new file mode 100644 index 0000000..a57d4bf --- /dev/null +++ b/src/structures/map.hpp @@ -0,0 +1,154 @@ +#pragma once + +#include +#include +#include +#include + +#include + +struct MapHeader { + uint32_t signature1; + uint16_t roomCount; + + uint8_t world_wrap_x_start; + uint8_t world_wrap_x_end; + uint32_t idk3; // always 8 + + uint32_t signature2; +}; + +struct MapTile { + uint16_t tile_id; + uint8_t param; // depends on tile_id + + union { + struct { + bool horizontal_mirror : 1; + bool vertical_mirror : 1; + bool rotate_90 : 1; + bool rotate_180 : 1; + }; + uint8_t flags; + }; + + bool operator==(const MapTile& other) const { + return tile_id == other.tile_id && param == other.param && flags == other.flags; + } +}; +static_assert(sizeof(MapTile) == 4); + +struct Room { + uint8_t x; + uint8_t y; + + uint8_t bgId; + uint8_t waterLevel; + + uint8_t pallet_index; + uint8_t idk1; + uint8_t idk2; + uint8_t idk3; + + MapTile tiles[2][22][40]; +}; + +static_assert(sizeof(Room) == 0x1b88); + +class Map { + public: + uint8_t world_wrap_x_start; + uint8_t world_wrap_x_end; + + glm::ivec2 offset; + glm::ivec2 size; + std::vector rooms; + std::unordered_map coordinate_map; + + Map() = default; + + explicit Map(std::span data) { + assert(data.size() >= sizeof(MapHeader)); + auto head = *(MapHeader*)(data.data()); + assert(data.size() >= 0x10 + head.roomCount * sizeof(Room)); + + Room* rooms = (Room*)(data.data() + 0x10); + + world_wrap_x_start = head.world_wrap_x_start; + world_wrap_x_end = head.world_wrap_x_end; + this->rooms = {rooms, rooms + head.roomCount}; + + int x_min = 65535, x_max = 0; + int y_min = 65535, y_max = 0; + + for (int i = 0; i < head.roomCount; i++) { + auto& room = this->rooms[i]; + x_min = std::min(x_min, (int)room.x); + x_max = std::max(x_max, (int)room.x); + y_min = std::min(y_min, (int)room.y); + y_max = std::max(y_max, (int)room.y); + + coordinate_map[room.x | (room.y << 8)] = i; + } + + int width = x_max - x_min + 1; + int height = y_max - y_min + 1; + + offset = {x_min, y_min}; + size = {width, height}; + } + + Room* getRoom(int x, int y) { + if (x < 0 || x >= 256 || y < 0 || y >= 256) + return nullptr; + if (coordinate_map.contains(x | (y << 8))) { + return &rooms[coordinate_map.at(x | (y << 8))]; + } + return nullptr; + } + + std::optional getTile(int layer, int x, int y) const { + auto rx = x / 40; + auto ry = y / 22; + + if (rx < 0 || rx >= 256 || ry < 0 || ry >= 256) + return std::nullopt; + + if (coordinate_map.contains(rx | (ry << 8))) { + const auto& room = rooms[coordinate_map.at(rx | (ry << 8))]; + return room.tiles[layer][y % 22][x % 40]; + } + + return std::nullopt; + } + + void setTile(int layer, int x, int y, MapTile tile) { + auto rx = x / 40; + auto ry = y / 22; + + if (rx < 0 || rx >= 256 || ry < 0 || ry >= 256) + return; + + if (coordinate_map.contains(rx | (ry << 8))) { + auto& room = rooms[coordinate_map.at(rx | (ry << 8))]; + room.tiles[layer][y % 22][x % 40] = tile; + } + } + + auto save() const { + auto size = sizeof(MapHeader) + rooms.size() * sizeof(Room); + std::vector data(size); + + *(MapHeader*)data.data() = { + 0xF00DCAFE, + (uint16_t)rooms.size(), + world_wrap_x_start, + world_wrap_x_end, + 8, + 0xF0F0CAFE + }; + std::memcpy(data.data() + 0x10, rooms.data(), rooms.size() * sizeof(Room)); + + return data; + } +}; diff --git a/src/structures/tile.hpp b/src/structures/tile.hpp new file mode 100644 index 0000000..31de585 --- /dev/null +++ b/src/structures/tile.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +#include + +struct uv_data { + glm::u16vec2 pos; + glm::u16vec2 size; + + union { + struct { + bool collides_left : 1; + bool collides_right : 1; + bool collides_up : 1; + bool collides_down : 1; + + bool not_placeable : 1; + bool additive : 1; + + bool obscures : 1; + bool contiguous : 1; + bool blocks_light : 1; + bool self_contiguous : 1; + bool hidden : 1; + bool dirt : 1; + bool has_normals : 1; + bool uv_light : 1; + }; + uint16_t flags; + }; +}; +static_assert(sizeof(uv_data) == 10); + +std::vector parse_uvs(std::span data) { + assert(data.size() >= 0xC); + + auto magic = *(uint32_t *)data.data(); + assert(magic == 0x00B00B00); + auto count = *(uint32_t *)(data.data() + 4); + auto unused = *(uint32_t *)(data.data() + 8); // null in the given asset + + assert(data.size() >= 0xC + count * sizeof(uv_data)); + + auto ptr = (uv_data *)(data.data() + 0xC); + return std::vector(ptr, ptr + count); +} + +std::vector save_uvs(std::span uvs) { + std::vector data(0xC + uvs.size() * sizeof(uv_data)); + + *((uint32_t*)data.data() + 0) = 0x00B00B00; + *((uint32_t*)data.data() + 1) = uvs.size(); // 1024 + *((uint32_t*)data.data() + 2) = 0; + + std::memcpy(data.data() + 0xC, uvs.data(), uvs.size() * sizeof(uv_data)); + + return data; +}