From ca1e19946b781cb93032f11e6e5eacdd65bb888d Mon Sep 17 00:00:00 2001 From: Archez Date: Sun, 3 Mar 2024 19:02:15 -0500 Subject: [PATCH 1/3] Bring over exporter arg and scene changes (#19) --- ZAPD/Globals.h | 2 +- ZAPD/Main.cpp | 34 +++++++++++++++++++--------------- ZAPD/ZResource.cpp | 12 ++++++++++-- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/ZAPD/Globals.h b/ZAPD/Globals.h index 4cd8fe1..95c22c2 100644 --- a/ZAPD/Globals.h +++ b/ZAPD/Globals.h @@ -42,7 +42,7 @@ class Globals bool forceUnaccountedStatic = false; bool otrMode = true; bool buildRawTexture = false; - bool onlyGenSohOtr = false; + bool onlyGenCustomOtr = false; ZRom* rom = nullptr; std::vector files; diff --git a/ZAPD/Main.cpp b/ZAPD/Main.cpp index a388087..d045a94 100644 --- a/ZAPD/Main.cpp +++ b/ZAPD/Main.cpp @@ -162,14 +162,26 @@ extern "C" int zapd_main(int argc, char* argv[]) // Parse File Mode ExporterSet* exporterSet = Globals::Instance->GetExporterSet(); - - if(Globals::Instance->onlyGenSohOtr) { - exporterSet->endProgramFunc(); - delete g; - return 0; + // We've parsed through our commands once. If an exporter exists, it's been set by now. + // Now we'll parse through them again but pass them on to our exporter if one is available. + if (exporterSet != nullptr && exporterSet->parseArgsFunc != nullptr) { + for (int32_t i = 2; i < argc; i++) { + exporterSet->parseArgsFunc(argc, argv, i); + } } - + + if (Globals::Instance->onlyGenCustomOtr) { + if (exporterSet != nullptr) { + exporterSet->endProgramFunc(); + } else { + printf("Error: No exporter set, unable to make custom otr.\n"); + } + + delete g; + return 0; + } + std::string buildMode = argv[1]; ZFileMode fileMode = ParseFileMode(buildMode, exporterSet); @@ -184,14 +196,6 @@ extern "C" int zapd_main(int argc, char* argv[]) if (fileMode == ZFileMode::ExtractDirectory) Globals::Instance->rom = new ZRom(Globals::Instance->baseRomPath.string()); - // We've parsed through our commands once. If an exporter exists, it's been set by now. - // Now we'll parse through them again but pass them on to our exporter if one is available. - if (exporterSet != nullptr && exporterSet->parseArgsFunc != nullptr) - { - for (int32_t i = 2; i < argc; i++) - exporterSet->parseArgsFunc(argc, argv, i); - } - if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO) printf("ZAPD: Zelda Asset Processor For Decomp: %s\n", gBuildHash); @@ -572,7 +576,7 @@ void Arg_SetBuildRawTexture([[maybe_unused]] int& i, [[maybe_unused]] char* argv void Arg_SetNoRomMode([[maybe_unused]] int& i, [[maybe_unused]] char* argv[]) { - Globals::Instance->onlyGenSohOtr = true; + Globals::Instance->onlyGenCustomOtr = true; } int HandleExtract(ZFileMode fileMode, ExporterSet* exporterSet) diff --git a/ZAPD/ZResource.cpp b/ZAPD/ZResource.cpp index 394abd4..bbae4b1 100644 --- a/ZAPD/ZResource.cpp +++ b/ZAPD/ZResource.cpp @@ -332,8 +332,16 @@ std::string ZResource::GetSourceOutputHeader([[maybe_unused]] const std::string& std::string xmlPath = StringHelper::Replace(parent->GetXmlFilePath().string(), "\\", "/"); - if (StringHelper::Contains(outName, "_room_") || StringHelper::Contains(outName, "_scene")) - prefix = "scenes/nonmq"; + if (StringHelper::Contains(outName, "_room_") || StringHelper::Contains(outName, "_scene")) { + prefix = "scenes/shared"; + + // Regex for xml paths that are dungeons with unique MQ variants (only the main dungeon, not boss rooms) + std::regex dungeonsWithMQ(R"(((ydan)|(ddan)|(bdan)|(Bmori1)|(HIDAN)|(MIZUsin)|(jyasinzou)|(HAKAdan)|(HAKAdanCH)|(ice_doukutu)|(men)|(ganontika))\.xml)"); + + if (StringHelper::Contains(xmlPath, "dungeons/") && std::regex_search(xmlPath, dungeonsWithMQ)) { + prefix = "scenes/nonmq"; + } + } else if (StringHelper::Contains(xmlPath, "objects/")) prefix = "objects"; else if (StringHelper::Contains(xmlPath, "textures/")) From d7e991526585d12a26ac6e17e16541bcdf668cba Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya-ai@users.noreply.github.com> Date: Sun, 7 Apr 2024 16:27:58 -0400 Subject: [PATCH 2/3] support ripping out unused stuff (#20) (cherry picked from commit 9f9d0be3c914354ecabab3695581f1123c13ac20) --- ZAPD/ZAnimation.h | 1 - ZAPD/ZRoom/Commands/SetPathways.h | 1 - 2 files changed, 2 deletions(-) diff --git a/ZAPD/ZAnimation.h b/ZAPD/ZAnimation.h index 157fb34..a249037 100644 --- a/ZAPD/ZAnimation.h +++ b/ZAPD/ZAnimation.h @@ -3,7 +3,6 @@ #include #include #include -#include "Vec3s.h" #include "ZResource.h" #include "ZSkeleton.h" #include "tinyxml2.h" diff --git a/ZAPD/ZRoom/Commands/SetPathways.h b/ZAPD/ZRoom/Commands/SetPathways.h index 9abc93d..b9d2a2f 100644 --- a/ZAPD/ZRoom/Commands/SetPathways.h +++ b/ZAPD/ZRoom/Commands/SetPathways.h @@ -1,6 +1,5 @@ #pragma once -#include "Vec3s.h" #include "ZPath.h" #include "ZResource.h" #include "ZRoom/ZRoomCommand.h" From 15b7b3c8a799c861993de122795fa4931a329727 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya-ai@users.noreply.github.com> Date: Wed, 1 May 2024 22:25:33 -0400 Subject: [PATCH 3/3] detangle utils (#21) (cherry picked from commit 222cb8a23bd0fa8b901b1654ac5d6cc2ecb25a2e) --- ZAPD/CMakeLists.txt | 16 +-- ZAPD/Utils/BinaryReader.cpp | 178 ++++++++++++++++++++++++++++++ ZAPD/Utils/BinaryReader.h | 41 +++++++ ZAPD/Utils/BinaryWriter.cpp | 148 +++++++++++++++++++++++++ ZAPD/Utils/BinaryWriter.h | 41 +++++++ ZAPD/Utils/BitConverter.h | 209 ++++++++++++++++++++++++++++++++++++ ZAPD/Utils/Directory.h | 60 +++++++++++ ZAPD/Utils/DiskFile.h | 91 ++++++++++++++++ ZAPD/Utils/MemoryStream.cpp | 97 +++++++++++++++++ ZAPD/Utils/MemoryStream.h | 33 ++++++ ZAPD/Utils/Path.h | 50 +++++++++ ZAPD/Utils/Stream.h | 34 ++++++ ZAPD/Utils/StringHelper.cpp | 191 ++++++++++++++++++++++++++++++++ ZAPD/Utils/StringHelper.h | 33 ++++++ ZAPD/Utils/vt.h | 45 ++++++++ 15 files changed, 1259 insertions(+), 8 deletions(-) create mode 100644 ZAPD/Utils/BinaryReader.cpp create mode 100644 ZAPD/Utils/BinaryReader.h create mode 100644 ZAPD/Utils/BinaryWriter.cpp create mode 100644 ZAPD/Utils/BinaryWriter.h create mode 100644 ZAPD/Utils/BitConverter.h create mode 100644 ZAPD/Utils/Directory.h create mode 100644 ZAPD/Utils/DiskFile.h create mode 100644 ZAPD/Utils/MemoryStream.cpp create mode 100644 ZAPD/Utils/MemoryStream.h create mode 100644 ZAPD/Utils/Path.h create mode 100644 ZAPD/Utils/Stream.h create mode 100644 ZAPD/Utils/StringHelper.cpp create mode 100644 ZAPD/Utils/StringHelper.h create mode 100644 ZAPD/Utils/vt.h diff --git a/ZAPD/CMakeLists.txt b/ZAPD/CMakeLists.txt index 12be637..800fc20 100644 --- a/ZAPD/CMakeLists.txt +++ b/ZAPD/CMakeLists.txt @@ -234,6 +234,12 @@ set(Source_Files__Z64__ZRoom__Commands ) source_group("Source Files\\Z64\\ZRoom\\Commands" FILES ${Source_Files__Z64__ZRoom__Commands}) +file(GLOB Source_Files__Utils RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "Utils/*.c" "Utils/*.cpp") +source_group("Source Files\\Utils" FILES ${Source_Files__Utils}) + +file(GLOB Header_Files__Utils RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "Utils/*.h") +source_group("Header Files\\Utils" FILES ${Header_Files__Utils}) + set(ALL_FILES ${Header_Files} ${Header_Files__Libraries} @@ -242,6 +248,7 @@ set(ALL_FILES ${Header_Files__Z64} ${Header_Files__Z64__ZRoom} ${Header_Files__Z64__ZRoom__Commands} + ${Header_Files__Utils} ${Resource_Files} ${Source_Files} ${Source_Files__Libraries__libgfxd} @@ -249,6 +256,7 @@ set(ALL_FILES ${Source_Files__Z64} ${Source_Files__Z64__ZRoom} ${Source_Files__Z64__ZRoom__Commands} + ${Source_Files__Utils} ${any__any} ) @@ -339,10 +347,8 @@ endif() find_package(PNG REQUIRED) target_include_directories(${PROJECT_NAME} PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../../libultraship/extern/ZAPDUtils ${CMAKE_CURRENT_SOURCE_DIR}/../../libultraship/src/resource ${CMAKE_CURRENT_SOURCE_DIR}/../../libultraship/include - ${CMAKE_CURRENT_SOURCE_DIR}/../../ZAPDTR/lib/tinyxml2 ${CMAKE_CURRENT_SOURCE_DIR}/../../ZAPDTR/lib/libgfxd ${PNG_PNG_INCLUDE_DIR}/ . @@ -438,7 +444,6 @@ endif() ################################################################################ add_dependencies(${PROJECT_NAME} OTRExporter - ZAPDUtils libultraship ) @@ -446,7 +451,6 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows") if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x64") set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) set(ADDITIONAL_LIBRARY_DEPENDENCIES - "ZAPDUtils;" "-WHOLEARCHIVE:$/$" "libultraship;" storm @@ -457,7 +461,6 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) set(ADDITIONAL_LIBRARY_DEPENDENCIES - "ZAPDUtils;" -Wl,-force_load $/$ "libultraship;" PNG::PNG @@ -468,7 +471,6 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) set(ADDITIONAL_LIBRARY_DEPENDENCIES - "ZAPDUtils;" -Wl,--whole-archive $/$ -Wl,--no-whole-archive "libultraship;" PNG::PNG @@ -476,7 +478,6 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") ) elseif(CMAKE_SYSTEM_NAME STREQUAL "CafeOS") set(ADDITIONAL_LIBRARY_DEPENDENCIES - "ZAPDUtils;" "libultraship;" PNG::PNG ) @@ -484,7 +485,6 @@ else() set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) set(ADDITIONAL_LIBRARY_DEPENDENCIES - "ZAPDUtils;" -Wl,--whole-archive $/$ -Wl,--no-whole-archive "libultraship;" PNG::PNG diff --git a/ZAPD/Utils/BinaryReader.cpp b/ZAPD/Utils/BinaryReader.cpp new file mode 100644 index 0000000..2fdff37 --- /dev/null +++ b/ZAPD/Utils/BinaryReader.cpp @@ -0,0 +1,178 @@ +#include "BinaryReader.h" +#include +#include +#include "Stream.h" + +BinaryReader::BinaryReader(Stream* nStream) +{ + stream.reset(nStream); +} + +BinaryReader::BinaryReader(std::shared_ptr nStream) +{ + stream = nStream; +} + +void BinaryReader::Close() +{ + stream->Close(); +} + +void BinaryReader::SetEndianness(Endianness endianness) +{ + this->endianness = endianness; +} + +Endianness BinaryReader::GetEndianness() const +{ + return endianness; +} + +void BinaryReader::Seek(uint32_t offset, SeekOffsetType seekType) +{ + stream->Seek(offset, seekType); +} + +uint32_t BinaryReader::GetBaseAddress() +{ + return stream->GetBaseAddress(); +} + +void BinaryReader::Read(int32_t length) +{ + stream->Read(length); +} + +void BinaryReader::Read(char* buffer, int32_t length) +{ + stream->Read(buffer, length); +} + +char BinaryReader::ReadChar() +{ + return (char)stream->ReadByte(); +} + +int8_t BinaryReader::ReadByte() +{ + return stream->ReadByte(); +} + +uint8_t BinaryReader::ReadUByte() +{ + return (uint8_t)stream->ReadByte(); +} + +int16_t BinaryReader::ReadInt16() +{ + int16_t result = 0; + + stream->Read((char*)&result, sizeof(int16_t)); + + if (endianness != Endianness::Native) + result = BSWAP16(result); + + return result; +} + +int32_t BinaryReader::ReadInt32() +{ + int32_t result = 0; + + stream->Read((char*)&result, sizeof(int32_t)); + + if (endianness != Endianness::Native) + result = BSWAP32(result); + + return result; +} + +uint16_t BinaryReader::ReadUInt16() +{ + uint16_t result = 0; + + stream->Read((char*)&result, sizeof(uint16_t)); + + if (endianness != Endianness::Native) + result = BSWAP16(result); + + return result; +} + +uint32_t BinaryReader::ReadUInt32() +{ + uint32_t result = 0; + + stream->Read((char*)&result, sizeof(uint32_t)); + + if (endianness != Endianness::Native) + result = BSWAP32(result); + + return result; +} + +uint64_t BinaryReader::ReadUInt64() +{ + uint64_t result = 0; + + stream->Read((char*)&result, sizeof(uint64_t)); + + if (endianness != Endianness::Native) + result = BSWAP64(result); + + return result; +} + +float BinaryReader::ReadSingle() +{ + float result = NAN; + + stream->Read((char*)&result, sizeof(float)); + + if (endianness != Endianness::Native) + { + float tmp; + char* dst = (char*)&tmp; + char* src = (char*)&result; + dst[3] = src[0]; dst[2] = src[1]; dst[1] = src[2]; dst[0] = src[3]; + result = tmp; + } + + if (std::isnan(result)) + throw std::runtime_error("BinaryReader::ReadSingle(): Error reading stream"); + + return result; +} + +double BinaryReader::ReadDouble() +{ + double result = NAN; + + stream->Read((char*)&result, sizeof(double)); + + if (endianness != Endianness::Native) + { + double tmp; + char* dst = (char*)&tmp; + char* src = (char*)&result; + dst[7] = src[0]; dst[6] = src[1]; dst[5] = src[2]; dst[4] = src[3]; + dst[3] = src[4]; dst[2] = src[5]; dst[1] = src[6]; dst[0] = src[7]; + result = tmp; + } + + if (std::isnan(result)) + throw std::runtime_error("BinaryReader::ReadDouble(): Error reading stream"); + + return result; +} + +std::string BinaryReader::ReadString() +{ + std::string res; + int numChars = ReadInt32(); + + for (int i = 0; i < numChars; i++) + res += ReadChar(); + + return res; +} \ No newline at end of file diff --git a/ZAPD/Utils/BinaryReader.h b/ZAPD/Utils/BinaryReader.h new file mode 100644 index 0000000..f4b37c9 --- /dev/null +++ b/ZAPD/Utils/BinaryReader.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include +#include "BitConverter.h" +#include "Stream.h" + +class BinaryReader +{ +public: + BinaryReader(Stream* nStream); + BinaryReader(std::shared_ptr nStream); + + void Close(); + + void SetEndianness(Endianness endianness); + Endianness GetEndianness() const; + + void Seek(uint32_t offset, SeekOffsetType seekType); + uint32_t GetBaseAddress(); + + void Read(int32_t length); + void Read(char* buffer, int32_t length); + char ReadChar(); + int8_t ReadByte(); + int16_t ReadInt16(); + int32_t ReadInt32(); + uint8_t ReadUByte(); + uint16_t ReadUInt16(); + uint32_t ReadUInt32(); + uint64_t ReadUInt64(); + float ReadSingle(); + double ReadDouble(); + std::string ReadString(); + +protected: + std::shared_ptr stream; + Endianness endianness = Endianness::Native; +}; \ No newline at end of file diff --git a/ZAPD/Utils/BinaryWriter.cpp b/ZAPD/Utils/BinaryWriter.cpp new file mode 100644 index 0000000..34bcad0 --- /dev/null +++ b/ZAPD/Utils/BinaryWriter.cpp @@ -0,0 +1,148 @@ +#include "BinaryWriter.h" + +BinaryWriter::BinaryWriter(Stream* nStream) +{ + stream.reset(nStream); +} + +BinaryWriter::BinaryWriter(std::shared_ptr nStream) +{ + stream = nStream; +} + +void BinaryWriter::SetEndianness(Endianness endianness) +{ + this->endianness = endianness; +} + +void BinaryWriter::Close() +{ + stream->Close(); +} + +std::shared_ptr BinaryWriter::GetStream() +{ + return stream; +} + +uint64_t BinaryWriter::GetBaseAddress() +{ + return stream->GetBaseAddress(); +} + +uint64_t BinaryWriter::GetLength() +{ + return stream->GetLength(); +} + +void BinaryWriter::Seek(int32_t offset, SeekOffsetType seekType) +{ + stream->Seek(offset, seekType); +} + +void BinaryWriter::Write(int8_t value) +{ + stream->Write((char*)&value, sizeof(int8_t)); +} + +void BinaryWriter::Write(uint8_t value) +{ + stream->Write((char*)&value, sizeof(uint8_t)); +} + +void BinaryWriter::Write(int16_t value) +{ + if (endianness != Endianness::Native) + value = BSWAP16(value); + + stream->Write((char*)&value, sizeof(int16_t)); +} + +void BinaryWriter::Write(uint16_t value) +{ + if (endianness != Endianness::Native) + value = BSWAP16(value); + + stream->Write((char*)&value, sizeof(uint16_t)); +} + +void BinaryWriter::Write(int32_t value) +{ + if (endianness != Endianness::Native) + value = BSWAP32(value); + + stream->Write((char*)&value, sizeof(int32_t)); +} + +void BinaryWriter::Write(int32_t valueA, int32_t valueB) +{ + Write(valueA); + Write(valueB); +} + +void BinaryWriter::Write(uint32_t value) +{ + if (endianness != Endianness::Native) + value = BSWAP32(value); + + stream->Write((char*)&value, sizeof(uint32_t)); +} + +void BinaryWriter::Write(int64_t value) +{ + if (endianness != Endianness::Native) + value = BSWAP64(value); + + stream->Write((char*)&value, sizeof(int64_t)); +} + +void BinaryWriter::Write(uint64_t value) +{ + if (endianness != Endianness::Native) + value = BSWAP64(value); + + stream->Write((char*)&value, sizeof(uint64_t)); +} + +void BinaryWriter::Write(float value) +{ + if (endianness != Endianness::Native) + { + float tmp; + char* dst = (char*)&tmp; + char* src = (char*)&value; + dst[3] = src[0]; dst[2] = src[1]; dst[1] = src[2]; dst[0] = src[3]; + value = tmp; + } + + stream->Write((char*)&value, sizeof(float)); +} + +void BinaryWriter::Write(double value) +{ + if (endianness != Endianness::Native) + { + double tmp; + char* dst = (char*)&tmp; + char* src = (char*)&value; + dst[7] = src[0]; dst[6] = src[1]; dst[5] = src[2]; dst[4] = src[3]; + dst[3] = src[4]; dst[2] = src[5]; dst[1] = src[6]; dst[0] = src[7]; + value = tmp; + } + + stream->Write((char*)&value, sizeof(double)); +} + +void BinaryWriter::Write(const std::string& str) +{ + int strLen = str.size(); + Write(strLen); + + for (char c : str) + stream->WriteByte(c); +} + +void BinaryWriter::Write(char* srcBuffer, size_t length) +{ + stream->Write(srcBuffer, length); +} diff --git a/ZAPD/Utils/BinaryWriter.h b/ZAPD/Utils/BinaryWriter.h new file mode 100644 index 0000000..67c8fcd --- /dev/null +++ b/ZAPD/Utils/BinaryWriter.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include +#include "BitConverter.h" +#include "Stream.h" + +class BinaryWriter +{ +public: + BinaryWriter(Stream* nStream); + BinaryWriter(std::shared_ptr nStream); + + void SetEndianness(Endianness endianness); + + std::shared_ptr GetStream(); + uint64_t GetBaseAddress(); + uint64_t GetLength(); + void Seek(int32_t offset, SeekOffsetType seekType); + void Close(); + + void Write(int8_t value); + void Write(uint8_t value); + void Write(int16_t value); + void Write(uint16_t value); + void Write(int32_t value); + void Write(int32_t valueA, int32_t valueB); + void Write(uint32_t value); + void Write(int64_t value); + void Write(uint64_t value); + void Write(float value); + void Write(double value); + void Write(const std::string& str); + void Write(char* srcBuffer, size_t length); + +protected: + std::shared_ptr stream; + Endianness endianness = Endianness::Native; +}; \ No newline at end of file diff --git a/ZAPD/Utils/BitConverter.h b/ZAPD/Utils/BitConverter.h new file mode 100644 index 0000000..c90b3df --- /dev/null +++ b/ZAPD/Utils/BitConverter.h @@ -0,0 +1,209 @@ +#pragma once + +#include +#include +#include +#include + +#ifdef _MSC_VER +#define BSWAP16 _byteswap_ushort +#define BSWAP32 _byteswap_ulong +#define BSWAP64 _byteswap_uint64 +#else +#define BSWAP16 __builtin_bswap16 +#define BSWAP32 __builtin_bswap32 +#define BSWAP64 __builtin_bswap64 +#endif + +enum class Endianness +{ + Little = 0, + Big = 1, + +#if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) || defined(__BIG_ENDIAN__) + Native = Big, +#else + Native = Little, +#endif +}; + +class BitConverter +{ +public: + static inline int8_t ToInt8BE(const uint8_t* data, int32_t offset) + { + return (uint8_t)data[offset + 0]; + } + + static inline int8_t ToInt8BE(const std::vector& data, int32_t offset) + { + return (uint8_t)data[offset + 0]; + } + + static inline uint8_t ToUInt8BE(const uint8_t* data, int32_t offset) + { + return (uint8_t)data[offset + 0]; + } + + static inline uint8_t ToUInt8BE(const std::vector& data, int32_t offset) + { + return (uint8_t)data[offset + 0]; + } + + static inline int16_t ToInt16BE(const uint8_t* data, int32_t offset) + { + return ((uint16_t)data[offset + 0] << 8) + (uint16_t)data[offset + 1]; + } + + static inline int16_t ToInt16BE(const std::vector& data, int32_t offset) + { + return ((uint16_t)data[offset + 0] << 8) + (uint16_t)data[offset + 1]; + } + + static inline uint16_t ToUInt16BE(const uint8_t* data, int32_t offset) + { + return ((uint16_t)data[offset + 0] << 8) + (uint16_t)data[offset + 1]; + } + + static inline uint16_t ToUInt16BE(const std::vector& data, int32_t offset) + { + return ((uint16_t)data[offset + 0] << 8) + (uint16_t)data[offset + 1]; + } + + static inline int32_t ToInt32BE(const uint8_t* data, int32_t offset) + { + return ((uint32_t)data[offset + 0] << 24) + ((uint32_t)data[offset + 1] << 16) + + ((uint32_t)data[offset + 2] << 8) + (uint32_t)data[offset + 3]; + } + + static inline int32_t ToInt32BE(const std::vector& data, int32_t offset) + { + return ((uint32_t)data[offset + 0] << 24) + ((uint32_t)data[offset + 1] << 16) + + ((uint32_t)data[offset + 2] << 8) + (uint32_t)data[offset + 3]; + } + + static inline uint32_t ToUInt32BE(const uint8_t* data, int32_t offset) + { + return ((uint32_t)data[offset + 0] << 24) + ((uint32_t)data[offset + 1] << 16) + + ((uint32_t)data[offset + 2] << 8) + (uint32_t)data[offset + 3]; + } + + static inline uint32_t ToUInt32BE(const std::vector& data, int32_t offset) + { + return ((uint32_t)data[offset + 0] << 24) + ((uint32_t)data[offset + 1] << 16) + + ((uint32_t)data[offset + 2] << 8) + (uint32_t)data[offset + 3]; + } + + static inline int64_t ToInt64BE(const uint8_t* data, int32_t offset) + { + return ((uint64_t)data[offset + 0] << 56) + ((uint64_t)data[offset + 1] << 48) + + ((uint64_t)data[offset + 2] << 40) + ((uint64_t)data[offset + 3] << 32) + + ((uint64_t)data[offset + 4] << 24) + ((uint64_t)data[offset + 5] << 16) + + ((uint64_t)data[offset + 6] << 8) + ((uint64_t)data[offset + 7]); + } + + static inline int64_t ToInt64BE(const std::vector& data, int32_t offset) + { + return ((uint64_t)data[offset + 0] << 56) + ((uint64_t)data[offset + 1] << 48) + + ((uint64_t)data[offset + 2] << 40) + ((uint64_t)data[offset + 3] << 32) + + ((uint64_t)data[offset + 4] << 24) + ((uint64_t)data[offset + 5] << 16) + + ((uint64_t)data[offset + 6] << 8) + ((uint64_t)data[offset + 7]); + } + + static inline uint64_t ToUInt64BE(const uint8_t* data, int32_t offset) + { + return ((uint64_t)data[offset + 0] << 56) + ((uint64_t)data[offset + 1] << 48) + + ((uint64_t)data[offset + 2] << 40) + ((uint64_t)data[offset + 3] << 32) + + ((uint64_t)data[offset + 4] << 24) + ((uint64_t)data[offset + 5] << 16) + + ((uint64_t)data[offset + 6] << 8) + ((uint64_t)data[offset + 7]); + } + + static inline uint64_t ToUInt64BE(const std::vector& data, int32_t offset) + { + return ((uint64_t)data[offset + 0] << 56) + ((uint64_t)data[offset + 1] << 48) + + ((uint64_t)data[offset + 2] << 40) + ((uint64_t)data[offset + 3] << 32) + + ((uint64_t)data[offset + 4] << 24) + ((uint64_t)data[offset + 5] << 16) + + ((uint64_t)data[offset + 6] << 8) + ((uint64_t)data[offset + 7]); + } + + static inline float ToFloatBE(const uint8_t* data, int32_t offset) + { + float value; + uint32_t floatData = ((uint32_t)data[offset + 0] << 24) + + ((uint32_t)data[offset + 1] << 16) + + ((uint32_t)data[offset + 2] << 8) + (uint32_t)data[offset + 3]; + static_assert(sizeof(uint32_t) == sizeof(float), "expected 32-bit float"); + std::memcpy(&value, &floatData, sizeof(value)); + return value; + } + + static inline float ToFloatBE(const std::vector& data, int32_t offset) + { + float value; + uint32_t floatData = ((uint32_t)data[offset + 0] << 24) + + ((uint32_t)data[offset + 1] << 16) + + ((uint32_t)data[offset + 2] << 8) + (uint32_t)data[offset + 3]; + static_assert(sizeof(uint32_t) == sizeof(float), "expected 32-bit float"); + std::memcpy(&value, &floatData, sizeof(value)); + return value; + } + + static inline double ToDoubleBE(const uint8_t* data, int32_t offset) + { + double value; + uint64_t floatData = + ((uint64_t)data[offset + 0] << 56) + ((uint64_t)data[offset + 1] << 48) + + ((uint64_t)data[offset + 2] << 40) + ((uint64_t)data[offset + 3] << 32) + + ((uint64_t)data[offset + 4] << 24) + ((uint64_t)data[offset + 5] << 16) + + ((uint64_t)data[offset + 6] << 8) + ((uint64_t)data[offset + 7]); + static_assert(sizeof(uint64_t) == sizeof(double), "expected 64-bit double"); + // Checks if the float format on the platform the ZAPD binary is running on supports the + // same float format as the object file. + static_assert(std::numeric_limits::is_iec559, + "expected IEC559 floats on host machine"); + std::memcpy(&value, &floatData, sizeof(value)); + return value; + } + + static inline double ToDoubleBE(const std::vector& data, int32_t offset) + { + double value; + uint64_t floatData = + ((uint64_t)data[offset + 0] << 56) + ((uint64_t)data[offset + 1] << 48) + + ((uint64_t)data[offset + 2] << 40) + ((uint64_t)data[offset + 3] << 32) + + ((uint64_t)data[offset + 4] << 24) + ((uint64_t)data[offset + 5] << 16) + + ((uint64_t)data[offset + 6] << 8) + ((uint64_t)data[offset + 7]); + static_assert(sizeof(uint64_t) == sizeof(double), "expected 64-bit double"); + // Checks if the float format on the platform the ZAPD binary is running on supports the + // same float format as the object file. + static_assert(std::numeric_limits::is_iec559, + "expected IEC559 doubles on host machine"); + std::memcpy(&value, &floatData, sizeof(value)); + return value; + } + + // Rewrites the rom data in-place to be in BigEndian/z64 format + static inline void RomToBigEndian(uint8_t* rom, size_t romSize) { + if (romSize <= 0) { + return; + } + + // Use the first byte to determine byte order + uint8_t firstByte = rom[0]; + + switch (firstByte) { + case 0x37: // v64 + for (size_t pos = 0; pos < (romSize / 2); pos++) { + ((uint16_t*)rom)[pos] = ToUInt16BE(rom, pos * 2); + } + break; + case 0x40: // n64 + for (size_t pos = 0; pos < (romSize / 4); pos++) { + ((uint32_t*)rom)[pos] = ToUInt32BE(rom, pos * 4); + } + break; + case 0x80: // z64 + break; // Already BE, no need to swap + } + } +}; diff --git a/ZAPD/Utils/Directory.h b/ZAPD/Utils/Directory.h new file mode 100644 index 0000000..ea792d8 --- /dev/null +++ b/ZAPD/Utils/Directory.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include "StringHelper.h" +#include + +#if __has_include() +#include +namespace fs = std::filesystem; +#else +#include +namespace fs = std::experimental::filesystem; +#endif + +#undef GetCurrentDirectory +#undef CreateDirectory + +class Directory +{ +public: + #ifndef PATH_HACK + static std::string GetCurrentDirectory() { return fs::current_path().string(); } + #endif + + static bool Exists(const fs::path& path) { return fs::exists(path); } + + // Stupid hack because of Windows.h + static void MakeDirectory(const std::string& path) + { + CreateDirectory(path); + } + + static void CreateDirectory(const std::string& path) + { + try + { + fs::create_directories(path); + } + catch (...) + { + } + } + + static std::vector ListFiles(const std::string& dir) + { + std::vector lst; + + if (Exists(dir)) + { + for (auto& p : fs::recursive_directory_iterator(dir)) + { + if (!p.is_directory()) + lst.push_back(p.path().generic_string()); + } + } + + return lst; + } +}; diff --git a/ZAPD/Utils/DiskFile.h b/ZAPD/Utils/DiskFile.h new file mode 100644 index 0000000..d3ec03c --- /dev/null +++ b/ZAPD/Utils/DiskFile.h @@ -0,0 +1,91 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "Path.h" +#include "Utils/StringHelper.h" +#include "Utils/Directory.h" + + +class DiskFile +{ +public: + static bool Exists(const fs::path& filePath) + { + std::ifstream file(filePath, std::ios::in | std::ios::binary | std::ios::ate); + return file.good(); + } + + static std::vector ReadAllBytes(const fs::path& filePath) + { + std::ifstream file(filePath, std::ios::in | std::ios::binary | std::ios::ate); + + if (!file) + return std::vector(); + + int32_t fileSize = (int32_t)file.tellg(); + file.seekg(0); + char* data = new char[fileSize]; + file.read(data, fileSize); + std::vector result = std::vector(data, data + fileSize); + delete[] data; + + return result; + }; + + static std::string ReadAllText(const fs::path& filePath) + { + std::ifstream file(filePath, std::ios::in | std::ios::binary | std::ios::ate); + int32_t fileSize = (int32_t)file.tellg(); + file.seekg(0); + char* data = new char[fileSize + 1]; + memset(data, 0, fileSize + 1); + file.read(data, fileSize); + std::string str = std::string((const char*)data); + delete[] data; + + return str; + }; + + static std::vector ReadAllLines(const fs::path& filePath) + { + std::string text = ReadAllText(filePath); + std::vector lines = StringHelper::Split(text, "\n"); + + return lines; + }; + + static void WriteAllBytes(const fs::path& filePath, const std::vector& data) + { + std::ofstream file(filePath, std::ios::binary); + file.write((char*)data.data(), data.size()); + }; + + static void WriteAllBytes(const std::string& filePath, const std::vector& data) + { + if (!Directory::Exists(Path::GetDirectoryName(filePath))) { + Directory::MakeDirectory(Path::GetDirectoryName(filePath).string()); + } + + std::ofstream file(filePath, std::ios::binary); + file.write((char*)data.data(), data.size()); + }; + + static void WriteAllBytes(const std::string& filePath, const char* data, int dataSize) + { + std::ofstream file(filePath, std::ios::binary); + file.write((char*)data, dataSize); + }; + + static void WriteAllText(const fs::path& filePath, const std::string& text) + { + if (!Directory::Exists(Path::GetDirectoryName(filePath))) { + Directory::MakeDirectory(Path::GetDirectoryName(filePath).string()); + } + std::ofstream file(filePath, std::ios::out); + file.write(text.c_str(), text.size()); + } +}; diff --git a/ZAPD/Utils/MemoryStream.cpp b/ZAPD/Utils/MemoryStream.cpp new file mode 100644 index 0000000..1c70c00 --- /dev/null +++ b/ZAPD/Utils/MemoryStream.cpp @@ -0,0 +1,97 @@ +#include "MemoryStream.h" +#include + +#ifndef _MSC_VER +#define memcpy_s(dest, destSize, source, sourceSize) memcpy(dest, source, destSize) +#endif + +MemoryStream::MemoryStream() +{ + buffer = std::vector(); + //buffer.reserve(1024 * 16); + bufferSize = 0; + baseAddress = 0; +} + +MemoryStream::MemoryStream(char* nBuffer, size_t nBufferSize) : MemoryStream() +{ + buffer = std::vector(nBuffer, nBuffer + nBufferSize); + bufferSize = nBufferSize; + baseAddress = 0; +} + +MemoryStream::~MemoryStream() +{ +} + +uint64_t MemoryStream::GetLength() +{ + return buffer.size(); +} + +void MemoryStream::Seek(int32_t offset, SeekOffsetType seekType) +{ + if (seekType == SeekOffsetType::Start) + baseAddress = offset; + else if (seekType == SeekOffsetType::Current) + baseAddress += offset; + else if (seekType == SeekOffsetType::End) + baseAddress = bufferSize - 1 - offset; +} + +std::unique_ptr MemoryStream::Read(size_t length) +{ + std::unique_ptr result = std::make_unique(length); + + memcpy_s(result.get(), length, &buffer[baseAddress], length); + baseAddress += length; + + return result; +} + +void MemoryStream::Read(const char* dest, size_t length) +{ + memcpy_s((void*)dest, length, &buffer[baseAddress], length); + baseAddress += length; +} + +int8_t MemoryStream::ReadByte() +{ + return buffer[baseAddress++]; +} + +void MemoryStream::Write(char* srcBuffer, size_t length) +{ + if (baseAddress + length >= buffer.size()) + { + buffer.resize(baseAddress + length); + bufferSize += length; + } + + memcpy_s(&buffer[baseAddress], length, srcBuffer, length); + baseAddress += length; +} + +void MemoryStream::WriteByte(int8_t value) +{ + if (baseAddress >= buffer.size()) + { + buffer.resize(baseAddress + 1); + bufferSize = baseAddress; + } + + buffer[baseAddress++] = value; +} + +std::vector MemoryStream::ToVector() +{ + return buffer; +} + +void MemoryStream::Flush() +{ +} + +void MemoryStream::Close() +{ +} \ No newline at end of file diff --git a/ZAPD/Utils/MemoryStream.h b/ZAPD/Utils/MemoryStream.h new file mode 100644 index 0000000..5a17bb0 --- /dev/null +++ b/ZAPD/Utils/MemoryStream.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include "Stream.h" + +class MemoryStream : public Stream +{ +public: + MemoryStream(); + MemoryStream(char* nBuffer, size_t nBufferSize); + ~MemoryStream(); + + uint64_t GetLength() override; + + void Seek(int32_t offset, SeekOffsetType seekType) override; + + std::unique_ptr Read(size_t length) override; + void Read(const char* dest, size_t length) override; + int8_t ReadByte() override; + + void Write(char* srcBuffer, size_t length) override; + void WriteByte(int8_t value) override; + + std::vector ToVector(); + + void Flush() override; + void Close() override; + +protected: + std::vector buffer; + std::size_t bufferSize; +}; \ No newline at end of file diff --git a/ZAPD/Utils/Path.h b/ZAPD/Utils/Path.h new file mode 100644 index 0000000..0f7ef27 --- /dev/null +++ b/ZAPD/Utils/Path.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include "Utils/StringHelper.h" + +#if __has_include() +#include +namespace fs = std::filesystem; +#else +#include +namespace fs = std::experimental::filesystem; +#endif + +class Path +{ +public: + static std::string GetFileName(const fs::path& input) + { + // https://en.cppreference.com/w/cpp/filesystem/path/filename + return input.filename().string(); + }; + + static std::string GetFileNameWithoutExtension(const fs::path& input) + { + // https://en.cppreference.com/w/cpp/filesystem/path/stem + return input.stem().string(); + }; + + static std::string GetFileNameExtension(const std::string& input) + { + return input.substr(input.find_last_of("."), input.length()); + }; + + static fs::path GetPath(const std::string& input) + { + std::vector split = StringHelper::Split(input, "/"); + fs::path output; + + for (std::string str : split) + { + if (str.find_last_of(".") == std::string::npos) + output /= str; + } + + return output; + }; + + static fs::path GetDirectoryName(const fs::path& path) { return path.parent_path(); }; +}; diff --git a/ZAPD/Utils/Stream.h b/ZAPD/Utils/Stream.h new file mode 100644 index 0000000..e72d794 --- /dev/null +++ b/ZAPD/Utils/Stream.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +enum class SeekOffsetType +{ + Start, + Current, + End +}; + +class Stream +{ +public: + virtual ~Stream() = default; + virtual uint64_t GetLength() = 0; + uint64_t GetBaseAddress() { return baseAddress; } + + virtual void Seek(int32_t offset, SeekOffsetType seekType) = 0; + + virtual std::unique_ptr Read(size_t length) = 0; + virtual void Read(const char* dest, size_t length) = 0; + virtual int8_t ReadByte() = 0; + + virtual void Write(char* destBuffer, size_t length) = 0; + virtual void WriteByte(int8_t value) = 0; + + virtual void Flush() = 0; + virtual void Close() = 0; + +protected: + uint64_t baseAddress; +}; \ No newline at end of file diff --git a/ZAPD/Utils/StringHelper.cpp b/ZAPD/Utils/StringHelper.cpp new file mode 100644 index 0000000..c55580e --- /dev/null +++ b/ZAPD/Utils/StringHelper.cpp @@ -0,0 +1,191 @@ +#include "StringHelper.h" + +#if (_MSC_VER) +#pragma optimize("2", on) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#ifndef _MSC_VER +#define vsprintf_s vsprintf +#endif + +std::vector StringHelper::Split(std::string s, const std::string& delimiter) +{ + size_t pos_start = 0, pos_end, delim_len = delimiter.length(); + std::string token; + std::vector res; + + while ((pos_end = s.find(delimiter, pos_start)) != std::string::npos) { + token = s.substr(pos_start, pos_end - pos_start); + pos_start = pos_end + delim_len; + res.push_back(token); + } + + res.push_back(s.substr(pos_start)); + return res; +} + +std::vector StringHelper::Split(std::string_view s, const std::string& delimiter) +{ + size_t pos_start = 0, pos_end, delim_len = delimiter.length(); + std::string_view token; + std::vector res; + + while ((pos_end = s.find(delimiter, pos_start)) != std::string_view::npos) + { + token = s.substr(pos_start, pos_end - pos_start); + pos_start = pos_end + delim_len; + res.push_back(token); + } + + res.push_back(s.substr(pos_start)); + return res; +} + +std::string StringHelper::Strip(std::string s, const std::string& delimiter) +{ + size_t pos = 0; + std::string token; + + while ((pos = s.find(delimiter)) != std::string::npos) + { + token = s.substr(0, pos); + s.erase(pos, pos + delimiter.length()); + } + + return s; +} + +std::string StringHelper::Replace(std::string str, const std::string& from, + const std::string& to) +{ + size_t start_pos = str.find(from); + + while (start_pos != std::string::npos) + { + str.replace(start_pos, from.length(), to); + start_pos = str.find(from); + } + + return str; +} + +void StringHelper::ReplaceOriginal(std::string& str, const std::string& from, const std::string& to) +{ + size_t start_pos = str.find(from); + + while (start_pos != std::string::npos) + { + str.replace(start_pos, from.length(), to); + start_pos = str.find(from); + } +} + +bool StringHelper::StartsWith(const std::string& s, const std::string& input) +{ +#if __cplusplus >= 202002L + return s.starts_with(input.c_str()); +#else + return s.rfind(input, 0) == 0; +#endif +} + +bool StringHelper::Contains(const std::string& s, const std::string& input) +{ + return s.find(input) != std::string::npos; +} + +bool StringHelper::EndsWith(const std::string& s, const std::string& input) +{ + size_t inputLen = strlen(input.c_str()); + return s.rfind(input) == (s.size() - inputLen); +} + +std::string StringHelper::Sprintf(const char* format, ...) +{ + char buffer[32768]; + // char buffer[2048]; + std::string output; + va_list va; + + va_start(va, format); + vsprintf_s(buffer, format, va); + va_end(va); + + output = buffer; + return output; +} + +std::string StringHelper::Implode(std::vector& elements, + const char* const separator) +{ + return ""; + + // return std::accumulate(std::begin(elements), std::end(elements), std::string(), + //[separator](std::string& ss, std::string& s) { + // return ss.empty() ? s : ss + separator + s; + //}); +} + +int64_t StringHelper::StrToL(const std::string& str, int32_t base) +{ + return std::strtoull(str.c_str(), nullptr, base); +} + +std::string StringHelper::BoolStr(bool b) +{ + return b ? "true" : "false"; +} + +bool StringHelper::HasOnlyDigits(const std::string& str) +{ + return std::all_of(str.begin(), str.end(), ::isdigit); +} + +// Validate a hex string based on the c89 standard +// https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html#Integer-Constants +bool StringHelper::IsValidHex(std::string_view str) +{ + if (str.length() < 3) + { + return false; + } + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) + { + return std::all_of(str.begin() + 2, str.end(), ::isxdigit); + } + return false; +} + + +bool StringHelper::IsValidHex(const std::string& str) +{ + return IsValidHex(std::string_view(str.c_str())); +} + +bool StringHelper::IsValidOffset(std::string_view str) +{ + if (str.length() == 1) + { + // 0 is a valid offset + return isdigit(str[0]); + } + return IsValidHex(str); +} + +bool StringHelper::IsValidOffset(const std::string& str) +{ + if (str.length() == 1) + { + // 0 is a valid offset + return isdigit(str[0]); + } + return IsValidHex(str); +} + + +bool StringHelper::IEquals(const std::string& a, const std::string& b) +{ + return std::equal(a.begin(), a.end(), b.begin(), b.end(), + [](char a, char b) { return tolower(a) == tolower(b); }); +} diff --git a/ZAPD/Utils/StringHelper.h b/ZAPD/Utils/StringHelper.h new file mode 100644 index 0000000..0fc58d3 --- /dev/null +++ b/ZAPD/Utils/StringHelper.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +class StringHelper +{ +public: + static std::vector Split(std::string s, const std::string& delimiter); + static std::vector Split(std::string_view s, const std::string& delimiter); + static std::string Strip(std::string s, const std::string& delimiter); + static std::string Replace(std::string str, const std::string& from, const std::string& to); + static void ReplaceOriginal(std::string& str, const std::string& from, const std::string& to); + static bool StartsWith(const std::string& s, const std::string& input); + static bool Contains(const std::string& s, const std::string& input); + static bool EndsWith(const std::string& s, const std::string& input); + static std::string Sprintf(const char* format, ...); + static std::string Implode(std::vector& elements, const char* const separator); + static int64_t StrToL(const std::string& str, int32_t base = 10); + static std::string BoolStr(bool b); + static bool HasOnlyDigits(const std::string& str); + static bool IsValidHex(std::string_view str); + static bool IsValidHex(const std::string& str); + static bool IsValidOffset(std::string_view str); + static bool IsValidOffset(const std::string& str); + static bool IEquals(const std::string& a, const std::string& b); +}; \ No newline at end of file diff --git a/ZAPD/Utils/vt.h b/ZAPD/Utils/vt.h new file mode 100644 index 0000000..23f4244 --- /dev/null +++ b/ZAPD/Utils/vt.h @@ -0,0 +1,45 @@ +#ifndef VT_H +#define VT_H + +// clang-format off +#define VT_COLOR_BLACK 0 +#define VT_COLOR_RED 1 +#define VT_COLOR_GREEN 2 +#define VT_COLOR_YELLOW 3 +#define VT_COLOR_BLUE 4 +#define VT_COLOR_PURPLE 5 +#define VT_COLOR_CYAN 6 +#define VT_COLOR_WHITE 7 +#define VT_COLOR_LIGHTGRAY 8 +#define VT_COLOR_DARKGRAY 9 + +#define VT_COLOR_FOREGROUND 3 +#define VT_COLOR_BACKGROUND 4 +// clang-format on + +#define VT_COLOR_EXPAND0(type, color) #type #color +#define VT_COLOR_EXPAND1(type, color) VT_COLOR_EXPAND0(type, color) +#define VT_COLOR(type, color) VT_COLOR_EXPAND1(VT_COLOR_##type, VT_COLOR_##color) + +#define VT_ESC "\x1b" +#define VT_CSI "[" +#define VT_CUP(x, y) VT_ESC VT_CSI y ";" x "H" +#define VT_ED(n) VT_ESC VT_CSI #n "J" +#define VT_SGR(n) VT_ESC VT_CSI n "m" + +// Add more macros if necessary +#define VT_COL(back, fore) VT_SGR(VT_COLOR(BACKGROUND, back) ";" VT_COLOR(FOREGROUND, fore)) +#define VT_FGCOL(color) VT_SGR(VT_COLOR(FOREGROUND, color)) +#define VT_BGCOL(color) VT_SGR(VT_COLOR(BACKGROUND, color)) + +// Bold +#define VT_BOLD "1" + +// Bold color support +#define VT_BOLD_FGCOL(color) VT_SGR(VT_BOLD ";" VT_COLOR(FOREGROUND, color)) +#define VT_BOLD_BGCOL(color) VT_SGR(VT_BOLD ";" VT_COLOR(BACKGROUND, color)) + +#define VT_RST VT_SGR("") +#define VT_CLS VT_ED(2) + +#endif