diff --git a/.github/workflows/master-release.yml b/.github/workflows/master-release.yml index a94a596..fe94e0f 100644 --- a/.github/workflows/master-release.yml +++ b/.github/workflows/master-release.yml @@ -40,4 +40,5 @@ jobs: generate_release_notes: true files: | ${{github.workspace}}/build/gifscript + ${{github.workspace}}/build/tpircsfig tag_name: ${{ steps.tag_version.outputs.new_tag }} diff --git a/CMakeLists.txt b/CMakeLists.txt index b974f1b..c907892 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,12 +16,15 @@ endif() find_package(RAGEL REQUIRED) -if(WIN32) - find_program(LEMON-PARSER "lemon" REQUIRED) - if(NOT ${LEMON-PARSER} STREQUAL LEMON-PARSER-NOTFOUND) - message("lemon parser (lemon.exe) not found. If using VCPKG install lemon-parser-generator and add `$VCPKG_ROOT\\packages\\lemon-parser-generator_x64-windows\\tools\\lemon` (or wherever lemon.exe is) to your $PATH.") - endif() -endif() +set(CORE_DIR ${CMAKE_SOURCE_DIR}/core) +set(CORE_INCLUDE ${CORE_DIR}/include) +set(CORE_SRC ${CORE_DIR}/src) + +set(BACKEND_DIR ${CMAKE_SOURCE_DIR}/backends) +set(BACKEND_INCLUDE ${BACKEND_DIR}/include) +set(BACKEND_SRC ${BACKEND_DIR}/src) + +set(FRONTEND_DIR ${CMAKE_SOURCE_DIR}/frontends) include(FetchContent) @@ -31,78 +34,75 @@ FetchContent_Declare(fmt ) FetchContent_MakeAvailable(fmt) -if(NOT WIN32) find_program(CLANG_TIDY_EXE NAMES "clang-tidy") - if(CLANG_TIDY_EXE AND NOT DISABLE_CLANG_TIDY AND NOT WIN32) - set(CMAKE_CXX_CLANG_TIDY clang-tidy -checks=-*,readability-*,modernize-*,performance-*,portability-*,bugprone-*,clang-analyzer-*) - endif() +if(CLANG_TIDY_EXE AND NOT DISABLE_CLANG_TIDY) + set(CMAKE_CXX_CLANG_TIDY clang-tidy -checks=-*,readability-*,modernize-*,performance-*,portability-*,bugprone-*,clang-analyzer-*) endif() RAGEL_TARGET(gifscript - ${CMAKE_SOURCE_DIR}/gifscript.rl + ${FRONTEND_DIR}/gifscript.rl ${CMAKE_CURRENT_BINARY_DIR}/gifscript.cpp COMPILE_FLAGS -G2 ) set(GENERATED_SOURCES - parser.h - parser.c + ${CMAKE_CURRENT_BINARY_DIR}/parser.h + ${CMAKE_CURRENT_BINARY_DIR}/parser.c ${RAGEL_gifscript_OUTPUTS} ) set(BACKEND_SOURCES - backend/backend.hpp - backend/c_code.cpp - backend/c_code.h + ${BACKEND_INCLUDE}/backend.hpp + ${BACKEND_INCLUDE}/c_code.hpp + ${BACKEND_INCLUDE}/gifscript_backend.hpp + ${BACKEND_SRC}/c_code.cpp + ${BACKEND_SRC}/gifscript_backend.cpp ) set(CORE_SOURCES - version.h - logger.h - machine.h - machine.cpp - registers.h - registers.cpp - ) + ${CORE_INCLUDE}/version.hpp + ${CORE_INCLUDE}/logger.hpp + ${CORE_INCLUDE}/machine.hpp + ${CORE_INCLUDE}/registers.hpp + ${CORE_SRC}/machine.cpp + ${CORE_SRC}/registers.cpp +) + +add_library(gifscript_core ${BACKEND_SOURCES} ${CORE_SOURCES} ${GENERATED_SOURCES}) +target_include_directories(gifscript_core PRIVATE ${CORE_INCLUDE} ${BACKEND_INCLUDE}) -add_executable(gifscript ${BACKEND_SOURCES} ${CORE_SOURCES} ${GENERATED_SOURCES}) +add_executable(gifscript gifscript.cpp) +target_include_directories(gifscript PRIVATE ${CORE_INCLUDE} ${BACKEND_INCLUDE}) +target_link_libraries(gifscript PRIVATE gifscript_core) -set_source_files_properties(${GENERATED_SOURCES} PROPERTIES +add_executable(tpircsfig ${FRONTEND_DIR}/tpircsfig.cpp) +target_include_directories(tpircsfig PRIVATE ${CORE_INCLUDE} ${BACKEND_INCLUDE} ${CMAKE_BINARY_DIR}) +target_link_libraries(tpircsfig PRIVATE gifscript_core) + +# DISABLING LINTING FOR WIP TPIRCSFIG FRONTEND FOR NOW +set_source_files_properties(${GENERATED_SOURCES} ${FRONTEND_DIR}/tpircsfig.cpp PROPERTIES SKIP_LINTING ON ) -if(WIN32) - target_compile_options(gifscript PRIVATE /std:c++latest) -else() - execute_process( - COMMAND git describe --tags --abbrev=4 --always - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE GIT_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE +execute_process( + COMMAND git describe --tags --abbrev=4 --always + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE ) - string(CONCAT GIT_VERSION "\"" ${GIT_VERSION} "\"") - message("git version: ${GIT_VERSION}") - target_compile_options(gifscript PRIVATE -DGIT_VERSION=${GIT_VERSION} -Wall -Werror -Wno-unused-const-variable ${CPP_23_ARG}) -endif() - -target_include_directories(gifscript PRIVATE ${CMAKE_SOURCE_DIR}) -target_include_directories(gifscript PRIVATE ${fmt_SOURCE_DIR}/include) -target_link_libraries(gifscript PRIVATE fmt::fmt) +string(CONCAT GIT_VERSION "\"" ${GIT_VERSION} "\"") +message("git version: ${GIT_VERSION}") +target_compile_options(gifscript_core PRIVATE -Wall -Werror -Wno-unused-const-variable ${CPP_23_ARG}) +target_compile_options(gifscript PRIVATE -DGIT_VERSION=${GIT_VERSION} -Wall -Werror -Wno-unused-const-variable ${CPP_23_ARG}) +target_compile_options(tpircsfig PRIVATE -DGIT_VERSION=${GIT_VERSION} -Wall -Werror -Wno-unused-const-variable ${CPP_23_ARG}) -# CMake on windows causes issues with the lemon parser template file -# We need to get the location of the current lemon parser executable -# And (on vcpkg at least) the location of the template file is in the same -# discord as the lemon executable - -if(WIN32) - get_filename_component(LEMON_PARSER_DIR ${LEMON-PARSER} DIRECTORY) - set(LEMON_PARSER_TEMPLATE "-T${LEMON_PARSER_DIR}/lempar.c") -endif() +target_include_directories(gifscript PUBLIC ${fmt_SOURCE_DIR}/include) +target_link_libraries(gifscript_core PUBLIC fmt::fmt) add_custom_command( OUTPUT parser.c parser.h - COMMAND lemon -q ${CMAKE_SOURCE_DIR}/parser.y -d${CMAKE_CURRENT_BINARY_DIR} ${LEMON_PARSER_TEMPLATE} - DEPENDS ${CMAKE_SOURCE_DIR}/parser.y + COMMAND lemon -q ${CORE_SRC}/parser.y -d${CMAKE_CURRENT_BINARY_DIR} ${LEMON_PARSER_TEMPLATE} + DEPENDS ${CORE_SRC}/parser.y USES_TERMINAL ) diff --git a/backend/backend.hpp b/backends/include/backend.hpp similarity index 92% rename from backend/backend.hpp rename to backends/include/backend.hpp index ff562a8..21d960b 100644 --- a/backend/backend.hpp +++ b/backends/include/backend.hpp @@ -1,5 +1,5 @@ #pragma once -#include "registers.h" +#include "registers.hpp" class Backend { diff --git a/backend/c_code.h b/backends/include/c_code.hpp similarity index 100% rename from backend/c_code.h rename to backends/include/c_code.hpp diff --git a/backends/include/gifscript_backend.hpp b/backends/include/gifscript_backend.hpp new file mode 100644 index 0000000..99acd8c --- /dev/null +++ b/backends/include/gifscript_backend.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include "backend.hpp" + +#include +#include + +class gifscript_backend : public Backend +{ + +public: + gifscript_backend() = default; + ~gifscript_backend(); + + bool arg_parse(int argc, char** argv) override; + + void set_output(const std::string_view& output) override + { + this->output = output; + }; + + void print_help() const override; + + void emit(GIFBlock& block) override; + + // Primitive dispatching + static std::string emit_primitive(gifscript_backend*, const GifRegister&); + static std::string emit_rgbaq(gifscript_backend*, const GifRegister&); + static std::string emit_uv(gifscript_backend*, const GifRegister&); + static std::string emit_xyz2(gifscript_backend*, const GifRegister&); + static std::string emit_tex0(gifscript_backend*, const GifRegister&); + static std::string emit_fog(gifscript_backend*, const GifRegister&); + static std::string emit_fogcol(gifscript_backend*, const GifRegister&); + static std::string emit_scissor(gifscript_backend*, const GifRegister&); + static std::string emit_signal(gifscript_backend*, const GifRegister&); + static std::string emit_finish(gifscript_backend*, const GifRegister&); + static std::string emit_label(gifscript_backend*, const GifRegister&); + + std::unordered_map> dispatch_table = + { + {0x00, gifscript_backend::emit_primitive}, + {0x01, gifscript_backend::emit_rgbaq}, + {0x03, gifscript_backend::emit_uv}, + {0x05, gifscript_backend::emit_xyz2}, + {0x06, gifscript_backend::emit_tex0}, + {0x0A, gifscript_backend::emit_fog}, + {0x3D, gifscript_backend::emit_fogcol}, + {0x40, gifscript_backend::emit_scissor}, + {0x60, gifscript_backend::emit_signal}, + {0x61, gifscript_backend::emit_finish}, + {0x62, gifscript_backend::emit_label}}; + +private: + std::string output = ""; + FILE* file = nullptr; + bool first_emit = true; +}; diff --git a/backend/c_code.cpp b/backends/src/c_code.cpp similarity index 99% rename from backend/c_code.cpp rename to backends/src/c_code.cpp index 0e4b4d6..ee4303e 100644 --- a/backend/c_code.cpp +++ b/backends/src/c_code.cpp @@ -1,8 +1,8 @@ -#include "c_code.h" -#include "registers.h" +#include "c_code.hpp" +#include "registers.hpp" #include #include -#include "logger.h" +#include "logger.hpp" auto c_code_backend::arg_parse(int argc, char** argv) -> bool { diff --git a/backends/src/gifscript_backend.cpp b/backends/src/gifscript_backend.cpp new file mode 100644 index 0000000..6f403ae --- /dev/null +++ b/backends/src/gifscript_backend.cpp @@ -0,0 +1,240 @@ +#include "gifscript_backend.hpp" +#include "registers.hpp" +#include "logger.hpp" +#include "version.hpp" + +#include +#include + +auto gifscript_backend::arg_parse(int argc, char** argv) -> bool +{ + return true; +} + +void gifscript_backend::print_help() const +{ + fmt::print( + "gifscript backend options: none\n"); +} + +gifscript_backend::~gifscript_backend() +{ + if(file != nullptr && file != stdout) + { + fclose(file); + } +} + +void gifscript_backend::emit(GIFBlock& block) +{ + std::string buffer = fmt::format("{} {{\n\t", block.name); + fmt::print("Emitting block: {}\n", block.name); + for(const auto& reg : block.registers) + { + buffer += dispatch_table[static_cast(reg->GetID())](this, *reg); + buffer += "\n\t"; + } + + buffer.pop_back(); + buffer.pop_back(); + buffer += "\n}\n"; + + if(first_emit) + { + if(output.empty()) + { + file = stdout; + } + else + { + file = fopen(output.c_str(), "w"); + } + + if(file == nullptr) + { + logger::error("Failed to open file: %s\n", output.cbegin()); + return; + } + const std::string prologue = fmt::format("// Generated with GIFScript version {}\n", GIT_VERSION); + fwrite(prologue.c_str(), 1, prologue.size(), file); + } + + fwrite(buffer.c_str(), 1, buffer.size(), file); +} + +auto gifscript_backend::emit_primitive(gifscript_backend* inst, const GifRegister& reg) -> std::string +{ + const auto& prim = dynamic_cast(reg); + + std::string line = "prim "; + switch(prim.GetType()) + { + case PrimType::Point: + line += "point"; + break; + case PrimType::Line: + line += "line"; + break; + case PrimType::LineStrip: + line += "linestrip"; + break; + case PrimType::Triangle: + line += "triangle"; + break; + case PrimType::TriangleStrip: + line += "trianglestrip"; + break; + case PrimType::TriangleFan: + line += "trianglefan"; + break; + case PrimType::Sprite: + line += "sprite"; + break; + default: + logger::error("Unknown primitive type: %d\n", static_cast(prim.GetType())); + } + + if(prim.IsGouraud()) + { + line += " gouraud"; + } + + if(prim.IsTextured()) + { + line += " textured"; + } + + if(prim.IsFogging()) + { + line += " fogging"; + } + + if(prim.IsAA1()) + { + line += " aa1"; + } + + line += ";"; + return line; +} + +auto gifscript_backend::emit_rgbaq(gifscript_backend* inst, const GifRegister& reg) -> std::string +{ + const auto& rgbaq = dynamic_cast(reg); + + auto val = rgbaq.GetValue(); + return fmt::format("rgbaq 0x{:x},0x{:x},0x{:x},0x{:x};", + val.x, val.y, val.z, val.w); +} + +auto gifscript_backend::emit_uv(gifscript_backend* inst, const GifRegister& reg) -> std::string +{ + const auto& uv_reg = dynamic_cast(reg); + + auto val = uv_reg.GetValue(); + return fmt::format("uv 0x{:x},0x{:x};", + val.x, val.y); +} + +auto gifscript_backend::emit_xyz2(gifscript_backend* inst, const GifRegister& reg) -> std::string +{ + const auto& xyz2 = dynamic_cast(reg); + + auto val = xyz2.GetValue(); + return fmt::format("xyz2 0x{:x},0x{:x},0x{:x};", + val.x, val.y, val.z); +} + +auto gifscript_backend::emit_tex0(gifscript_backend* inst, const GifRegister& reg) -> std::string +{ + const auto& tex0 = dynamic_cast(reg); + + std::string line = fmt::format("tex0 0x{:x} 0x{:x} 0x{:x},0x{:x}", + tex0.GetTBP(), tex0.GetTBW(), tex0.GetTW(), tex0.GetTH()); + + switch(tex0.GetPSM()) + { + case PSM::CT32: + line += " CT32"; + break; + case PSM::CT24: + line += " CT24"; + break; + case PSM::CT16: + line += " CT16"; + break; + } + + switch(tex0.GetTFX()) + { + case TFX::Modulate: + line += " modulate"; + break; + case TFX::Decal: + line += " decal"; + break; + case TFX::Highlight: + line += " highlight"; + break; + case TFX::Highlight2: + line += " highlight2"; + break; + } + + line += ";"; + return line; +} + +auto gifscript_backend::emit_fog(gifscript_backend* inst, const GifRegister& reg) -> std::string +{ + const auto& fog = dynamic_cast(reg); + + auto val = fog.GetValue(); + return fmt::format("fog 0x{:x};", + val); +} + +auto gifscript_backend::emit_fogcol(gifscript_backend* inst, const GifRegister& reg) -> std::string +{ + const auto& fogcol = dynamic_cast(reg); + + auto val = fogcol.GetValue(); + return fmt::format("fogcol 0x{:x},0x{:x},0x{:x};", + val.x, val.y, val.z); +} + +auto gifscript_backend::emit_scissor(gifscript_backend* inst, const GifRegister& reg) -> std::string +{ + const auto& scissor = dynamic_cast(reg); + + auto val = scissor.GetValue(); + return fmt::format("scissor 0x{:x},0x{:x},0x{:x},0x{:x};", + val.x, val.y, val.z, val.w); +} + +auto gifscript_backend::emit_signal(gifscript_backend* inst, const GifRegister& reg) -> std::string +{ + const auto& signal = dynamic_cast(reg); + + auto val = signal.GetValue(); + return fmt::format("signal 0x{:x},0x{:x};", + val.x, val.y); +} + +auto gifscript_backend::emit_finish(gifscript_backend* inst, const GifRegister& reg) -> std::string +{ + const auto& finish = dynamic_cast(reg); + + auto val = finish.GetValue(); + return fmt::format("finish 0x{:x};", + val); +} + +auto gifscript_backend::emit_label(gifscript_backend* inst, const GifRegister& reg) -> std::string +{ + const auto& label = dynamic_cast(reg); + + auto val = label.GetValue(); + return fmt::format("label 0x{:x},0x{:x};", + val.x, val.y); +} diff --git a/core/include/logger.hpp b/core/include/logger.hpp new file mode 100644 index 0000000..78288cd --- /dev/null +++ b/core/include/logger.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +namespace logger +{ + struct fmt_location + { + const char* fmt; + std::source_location location; + + fmt_location(const char* fmt, const std::source_location& location = std::source_location::current()) + : fmt(fmt) + , location(location) + { + } + }; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-security" + template + void log(fmt_location format, fmt::color fgcol, Args&&... args) + { + int size_s = std::snprintf(nullptr, 0, format.fmt, std::forward(args)...); + char* buf = new char[size_s + 1]; + std::snprintf(buf, size_s + 1, format.fmt, std::forward(args)...); + fmt::print(fg(fgcol), "{}: {}\n", format.location.function_name(), buf); + + delete[] buf; + } +#pragma GCC diagnostic pop + template + void info(fmt_location format, Args&&... args) + { + fmt::print(fg(fmt::color::blue), "[INFO ] "); + log(format, fmt::color::blue, std::forward(args)...); + } + + template + void warn(fmt_location format, Args&&... args) + { + fmt::print(fg(fmt::color::yellow), "[WARN ] "); + log(format, fmt::color::yellow, std::forward(args)...); + } + + template + void error(fmt_location format, Args&&... args) + { + fmt::print(fg(fmt::color::red), "[ERROR] "); + log(format, fmt::color::red, std::forward(args)...); + } + + template + void debug(fmt_location format, Args&&... args) + { + fmt::print(fg(fmt::color::green), "[DEBUG] "); + log(format, fmt::color::green, std::forward(args)...); + } +}; // namespace logger diff --git a/machine.h b/core/include/machine.hpp similarity index 97% rename from machine.h rename to core/include/machine.hpp index 9be1ce0..a18a3e3 100644 --- a/machine.h +++ b/core/include/machine.hpp @@ -6,8 +6,8 @@ #include #include -#include "registers.h" -#include "backend/backend.hpp" +#include "registers.hpp" +#include "backend.hpp" class Machine { diff --git a/registers.h b/core/include/registers.hpp similarity index 99% rename from registers.h rename to core/include/registers.hpp index 4359c51..5d634c6 100644 --- a/registers.h +++ b/core/include/registers.hpp @@ -1,7 +1,7 @@ #pragma once -#include "types.h" -#include "logger.h" +#include "types.hpp" +#include "logger.hpp" #include #include #include diff --git a/types.h b/core/include/types.hpp similarity index 100% rename from types.h rename to core/include/types.hpp diff --git a/core/include/version.hpp b/core/include/version.hpp new file mode 100644 index 0000000..eecbb66 --- /dev/null +++ b/core/include/version.hpp @@ -0,0 +1,3 @@ +#ifndef GIT_VERSION +#define GIT_VERSION "unknown" +#endif diff --git a/machine.cpp b/core/src/machine.cpp similarity index 98% rename from machine.cpp rename to core/src/machine.cpp index da3a108..3d99543 100644 --- a/machine.cpp +++ b/core/src/machine.cpp @@ -1,9 +1,9 @@ -#include "machine.h" +#include "machine.hpp" #include -#include "registers.h" -#include "types.h" -#include "logger.h" +#include "registers.hpp" +#include "types.hpp" +#include "logger.hpp" Machine machine; @@ -133,7 +133,7 @@ auto Machine::TryInsertMacro(const std::string& name, Vec2 xyOffset) -> bool GIFBlock tmpMacro = macro->second; for(const auto& reg : tmpMacro.registers) { - if(reg->GetID() == GifRegisterID::PRIM) + if(reg->GetID() == GifRegisterID::XYZ2) { // Copies the register XYZ2 xyz2 = dynamic_cast(*reg); diff --git a/parser.y b/core/src/parser.y similarity index 95% rename from parser.y rename to core/src/parser.y index 7db5b6c..9a44914 100644 --- a/parser.y +++ b/core/src/parser.y @@ -2,12 +2,13 @@ #include #include #include -#include "types.h" -#include "registers.h" +#include "types.hpp" +#include "registers.hpp" +#include "machine.hpp" #include "parser.h" -#include "machine.h" -#include "backend/backend.hpp" + +#include "backend.hpp" } %syntax_error { diff --git a/registers.cpp b/core/src/registers.cpp similarity index 97% rename from registers.cpp rename to core/src/registers.cpp index 332b383..66dd8bf 100644 --- a/registers.cpp +++ b/core/src/registers.cpp @@ -1,4 +1,4 @@ -#include "registers.h" +#include "registers.hpp" #include auto GenReg(GifRegisters reg) -> std::unique_ptr diff --git a/gifscript.rl b/frontends/gifscript.rl similarity index 96% rename from gifscript.rl rename to frontends/gifscript.rl index 2315ade..06426d9 100644 --- a/gifscript.rl +++ b/frontends/gifscript.rl @@ -1,4 +1,4 @@ -#include "version.h" +#include "version.hpp" #include #include @@ -6,17 +6,18 @@ #include #include -#include "backend/c_code.h" +#include "c_code.hpp" +#include "gifscript_backend.hpp" + +#include "logger.hpp" -#include "logger.h" -#ifndef WIN32 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" #include "parser.c" #pragma GCC diagnostic pop -#endif -#include "registers.h" -#include "machine.h" + +#include "registers.hpp" +#include "machine.hpp" static bool valid = true; static Backend* backend = nullptr; @@ -553,6 +554,8 @@ void print_help(char* argv0) "Valid backends are:\n\t" " c_code(default)\n\t" " Generates a c file with an array for each gif block\n" + " gifscript\n\t" + " Generates a gifscript file. Mostly used for debugging or tpircsfig\n" "For backend specific help, please pass --bhelp to your backend\n" , argv0); }; @@ -581,6 +584,16 @@ int main(int argc, char **argv) return 1; } } + else if (backend_str == "gifscript") + { + fmt::print("Using gifscript backend\n"); + backend = new gifscript_backend(); + if(!backend->arg_parse(argc, argv)) + { + fmt::print("Use --bhelp for valid backend configuration arguments\n"); + return 1; + } + } else { fmt::print("Unknown backend: {}\n", backend_str); diff --git a/frontends/tpircsfig.cpp b/frontends/tpircsfig.cpp new file mode 100644 index 0000000..ce639dc --- /dev/null +++ b/frontends/tpircsfig.cpp @@ -0,0 +1,434 @@ +#include + +#include "logger.hpp" +#include "registers.hpp" +#include "machine.hpp" +#include "backend.hpp" +#include "c_code.hpp" +#include "gifscript_backend.hpp" +#include "version.hpp" +#include "parser.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +#include "parser.c" +#pragma GCC diagnostic pop + +static bool valid = true; +static Backend* backend = nullptr; +static void* lparser; + +struct GIFTag +{ + uint32_t NLOOP : 15; + uint32_t EOP : 1; + uint32_t : 16; + uint32_t : 14; + uint32_t PRE : 1; + uint32_t PRIM : 11; + uint32_t FLG : 2; + uint32_t NREG : 4; + uint64_t REGS; +}; + +void ParsePRIM(const uint64_t& prim) +{ + Parse(lparser, REG, new std::any(GifRegisters::PRIM), &valid); + switch(prim & 0x7) + { + case 0: + Parse(lparser, MOD, new std::any(RegModifier::Point), &valid); + break; + case 1: + Parse(lparser, MOD, new std::any(RegModifier::Line), &valid); + break; + case 2: + Parse(lparser, MOD, new std::any(RegModifier::TriangleStrip), &valid); + break; + case 3: + Parse(lparser, MOD, new std::any(RegModifier::Triangle), &valid); + break; + case 4: + Parse(lparser, MOD, new std::any(RegModifier::TriangleStrip), &valid); + break; + case 5: + Parse(lparser, MOD, new std::any(RegModifier::TriangleFan), &valid); + break; + case 6: + Parse(lparser, MOD, new std::any(RegModifier::Sprite), &valid); + break; + case 7: + logger::error("Invalid PRIM type: 7"); + break; + default: + std::unreachable(); + } + + if(prim & 0x8) + { + Parse(lparser, MOD, new std::any(RegModifier::Gouraud), &valid); + } + + if(prim & 0x10) + { + Parse(lparser, MOD, new std::any(RegModifier::Texture), &valid); + } + + if(prim & 0x20) + { + Parse(lparser, MOD, new std::any(RegModifier::Fogging), &valid); + } + + Parse(lparser, 0, nullptr, &valid); +} + +void ParseRGBAQ(const uint64_t& rgbaq) +{ + Parse(lparser, REG, new std::any(GifRegisters::RGBAQ), &valid); + Vec4 color = Vec4(rgbaq & 0xFF, (rgbaq >> 8) & 0xFF, (rgbaq >> 16) & 0xFF, (rgbaq >> 24) & 0xFF); + Parse(lparser, VEC4, new std::any(color), &valid); + Parse(lparser, 0, 0, &valid); +} + +void ParseUV(const uint64_t& uv_reg) +{ + Parse(lparser, REG, new std::any(GifRegisters::UV), &valid); + Vec2 uv_vec = Vec2((uv_reg >> 4) & 0x3FFF, (uv_reg >> 20) & 0x3FFF); + Parse(lparser, VEC2, new std::any(uv_vec), &valid); + Parse(lparser, 0, nullptr, &valid); +} + +void ParseXYZ2(const uint64_t& xyz2) +{ + Parse(lparser, REG, new std::any(GifRegisters::XYZ2), &valid); + Vec3 xyz = Vec3((xyz2 >> 4) & 0xFFFF, (xyz2 >> 20) & 0xFFFF, (xyz2 >> 36) & 0xFFFF); + Parse(lparser, VEC3, new std::any(xyz), &valid); + Parse(lparser, 0, 0, &valid); +} + +void ParseTEX0(const uint64_t& tex0) +{ + Parse(lparser, REG, new std::any(GifRegisters::TEX0), &valid); + Parse(lparser, NUMBER_LITERAL, new std::any(static_cast(tex0 & 0x3FFF)), &valid); + Parse(lparser, NUMBER_LITERAL, new std::any(static_cast((tex0 >> 14) & 0x3F)), &valid); + Parse(lparser, VEC2, new std::any(Vec2((tex0 >> 26) & 0xF, (tex0 >> 30) & 0xF)), &valid); + switch(tex0 >> 20 & 0x3F) + { + case 0: + Parse(lparser, MOD, new std::any(RegModifier::CT32), &valid); + break; + case 1: + Parse(lparser, MOD, new std::any(RegModifier::CT24), &valid); + break; + case 2: + Parse(lparser, MOD, new std::any(RegModifier::CT16), &valid); + break; + default: + logger::error("Invalid TBP value: {}", tex0 >> 20 & 0xF); + } + + switch((tex0 >> 35) & 0x3) + { + case 0: + Parse(lparser, MOD, new std::any(RegModifier::Modulate), &valid); + break; + case 1: + Parse(lparser, MOD, new std::any(RegModifier::Decal), &valid); + break; + case 2: + Parse(lparser, MOD, new std::any(RegModifier::Highlight), &valid); + break; + case 3: + Parse(lparser, MOD, new std::any(RegModifier::Highlight2), &valid); + break; + } + + Parse(lparser, 0, nullptr, &valid); +} + +void ParseFOG(const uint64_t& fog) +{ + Parse(lparser, REG, new std::any(GifRegisters::FOG), &valid); + Parse(lparser, NUMBER_LITERAL, new std::any(static_cast((fog >> 56) & 0xFF)), &valid); + Parse(lparser, 0, nullptr, &valid); +} + +void ParseFOGCOL(const uint64_t& fogcol) +{ + Parse(lparser, REG, new std::any(GifRegisters::FOGCOL), &valid); + Vec3 color = Vec3(fogcol & 0xFF, (fogcol >> 8) & 0xFF, (fogcol >> 16) & 0xFF); + Parse(lparser, VEC3, new std::any(color), &valid); + Parse(lparser, 0, nullptr, &valid); +} + +void ParseSCISSOR(const uint64_t& scissor) +{ + Parse(lparser, REG, new std::any(GifRegisters::SCISSOR), &valid); + Vec4 scissor_vec = Vec4(scissor & 0x7FF, (scissor >> 16) & 0x7FF, (scissor >> 32) & 0x7FF, (scissor >> 48) & 0x7FF); + Parse(lparser, VEC4, new std::any(scissor_vec), &valid); + Parse(lparser, 0, nullptr, &valid); +} + +void ParseSIGNAL(const uint64_t& signal) +{ + Parse(lparser, REG, new std::any(GifRegisters::SIGNAL), &valid); + Parse(lparser, VEC2, new std::any(Vec2(signal & UINT32_MAX, signal >> 32)), &valid); + Parse(lparser, 0, nullptr, &valid); +} + +void ParseFINISH(const uint64_t& finish) +{ + Parse(lparser, REG, new std::any(GifRegisters::FINISH), &valid); + Parse(lparser, NUMBER_LITERAL, new std::any(static_cast(finish)), &valid); + Parse(lparser, 0, nullptr, &valid); +} + +void ParseLABEL(const uint64_t& label) +{ + Parse(lparser, REG, new std::any(GifRegisters::LABEL), &valid); + Parse(lparser, NUMBER_LITERAL, new std::any(static_cast(label)), &valid); + Parse(lparser, 0, nullptr, &valid); +} + +void Scan(uint64_t* buffer, size_t size) +{ + uint64_t* ptr = buffer; + + // GIFTag + GIFTag& tag = *reinterpret_cast(ptr); + Parse(lparser, IDENTIFIER, new std::any(fmt::format("block_{:x}", reinterpret_cast(ptr) - reinterpret_cast(buffer))), &valid); + Parse(lparser, BLOCK_START, nullptr, &valid); + Parse(lparser, 0, nullptr, &valid); + + if(tag.PRE) + { + ParsePRIM(tag.PRIM); + } + + ptr++; + uint64_t gifregs = *ptr; + ptr++; + + switch(tag.FLG) + { + case 0: + for(int i = 0; i < tag.NLOOP; i++) + { + for(int j = 0; j < tag.NREG; j++) + { + uint64_t gifreg = gifregs >> (j * 4) & 0xF; + logger::warn("GIFREG is %x, %016X", gifreg, *ptr); + + switch(gifreg) + { + case 0x0E: + { + uint64_t data = *ptr; + ptr++; + uint64_t dest = *ptr; + ptr++; + switch(static_cast(dest)) + { + case GifRegisterID::PRIM: + ParsePRIM(data); + break; + case GifRegisterID::RGBAQ: + ParseRGBAQ(data); + break; + case GifRegisterID::UV: + ParseUV(data); + break; + case GifRegisterID::XYZ2: + ParseXYZ2(data); + break; + case GifRegisterID::TEX0: + ParseTEX0(data); + break; + case GifRegisterID::FOG: + ParseFOG(data); + break; + case GifRegisterID::FOGCOL: + ParseFOGCOL(data); + break; + case GifRegisterID::SCISSOR: + ParseSCISSOR(data); + break; + case GifRegisterID::SIGNAL: + ParseSIGNAL(data); + break; + case GifRegisterID::FINISH: + ParseFINISH(data); + break; + case GifRegisterID::LABEL: + ParseLABEL(data); + break; + default: + logger::error("Unsupported gs register"); + } + } + break; + default: + logger::error("Only supports AD gifreg currently"); + } + } + } + break; + default: + logger::error("Unsupported FLG: %u", (uint32_t)tag.FLG); + } + Parse(lparser, BLOCK_END, nullptr, &valid); + Parse(lparser, 0, nullptr, &valid); +} + +void print_help(char* argv0) +{ + fmt::print("Usage: {} [--backend=] [--b]\n\t" + "General Arguments:\n\t" + " --help, -h\n\t" + " Prints this help message\n\t" + " --version, -v\n\t" + " Prints the version of tpircsfig\n\t" + "Valid backends are:\n\t" + " c_code(default)\n\t" + " Generates a c file with an array for each gif block\n" + " gifscript\n\t" + " Generates a gifscript file. Mostly used for debugging or tpircsfig\n" + "For backend specific help, please pass --bhelp to your backend\n", + argv0); +}; + + +std::string file_in; +std::string file_out; +auto main(int argc, char** argv) -> int +{ + machine.DisableOptimization(Machine::Optimization::DEAD_STORE_ELIMINATION); + //machine.DisableOptimization(Machine::Optimization::USE_TAG_PRIM); + + if(argc < 2) + { + print_help(argv[0]); + return 1; + } + for(int i = 1; i < argc; i++) + { + std::string_view arg = argv[i]; + if(arg.starts_with("--backend=")) + { + std::string_view backend_str = arg.substr(strlen("--backend=")); + if(backend_str == "c_code") + { + fmt::print("Using C backend\n"); + backend = new c_code_backend(); + if(!backend->arg_parse(argc, argv)) + { + fmt::print("Use --bhelp for valid backend configuration arguments\n"); + return 1; + } + } + else if(backend_str == "gifscript") + { + fmt::print("Using gifscript backend\n"); + backend = new gifscript_backend(); + if(!backend->arg_parse(argc, argv)) + { + fmt::print("Use --bhelp for valid backend configuration arguments\n"); + return 1; + } + } + else + { + fmt::print("Unknown backend: {}\n", backend_str); + return 1; + } + } + else if(arg == "--help" || arg == "-h") + { + print_help(argv[0]); + return 1; + } + else if(arg == "--version" || arg == "-v") + { + fmt::print("Gifscript(tpircsfig) version: {}\n", GIT_VERSION); + return 0; + } + else if(arg == "--keep-deadstore") + { + machine.DisableOptimization(Machine::Optimization::DEAD_STORE_ELIMINATION); + } + else if(arg == "--no-tag-prim") + { + machine.DisableOptimization(Machine::Optimization::USE_TAG_PRIM); + } + else if(file_in.empty() && !arg.starts_with("-")) + { + file_in = arg; + } + else if(file_out.empty() && !arg.starts_with("-")) + { + file_out = arg; + } + else if(!arg.starts_with("--b")) + { + fmt::print("Unknown argument: {}\n", arg); + return 1; + } + } + + if(backend == nullptr) + { + logger::info("No backend specified, using default 'gifscript'"); + backend = new gifscript_backend(); + if(!backend->arg_parse(argc, argv)) + { + fmt::print("Use --bhelp for valid backend configuration arguments\n"); + return 1; + } + } + + machine.SetBackend(backend); + + backend->set_output(file_out); + + if(file_in.empty()) + { + fmt::print("No input file specified\n"); + return 1; + } + + if(file_out.empty()) + { + fmt::print("No output file specified. Printing to stdout\n"); + } + + lparser = ParseAlloc(malloc); + const size_t buffer_size = 8192; + std::array buffer; + FILE* fin; + unsigned long numbytes; + + fin = fopen(file_in.c_str(), "rb"); + if(fin == nullptr) + { + fmt::print("Failed to open file: {}\n", file_in); + return 1; + } + fseek(fin, 0, SEEK_END); + numbytes = ftell(fin); + fseek(fin, 0, SEEK_SET); + + if(numbytes > sizeof(buffer)) + { + fmt::print("File {} bytes is too large for internal buffer {} bytes :(\n", numbytes, sizeof(buffer)); + return 1; + } + + fread(buffer.data(), sizeof(uint64_t), numbytes, fin); + + Scan(buffer.data(), numbytes); + + ParseFree(lparser, free); + delete backend; + fclose(fin); + return 0; +} diff --git a/logger.h b/logger.h deleted file mode 100644 index 4d426a5..0000000 --- a/logger.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace logger -{ -struct fmt_location -{ - const char *fmt; - std::source_location location; - - fmt_location(const char *fmt, const std::source_location &location = std::source_location::current()) : fmt(fmt), location(location) {} -}; - -#ifndef _WIN32 -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wformat-security" -#endif -template -void log(fmt_location format, fmt::color fgcol, Args &&...args) -{ - int size_s = std::snprintf(nullptr, 0, format.fmt, std::forward(args)...); - char *buf = new char[size_s + 1]; - std::snprintf(buf, size_s + 1, format.fmt, std::forward(args)...); - fmt::print(fg(fgcol), "{}: {}\n", format.location.function_name(), buf); - - delete[] buf; -} -#ifndef _WIN32 -# pragma GCC diagnostic pop -#endif - -template -void info(fmt_location format, Args &&...args) -{ - fmt::print(fg(fmt::color::blue), "[INFO ] "); - log(format,fmt::color::blue, std::forward(args)...); -} - -template -void warn(fmt_location format, Args &&...args) -{ - fmt::print(fg(fmt::color::yellow), "[WARN ] "); - log(format,fmt::color::yellow, std::forward(args)...); -} - -template -void error(fmt_location format, Args &&...args) -{ - fmt::print(fg(fmt::color::red), "[ERROR] "); - log(format,fmt::color::red, std::forward(args)...); -} - -template -void debug(fmt_location format, Args &&...args) -{ - fmt::print(fg(fmt::color::green), "[DEBUG] "); - log(format,fmt::color::green, std::forward(args)...); -} -}; diff --git a/version.h b/version.h deleted file mode 100644 index 9bc8703..0000000 --- a/version.h +++ /dev/null @@ -1,3 +0,0 @@ -#ifndef GIT_VERSION - #define GIT_VERSION "unknown" -#endif