From df02d49e9becdfe13e35f3a22598c4e448c4ac76 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Mon, 30 Mar 2026 13:29:25 +0100 Subject: [PATCH] INCGFX: in-source arguments for gbagfx 'INCGFX' is like 'INCBIN' except that it specifies the arguments to pass to 'gbagfx' which alleviates the user-unfriendliness caused by catch-all rules. Specifically, users frequently forget to add to 'spritesheet_rules.mk' when adding object event graphics, and then the built '.4bpp' file is not invalidated when the add their rule so they have to 'touch' the source file or similar. The built artifacts are placed in 'build/assets'. --- Makefile | 14 +-- tools/preproc/c_file.cpp | 171 ++++++++++++++++++++++++++++------ tools/preproc/c_file.h | 5 +- tools/preproc/preproc.cpp | 16 ++-- tools/scaninc/c_file.cpp | 117 ++++++++++++++++++++++- tools/scaninc/c_file.h | 9 +- tools/scaninc/scaninc.cpp | 70 +++++++++++++- tools/scaninc/scaninc.h | 14 +++ tools/scaninc/source_file.cpp | 12 ++- tools/scaninc/source_file.h | 3 +- 10 files changed, 377 insertions(+), 54 deletions(-) diff --git a/Makefile b/Makefile index 754ecebe36..25c7a7248b 100644 --- a/Makefile +++ b/Makefile @@ -71,6 +71,7 @@ ROM_NAME := $(FILE_NAME).gba OBJ_DIR_NAME := $(BUILD_DIR)/emerald MODERN_ROM_NAME := $(FILE_NAME)_modern.gba MODERN_OBJ_DIR_NAME := $(BUILD_DIR)/modern +ASSETS_DIR_NAME := $(BUILD_DIR)/assets ELF_NAME := $(ROM_NAME:.gba=.elf) MAP_NAME := $(ROM_NAME:.gba=.map) @@ -234,6 +235,7 @@ clean: tidy clean-tools clean-generated clean-assets @$(MAKE) clean -C libagbsyscall clean-assets: + rm -rf $(ASSETS_DIR_NAME) rm -f $(MID_SUBDIR)/*.s rm -f $(DATA_ASM_SUBDIR)/layouts/layouts.inc $(DATA_ASM_SUBDIR)/layouts/layouts_table.inc rm -f $(DATA_ASM_SUBDIR)/maps/connections.inc $(DATA_ASM_SUBDIR)/maps/events.inc $(DATA_ASM_SUBDIR)/maps/groups.inc $(DATA_ASM_SUBDIR)/maps/headers.inc @@ -306,16 +308,16 @@ endif $(C_BUILDDIR)/%.o: $(C_SUBDIR)/%.c ifneq ($(KEEP_TEMPS),1) @echo "$(CC1) -o $@ $<" - @$(CPP) $(CPPFLAGS) $< | $(PREPROC) -i $< charmap.txt | $(CC1) $(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $(AS) $(ASFLAGS) -o $@ - + @$(CPP) $(CPPFLAGS) $< | $(PREPROC) -i -g $(ASSETS_DIR_NAME) $< charmap.txt | $(CC1) $(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $(AS) $(ASFLAGS) -o $@ - else @$(CPP) $(CPPFLAGS) $< -o $(C_BUILDDIR)/$*.i - @$(PREPROC) $(C_BUILDDIR)/$*.i charmap.txt | $(CC1) $(CFLAGS) -o $(C_BUILDDIR)/$*.s + @$(PREPROC) -g $(ASSETS_DIR_NAME) $(C_BUILDDIR)/$*.i charmap.txt | $(CC1) $(CFLAGS) -o $(C_BUILDDIR)/$*.s @echo -e ".text\n\t.align\t2, 0\n" >> $(C_BUILDDIR)/$*.s $(AS) $(ASFLAGS) -o $@ $(C_BUILDDIR)/$*.s endif $(C_BUILDDIR)/%.d: $(C_SUBDIR)/%.c - $(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) -I tools/agbcc/include $< + $(SCANINC) -M $@ -g $(ASSETS_DIR_NAME) $(INCLUDE_SCANINC_ARGS) -I tools/agbcc/include $< ifneq ($(NODEP),1) -include $(addprefix $(OBJ_DIR)/,$(C_SRCS:.c=.d)) @@ -325,7 +327,7 @@ $(ASM_BUILDDIR)/%.o: $(ASM_SUBDIR)/%.s $(AS) $(ASFLAGS) -o $@ $< $(ASM_BUILDDIR)/%.d: $(ASM_SUBDIR)/%.s - $(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) -I "" $< + $(SCANINC) -M $@ -g $(ASSETS_DIR_NAME) $(INCLUDE_SCANINC_ARGS) -I "" $< ifneq ($(NODEP),1) -include $(addprefix $(OBJ_DIR)/,$(ASM_SRCS:.s=.d)) @@ -335,7 +337,7 @@ $(C_BUILDDIR)/%.o: $(C_SUBDIR)/%.s $(PREPROC) $< charmap.txt | $(CPP) $(INCLUDE_SCANINC_ARGS) - | $(PREPROC) -ie $< charmap.txt | $(AS) $(ASFLAGS) -o $@ $(C_BUILDDIR)/%.d: $(C_SUBDIR)/%.s - $(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) -I "" $< + $(SCANINC) -M $@ -g $(ASSETS_DIR_NAME) $(INCLUDE_SCANINC_ARGS) -I "" $< ifneq ($(NODEP),1) -include $(addprefix $(OBJ_DIR)/,$(C_ASM_SRCS:.s=.d)) @@ -345,7 +347,7 @@ $(DATA_ASM_BUILDDIR)/%.o: $(DATA_ASM_SUBDIR)/%.s $(PREPROC) $< charmap.txt | $(CPP) $(INCLUDE_SCANINC_ARGS) - | $(PREPROC) -ie $< charmap.txt | $(AS) $(ASFLAGS) -o $@ $(DATA_ASM_BUILDDIR)/%.d: $(DATA_ASM_SUBDIR)/%.s - $(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) -I "" $< + $(SCANINC) -M $@ -g $(ASSETS_DIR_NAME) $(INCLUDE_SCANINC_ARGS) -I "" $< ifneq ($(NODEP),1) -include $(addprefix $(OBJ_DIR)/,$(REGULAR_DATA_ASM_SRCS:.s=.d)) diff --git a/tools/preproc/c_file.cpp b/tools/preproc/c_file.cpp index d034b3f1f1..8de8ed7a92 100644 --- a/tools/preproc/c_file.cpp +++ b/tools/preproc/c_file.cpp @@ -18,6 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include #include #include #include @@ -32,7 +33,7 @@ #include "string_parser.h" #include "io.h" -CFile::CFile(const char * filenameCStr, bool isStdin) +CFile::CFile(const char * filenameCStr, bool isStdin, const char * graphicsRootCStr) { if (isStdin) m_filename = std::string{"/"}.append(filenameCStr); @@ -44,6 +45,9 @@ CFile::CFile(const char * filenameCStr, bool isStdin) m_pos = 0; m_lineNum = 1; m_isStdin = isStdin; + m_graphicsRoot = graphicsRootCStr; + if (m_graphicsRoot.empty()) m_graphicsRoot = "./"; + if (m_graphicsRoot[m_graphicsRoot.length() - 1] != '/') m_graphicsRoot.push_back('/'); } CFile::CFile(CFile&& other) : m_filename(std::move(other.m_filename)) @@ -94,6 +98,7 @@ void CFile::Preproc() { TryConvertString(); TryConvertIncbin(); + TryConvertIncgfx(); if (m_pos >= m_size) break; @@ -237,6 +242,37 @@ bool CFile::CheckIdentifier(const std::string& ident) return (i == ident.length()); } +std::string CFile::ReadString() +{ + if (m_buffer[m_pos] != '"') + RaiseError("expected '\"'"); + m_pos++; + + long startPos = m_pos; + + while (m_buffer[m_pos] != '"') + { + if (m_buffer[m_pos] == 0) + { + if (m_pos >= m_size) + RaiseError("unexpected EOF in string"); + else + RaiseError("unexpected null character in string"); + } + + if (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n') + RaiseError("unexpected end of line character in string"); + + if (m_buffer[m_pos] == '\\') + RaiseError("unexpected escape in string"); + + m_pos++; + } + m_pos++; + + return std::string(&m_buffer[startPos], m_pos - 1 - startPos); +} + std::unique_ptr CFile::ReadWholeFile(const std::string& path, int& size) { FILE* fp = std::fopen(path.c_str(), "rb"); @@ -319,36 +355,7 @@ void CFile::TryConvertIncbin() while (true) { SkipWhitespace(); - - if (m_buffer[m_pos] != '"') - RaiseError("expected double quote"); - - m_pos++; - - int startPos = m_pos; - - while (m_buffer[m_pos] != '"') - { - if (m_buffer[m_pos] == 0) - { - if (m_pos >= m_size) - RaiseError("unexpected EOF in path string"); - else - RaiseError("unexpected null character in path string"); - } - - if (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n') - RaiseError("unexpected end of line character in path string"); - - if (m_buffer[m_pos] == '\\') - RaiseError("unexpected escape in path string"); - - m_pos++; - } - - std::string path(&m_buffer[startPos], m_pos - startPos); - - m_pos++; + auto path = ReadString(); int fileSize; std::unique_ptr buffer = ReadWholeFile(path, fileSize); @@ -382,6 +389,108 @@ void CFile::TryConvertIncbin() std::printf("}"); } +void CFile::TryConvertIncgfx() +{ + if (!CheckIdentifier("INCGFX_")) + return; + + std::string idents[3] = { "INCGFX_U8", "INCGFX_U16", "INCGFX_U32" }; + int incgfxType = -1; + + for (int i = 0; i < 3; i++) + { + if (CheckIdentifier(idents[i])) + { + incgfxType = i; + break; + } + } + + if (incgfxType == -1) + return; + + int size = 1 << incgfxType; + + long oldPos = m_pos; + long oldLineNum = m_lineNum; + + m_pos += idents[incgfxType].length(); + + SkipWhitespace(); + if (m_buffer[m_pos] != '(') + { + m_pos = oldPos; + m_lineNum = oldLineNum; + return; + } + m_pos++; + + SkipWhitespace(); + auto source = ReadString(); + + SkipWhitespace(); + if (m_buffer[m_pos] != ',') + RaiseError("expected ','"); + m_pos++; + + SkipWhitespace(); + auto extensions = ReadString(); + + SkipWhitespace(); + std::string arguments; + if (m_buffer[m_pos] == ',') + { + m_pos++; + SkipWhitespace(); + arguments = ReadString(); + + SkipWhitespace(); + if (m_buffer[m_pos] != ')') + RaiseError("expected ')'"); + m_pos++; + } + else if (m_buffer[m_pos] == ')') + { + m_pos++; + } + else + { + RaiseError("exected ')' or ','"); + } + + // WARNING: This must stay in-sync with 'tools/scaninc/source_file.cpp'. + std::string arguments_as_path; + for (auto c : arguments) + { + if (std::isalnum(c)) + arguments_as_path += c; + else + arguments_as_path += '_'; + } + + // WARNING: This must stay in-sync with 'tools/scaninc/scaninc.cpp'. + auto converted = m_graphicsRoot + source + arguments_as_path + extensions; + + int fileSize; + std::unique_ptr buffer = ReadWholeFile(converted, fileSize); + + if ((fileSize % size) != 0) + RaiseError("Size %d doesn't evenly divide file size %d.\n", size, fileSize); + + std::printf("{"); + + int count = fileSize / size; + int offset = 0; + for (int i = 0; i < count; i++) + { + int data = ExtractData(buffer, offset, size); + offset += size; + std::printf("%uu,", data); + } + + std::printf("}"); +} + // Reports a diagnostic message. void CFile::ReportDiagnostic(const char* type, const char* format, std::va_list args) { diff --git a/tools/preproc/c_file.h b/tools/preproc/c_file.h index c40c33c962..0dbc4aef1b 100644 --- a/tools/preproc/c_file.h +++ b/tools/preproc/c_file.h @@ -30,7 +30,7 @@ class CFile { public: - CFile(const char * filenameCStr, bool isStdin); + CFile(const char * filenameCStr, bool isStdin, const char * graphicsRootCStr); CFile(CFile&& other); CFile(const CFile&) = delete; ~CFile(); @@ -43,6 +43,7 @@ private: long m_lineNum; std::string m_filename; bool m_isStdin; + std::string m_graphicsRoot; bool ConsumeHorizontalWhitespace(); bool ConsumeNewline(); @@ -50,7 +51,9 @@ private: void TryConvertString(); std::unique_ptr ReadWholeFile(const std::string& path, int& size); bool CheckIdentifier(const std::string& ident); + std::string ReadString(); void TryConvertIncbin(); + void TryConvertIncgfx(); void ReportDiagnostic(const char* type, const char* format, std::va_list args); void RaiseError(const char* format, ...); void RaiseWarning(const char* format, ...); diff --git a/tools/preproc/preproc.cpp b/tools/preproc/preproc.cpp index ac9496d701..112d884044 100644 --- a/tools/preproc/preproc.cpp +++ b/tools/preproc/preproc.cpp @@ -118,9 +118,9 @@ void PreprocAsmFile(std::string filename, bool isStdin, bool doEnum) } } -void PreprocCFile(const char * filename, bool isStdin) +void PreprocCFile(const char * filename, bool isStdin, const char * graphicsRoot) { - CFile cFile(filename, isStdin); + CFile cFile(filename, isStdin, graphicsRoot); cFile.Preproc(); } @@ -147,7 +147,7 @@ const char* GetFileExtension(const char* filename) static void UsageAndExit(const char *program) { - std::fprintf(stderr, "Usage: %s [-i] [-e] SRC_FILE CHARMAP_FILE\nwhere -i denotes if input is from stdin\n -e enables enum handling\n", program); + std::fprintf(stderr, "Usage: %s [-i] [-e] [-g PATH] SRC_FILE CHARMAP_FILE\nwhere -i denotes if input is from stdin\n -e enables enum handling\n-g specifies the root for INCGFX\n", program); std::exit(EXIT_FAILURE); } @@ -158,9 +158,10 @@ int main(int argc, char **argv) const char *charmap = NULL; bool isStdin = false; bool doEnum = false; + const char *graphicsRoot = ""; - /* preproc [-i] [-e] SRC_FILE CHARMAP_FILE */ - while ((opt = getopt(argc, argv, "ie")) != -1) + /* preproc [-i] [-e] [-g PATH] SRC_FILE CHARMAP_FILE */ + while ((opt = getopt(argc, argv, "ieg:")) != -1) { switch (opt) { @@ -170,6 +171,9 @@ int main(int argc, char **argv) case 'e': doEnum = true; break; + case 'g': + graphicsRoot = optarg; + break; default: UsageAndExit(argv[0]); break; @@ -202,7 +206,7 @@ int main(int argc, char **argv) { if (doEnum) FATAL_ERROR("-e is invalid for C sources\n"); - PreprocCFile(source, isStdin); + PreprocCFile(source, isStdin, graphicsRoot); } else { diff --git a/tools/scaninc/c_file.cpp b/tools/scaninc/c_file.cpp index f76976696d..e6ce65d056 100644 --- a/tools/scaninc/c_file.cpp +++ b/tools/scaninc/c_file.cpp @@ -18,6 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include #include "c_file.h" CFile::CFile(std::string path) @@ -86,6 +87,7 @@ void CFile::FindIncbins() SkipWhitespace(); CheckInclude(); CheckIncbin(); + CheckIncgfx(); if (m_pos >= m_size) break; @@ -215,7 +217,7 @@ void CFile::CheckIncbin() && m_buffer[m_pos+5] == 'N' && m_buffer[m_pos+6] == '_')) { - return; + return; } std::string idents[3] = { "INCBIN_U8", "INCBIN_U16", "INCBIN_U32" }; @@ -272,6 +274,86 @@ void CFile::CheckIncbin() } +void CFile::CheckIncgfx() +{ + // Optimization: assume most lines are not incgfxs + if (!(m_buffer[m_pos+0] == 'I' + && m_buffer[m_pos+1] == 'N' + && m_buffer[m_pos+2] == 'C' + && m_buffer[m_pos+3] == 'G' + && m_buffer[m_pos+4] == 'F' + && m_buffer[m_pos+5] == 'X' + && m_buffer[m_pos+6] == '_')) + { + return; + } + + std::string idents[3] = { "INCGFX_U8", "INCGFX_U16", "INCGFX_U32" }; + int incgfxType = -1; + + for (int i = 0; i < 3; i++) + { + if (CheckIdentifier(idents[i])) + { + incgfxType = i; + break; + } + } + + if (incgfxType == -1) + return; + + long oldPos = m_pos; + long oldLineNum = m_lineNum; + + m_pos += idents[incgfxType].length(); + + SkipWhitespace(); + if (m_buffer[m_pos] != '(') + { + m_pos = oldPos; + m_lineNum = oldLineNum; + return; + } + m_pos++; + + SkipWhitespace(); + std::string path = ReadPath(); + + SkipWhitespace(); + if (m_buffer[m_pos] != ',') + FATAL_INPUT_ERROR("expected ','"); + m_pos++; + + SkipWhitespace(); + std::string extensions = ReadString(); + + SkipWhitespace(); + std::string arguments; + if (m_buffer[m_pos] == ',') + { + m_pos++; + SkipWhitespace(); + arguments = ReadString(); + + SkipWhitespace(); + if (m_buffer[m_pos] != ')') + FATAL_INPUT_ERROR("expected ')'"); + m_pos++; + } + else if (m_buffer[m_pos] == ')') + { + m_pos++; + } + else + { + FATAL_INPUT_ERROR("expected ')' or ','"); + } + + Incgfx incgfx = { path, extensions, arguments }; + m_incgfxs.emplace(incgfx); +} + std::string CFile::ReadPath() { if (m_buffer[m_pos] != '"') @@ -285,7 +367,7 @@ std::string CFile::ReadPath() m_pos++; - int startPos = m_pos; + long startPos = m_pos; while (m_buffer[m_pos] != '"') { @@ -310,3 +392,34 @@ std::string CFile::ReadPath() return std::string(m_buffer + startPos, m_pos - 1 - startPos); } + +std::string CFile::ReadString() +{ + if (m_buffer[m_pos] != '"') + FATAL_INPUT_ERROR("expected '\"', got: '%c'", m_buffer[m_pos]); + m_pos++; + + long startPos = m_pos; + + while (m_buffer[m_pos] != '"') + { + if (m_buffer[m_pos] == 0) + { + if (m_pos >= m_size) + FATAL_INPUT_ERROR("expected EOF in string"); + else + FATAL_INPUT_ERROR("unexpected null character in string"); + } + + if (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n') + FATAL_INPUT_ERROR("unexpected end of line character in string"); + + if (m_buffer[m_pos] == '\\') + FATAL_INPUT_ERROR("unexpected escape in string"); + + m_pos++; + } + m_pos++; + + return std::string(m_buffer + startPos, m_pos - 1 - startPos); +} diff --git a/tools/scaninc/c_file.h b/tools/scaninc/c_file.h index 618901b855..fc1565b73a 100644 --- a/tools/scaninc/c_file.h +++ b/tools/scaninc/c_file.h @@ -21,9 +21,10 @@ #ifndef C_FILE_H #define C_FILE_H -#include -#include #include +#include +#include +#include #include "scaninc.h" class CFile @@ -33,6 +34,7 @@ public: ~CFile(); void FindIncbins(); const std::set& GetIncbins() { return m_incbins; } + const std::set& GetIncgfxs() { return m_incgfxs; } const std::set& GetIncludes() { return m_includes; } private: @@ -42,6 +44,7 @@ private: int m_lineNum; std::string m_path; std::set m_incbins; + std::set m_incgfxs; std::set m_includes; bool ConsumeHorizontalWhitespace(); @@ -51,7 +54,9 @@ private: bool CheckIdentifier(const std::string& ident); void CheckInclude(); void CheckIncbin(); + void CheckIncgfx(); std::string ReadPath(); + std::string ReadString(); }; #endif // C_FILE_H diff --git a/tools/scaninc/scaninc.cpp b/tools/scaninc/scaninc.cpp index d470a3f163..56434e5a41 100644 --- a/tools/scaninc/scaninc.cpp +++ b/tools/scaninc/scaninc.cpp @@ -18,9 +18,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include #include #include #include +#include #include #include #include @@ -41,18 +43,20 @@ bool CanOpenFile(std::string path) return true; } -const char *const USAGE = "Usage: scaninc [-I INCLUDE_PATH] [-M DEPENDENCY_OUT_PATH] FILE_PATH\n"; +const char *const USAGE = "Usage: scaninc [-I INCLUDE_PATH] [-M DEPENDENCY_OUT_PATH] [-g PATH] FILE_PATH\n"; int main(int argc, char **argv) { std::queue filesToProcess; std::set dependencies; std::set dependencies_includes; + std::map> dependencies_gfx_rules; std::vector includeDirs; bool makeformat = false; std::string make_outfile; + std::string gfx_root; argc--; argv++; @@ -82,6 +86,12 @@ int main(int argc, char **argv) argv++; make_outfile = std::string(argv[0]); } + else if (arg.substr(0, 2) == "-g") + { + argc--; + argv++; + gfx_root = std::string(argv[0]); + } else { FATAL_ERROR(USAGE); @@ -94,6 +104,11 @@ int main(int argc, char **argv) FATAL_ERROR(USAGE); } + size_t ext_pos = make_outfile.find_last_of("."); + auto object_file = make_outfile.substr(0, ext_pos + 1) + "o"; + if (gfx_root.empty()) gfx_root = "./"; + if (gfx_root[gfx_root.length() - 1] != '/') gfx_root.push_back('/'); + std::string initialPath(argv[0]); filesToProcess.push(initialPath); @@ -109,6 +124,34 @@ int main(int argc, char **argv) { dependencies.insert(incbin); } + for (auto incgfx : file.GetIncgfxs()) + { + // WARNING: This must stay in-sync with 'tools/preproc/c_file.cpp'. + std::string arguments_as_path; + for (auto c : incgfx.arguments) + { + if (std::isalnum(c)) + arguments_as_path += c; + else + arguments_as_path += '_'; + } + + // WARNING: This must stay in-sync with 'tools/preproc/c_file.cpp'. + auto target = gfx_root + incgfx.source + arguments_as_path + incgfx.extensions; + size_t target_slash_pos = target.find_last_of('/'); + auto mk_target_basedir = target_slash_pos == std::string::npos + ? "" + : "\t@mkdir -p '" + target.substr(0, target_slash_pos) + "'\n"; + auto rule = mk_target_basedir + "\t$(GFX) $< $@ " + incgfx.arguments + "\n"; + + dependencies.insert(target); + + // If "foo.4bpp.lz" we want a rule for "foo.4bpp", the ".lz" + // doesn't require any arguments. + size_t dot_pos = incgfx.extensions.find_first_of('.', 1); + auto firstTarget = gfx_root + incgfx.source + arguments_as_path + incgfx.extensions.substr(0, dot_pos); + dependencies_gfx_rules[firstTarget] = std::make_pair(incgfx.source, rule); + } for (auto include : file.GetIncludes()) { bool exists = false; @@ -141,7 +184,7 @@ int main(int argc, char **argv) includeDirs.pop_back(); } - if(!makeformat) + if (!makeformat) { for (const std::string &path : dependencies) { @@ -155,8 +198,6 @@ int main(int argc, char **argv) std::ofstream output(make_outfile); // Print a make rule for the object file - size_t ext_pos = make_outfile.find_last_of("."); - auto object_file = make_outfile.substr(0, ext_pos + 1) + "o"; output << object_file.c_str() << ":"; for (const std::string &path : dependencies) { @@ -180,6 +221,27 @@ int main(int argc, char **argv) output << path << ":\n"; } + // Graphics rules + // GNU make will issue a warning if there is more than one + // recipe for a target. This would occur whenever a target is + // 'INCGFX'ed multiple times, which is something that happens a + // few times in vanilla. As a workaround, we maintain a variable + // with all the 'INCGFX' targets and only emit the recipe if + // the target is not in that variable. This is safe, because + // targets with the same name necessarily have the same recipe. + output + << "ifndef _INCGFX\n" + << "_INCGFX :=\n" + << "endif\n"; + for (auto gfx_rule : dependencies_gfx_rules) + { + output + << "ifeq (,$(findstring " << gfx_rule.first << ",$(_INCGFX)))\n" + << "_INCGFX += " << gfx_rule.first << "\n" + << gfx_rule.first << ": " << gfx_rule.second.first << "\n" << gfx_rule.second.second + << "endif\n"; + } + output.flush(); output.close(); } diff --git a/tools/scaninc/scaninc.h b/tools/scaninc/scaninc.h index 30cc9611c1..902d8bea16 100644 --- a/tools/scaninc/scaninc.h +++ b/tools/scaninc/scaninc.h @@ -56,4 +56,18 @@ do { \ #define SCANINC_MAX_PATH 255 +struct Incgfx +{ + std::string source; + std::string extensions; + std::string arguments; + + bool operator<(const struct Incgfx &rhs) const + { + return source < rhs.source + || extensions < rhs.extensions + || arguments < rhs.arguments; + } +}; + #endif // SCANINC_H diff --git a/tools/scaninc/source_file.cpp b/tools/scaninc/source_file.cpp index 9d188eb738..e90959f7b3 100644 --- a/tools/scaninc/source_file.cpp +++ b/tools/scaninc/source_file.cpp @@ -72,6 +72,7 @@ SourceFile::SourceFile(std::string path) { AsmFile file(path); std::set incbins; + std::set incgfxs; std::set includes; IncDirectiveType incDirectiveType; @@ -85,7 +86,7 @@ SourceFile::SourceFile(std::string path) incbins.insert(outputPath); } - new (&m_source_file.asm_wrapper) SourceFile::InnerUnion::AsmWrapper{incbins, includes}; + new (&m_source_file.asm_wrapper) SourceFile::InnerUnion::AsmWrapper{incbins, incgfxs, includes}; } } @@ -103,6 +104,7 @@ SourceFile::~SourceFile() else { m_source_file.asm_wrapper.asm_incbins.~set(); + m_source_file.asm_wrapper.asm_incgfxs.~set(); m_source_file.asm_wrapper.asm_includes.~set(); } } @@ -115,6 +117,14 @@ const std::set& SourceFile::GetIncbins() return m_source_file.asm_wrapper.asm_incbins; } +const std::set& SourceFile::GetIncgfxs() +{ + if (m_file_type == SourceFileType::Cpp || m_file_type == SourceFileType::Header) + return m_source_file.c_file.GetIncgfxs(); + else + return m_source_file.asm_wrapper.asm_incgfxs; +} + const std::set& SourceFile::GetIncludes() { if (m_file_type == SourceFileType::Cpp || m_file_type == SourceFileType::Header) diff --git a/tools/scaninc/source_file.h b/tools/scaninc/source_file.h index 854b3f116b..91cdd67bdd 100644 --- a/tools/scaninc/source_file.h +++ b/tools/scaninc/source_file.h @@ -46,8 +46,8 @@ public: SourceFile(SourceFile&&) = delete; SourceFile& operator =(SourceFile const&) = delete; SourceFile& operator =(SourceFile&&) = delete; - bool HasIncbins(); const std::set& GetIncbins(); + const std::set& GetIncgfxs(); const std::set& GetIncludes(); std::string& GetSrcDir(); SourceFileType FileType(); @@ -57,6 +57,7 @@ private: CFile c_file; struct AsmWrapper { std::set asm_incbins; + std::set asm_incgfxs; std::set asm_includes; } asm_wrapper;