mirror of
https://github.com/pret/pokegold-spaceworld.git
synced 2026-04-26 00:05:12 -05:00
Port scan_includes.c from pokepicross
This commit is contained in:
parent
7e49b5ebc6
commit
09689c6ffd
25
.gitignore
vendored
25
.gitignore
vendored
|
|
@ -10,30 +10,15 @@
|
|||
!shim.sym
|
||||
|
||||
# build artifacts
|
||||
build/
|
||||
*.d
|
||||
*.o
|
||||
*.2bpp
|
||||
*.1bpp
|
||||
*.pic
|
||||
*.pcm
|
||||
shim.asm
|
||||
/build
|
||||
|
||||
# build utilities
|
||||
# build tools
|
||||
tools/scan_includes
|
||||
tools/pkmncompress
|
||||
tools/gfx
|
||||
tools/fix_sections_directory.py
|
||||
*.exe
|
||||
*.pyc
|
||||
__pycache__
|
||||
|
||||
# editor files
|
||||
*~
|
||||
.idea/
|
||||
|
||||
# extra utilities
|
||||
coverage.png
|
||||
coverage.log
|
||||
temp/
|
||||
pokegold-spaceworld-gen.link
|
||||
*.txt
|
||||
# utility output
|
||||
/coverage.png
|
||||
|
|
|
|||
21
Makefile
21
Makefile
|
|
@ -26,6 +26,8 @@ RGBLINK ?= $(RGBDS)rgblink
|
|||
|
||||
RGBASMFLAGS := -h -E -i $(BUILD)/ -DGOLD
|
||||
|
||||
SCAN_INCLUDES := tools/scan_includes
|
||||
|
||||
tools/gfx :=
|
||||
|
||||
|
||||
|
|
@ -41,19 +43,19 @@ compare: $(ROM) $(CORRECTEDROM)
|
|||
$(MD5) roms.md5
|
||||
|
||||
.PHONY: tools
|
||||
tools tools/pkmncompress tools/gfx:
|
||||
tools tools/pkmncompress tools/gfx tools/scan_includes:
|
||||
"$(MAKE)" -C tools/
|
||||
|
||||
# Remove files generated by the build process.
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf $(ROM) $(CORRECTEDROM) $(BUILD) $(ROMS:.gb=.sym) $(ROMS:.gb=.map) *.d
|
||||
rm -rf $(ROM) $(CORRECTEDROM) $(BUILD) $(ROMS:.gb=.sym) $(ROMS:.gb=.map)
|
||||
"$(MAKE)" -C tools clean
|
||||
|
||||
# Remove generated files except for graphics.
|
||||
.PHONY: tidy
|
||||
tidy:
|
||||
rm -rf $(ROM) $(CORRECTEDROM) $(OBJS) $(ROMS:.gb=.sym) $(ROMS:.gb=.map) *.d
|
||||
rm -rf $(ROM) $(CORRECTEDROM) $(OBJS) $(ROMS:.gb=.sym) $(ROMS:.gb=.map)
|
||||
|
||||
# Visualize disassembly progress.
|
||||
.PHONY: coverage
|
||||
|
|
@ -68,8 +70,8 @@ $(CORRECTEDROM): %-correctheader.gb: %.gb
|
|||
cp $(<:.gb=.sym) $(@:.gb=.sym)
|
||||
$(RGBFIX) -f hg -m 0x10 $@
|
||||
|
||||
$(ROM): poke%-spaceworld.gb: $(OBJS) | $(BASEROM)
|
||||
$(RGBLINK) -d -n $(@:.gb=.sym) -m $(@:.gb=.map) -l layout.link -O $(BASEROM) -o $@ $^
|
||||
$(ROM): poke%-spaceworld.gb: layout.link $(OBJS) | $(BASEROM)
|
||||
$(RGBLINK) -d -n $(@:.gb=.sym) -m $(@:.gb=.map) -l layout.link -O $(BASEROM) -o $@ $(filter-out $<, $^)
|
||||
$(RGBFIX) -f lh -k 01 -l 0x33 -m 0x03 -p 0 -r 3 -t "POKEMON2$(shell echo $* | cut -d _ -f 1 | tr '[:lower:]' '[:upper:]')" $@
|
||||
|
||||
$(BASEROM):
|
||||
|
|
@ -85,6 +87,9 @@ $(BUILD)/%.o: $(BUILD)/%.asm | $$(dir $$@)
|
|||
$(BUILD)/%.o: %.asm | $$(dir $$@)
|
||||
$(RGBASM) $(RGBASMFLAGS) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(BUILD)/%.d: %.asm | $$(dir $$@) $(SCAN_INCLUDES)
|
||||
@$(SCAN_INCLUDES) -b $(BUILD)/ -i $(BUILD)/ -i ./ -o $@ -t $(@:.d=.o) $<
|
||||
|
||||
|
||||
### Misc file-specific graphics rules
|
||||
|
||||
|
|
@ -137,8 +142,6 @@ $(BUILD)/%.tilemap: %.png | $$(dir $$@)
|
|||
|
||||
### Scan .asm files for INCLUDE dependencies
|
||||
|
||||
DEPENDENCY_SCAN_EXIT_STATUS := $(shell $(PYTHON) tools/scan_includes.py $(BUILD:%=-b %) $(ASMFILES) > dependencies.d; echo $$?)
|
||||
ifneq ($(DEPENDENCY_SCAN_EXIT_STATUS), 0)
|
||||
$(error Dependency scan failed)
|
||||
ifeq (,$(filter clean tools,$(MAKECMDGOALS)))
|
||||
-include $(patsubst %.o, %.d, $(OBJS))
|
||||
endif
|
||||
include dependencies.d
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ CFLAGS := -O3 -std=c99 -Wall -Wextra
|
|||
|
||||
tools := \
|
||||
pkmncompress \
|
||||
gfx
|
||||
gfx \
|
||||
scan_includes
|
||||
|
||||
all: $(tools)
|
||||
|
||||
|
|
|
|||
275
tools/scan_includes.c
Normal file
275
tools/scan_includes.c
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
#define _DEFAULT_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void usage(void) {
|
||||
printf("Usage: scan_includes [-h] [-o output] [-s] [-i path] [-b path] filename\n"
|
||||
"-h, --help\n"
|
||||
" Print usage and exit\n"
|
||||
"-o, --output\n"
|
||||
" Filename to store the output in\n"
|
||||
"-s, --strict\n"
|
||||
" Fail if a file cannot be read\n"
|
||||
"-i, --include\n"
|
||||
" Add an include path\n"
|
||||
"-b, --build-prefix\n"
|
||||
" Set path to generate non-existing files in\n"
|
||||
"-t, --target\n"
|
||||
" Generate a makefile fragment for target file\n");
|
||||
}
|
||||
|
||||
struct Options {
|
||||
bool help;
|
||||
char *output;
|
||||
bool strict;
|
||||
char **include_paths;
|
||||
int include_paths_len;
|
||||
char *build_prefix;
|
||||
char *target;
|
||||
};
|
||||
|
||||
struct Options Options = {0};
|
||||
|
||||
void *xmalloc(size_t size)
|
||||
{
|
||||
void *ptr = malloc(size);
|
||||
if (!ptr) {
|
||||
perror("malloc");
|
||||
exit(1);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *xrealloc(void *ptr, size_t size)
|
||||
{
|
||||
ptr = realloc(ptr, size);
|
||||
if (!ptr) {
|
||||
perror("realloc");
|
||||
exit(1);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void options_add_file(char *filename)
|
||||
{
|
||||
Options.include_paths_len++;
|
||||
Options.include_paths = xrealloc(Options.include_paths,
|
||||
sizeof(Options.include_paths[0]) * Options.include_paths_len);
|
||||
Options.include_paths[Options.include_paths_len - 1] = filename;
|
||||
}
|
||||
|
||||
void filelist_append(char **string, char *append)
|
||||
{
|
||||
size_t orig_len = *string ? strlen(*string) : 0;
|
||||
size_t len = orig_len + strlen(append) + 2;
|
||||
if (orig_len == 0) len--;
|
||||
*string = xrealloc(*string, len);
|
||||
|
||||
if (orig_len != 0) {
|
||||
(*string)[orig_len + 0] = ' ';
|
||||
(*string)[orig_len + 1] = '\0';
|
||||
} else {
|
||||
(*string)[orig_len + 0] = '\0';
|
||||
}
|
||||
strcat(*string, append);
|
||||
}
|
||||
|
||||
char *joinpath(char *dir, char *file)
|
||||
{
|
||||
size_t len = strlen(dir) + strlen(file) + 1;
|
||||
char *path = xmalloc(len);
|
||||
snprintf(path, len, "%s%s", dir, file);
|
||||
return path;
|
||||
}
|
||||
|
||||
char *find_file(char *filename) {
|
||||
if (access(filename, F_OK) == 0) {
|
||||
char *fname = strdup(filename);
|
||||
if (!fname) {
|
||||
perror("strdup");
|
||||
exit(1);
|
||||
}
|
||||
return fname;
|
||||
}
|
||||
|
||||
// Try to find file in any of the include paths
|
||||
for (int i = 0; i < Options.include_paths_len; i++) {
|
||||
char *path = joinpath(Options.include_paths[i], filename);
|
||||
if (access(path, F_OK) == 0) return path;
|
||||
free(path);
|
||||
}
|
||||
|
||||
if (Options.strict) {
|
||||
fprintf(stderr, "Could not open file: '%s'\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void scan_file(char **includes, char **incbins, char *filename) {
|
||||
FILE *f = fopen(filename, "r");
|
||||
if (!f) return;
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long size = ftell(f);
|
||||
rewind(f);
|
||||
|
||||
char *buffer = xmalloc(size + 1);
|
||||
char *orig = buffer;
|
||||
size = fread(buffer, 1, size, f);
|
||||
buffer[size] = '\0';
|
||||
fclose(f);
|
||||
|
||||
for (; buffer && (buffer - orig < size); buffer++) {
|
||||
bool is_include = false;
|
||||
bool is_incbin = false;
|
||||
switch (*buffer) {
|
||||
case ';':
|
||||
buffer = strchr(buffer, '\n');
|
||||
if (!buffer) {
|
||||
fprintf(stderr, "%s: no newline at end of file\n", filename);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case '"':
|
||||
buffer++;
|
||||
buffer = strchr(buffer, '"');
|
||||
if (!buffer) {
|
||||
fprintf(stderr, "%s: unterminated string\n", filename);
|
||||
break;
|
||||
}
|
||||
buffer++;
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
case 'I':
|
||||
if ((strncmp(buffer, "INCBIN", 6) == 0) ||
|
||||
(strncmp(buffer, "incbin", 6) == 0)) {
|
||||
is_incbin = true;
|
||||
} else if ((strncmp(buffer, "INCLUDE", 7) == 0) ||
|
||||
(strncmp(buffer, "include", 7) == 0)) {
|
||||
is_include = true;
|
||||
}
|
||||
if (is_incbin || is_include) {
|
||||
buffer = strchr(buffer, '"');
|
||||
if (!buffer) {
|
||||
break;
|
||||
}
|
||||
buffer++;
|
||||
|
||||
size_t length = strcspn(buffer, "\"");
|
||||
buffer[length] = '\0';
|
||||
char *include = find_file(buffer);
|
||||
|
||||
char *append = buffer;
|
||||
if (Options.build_prefix) {
|
||||
append = include;
|
||||
if (!include) {
|
||||
append = joinpath(Options.build_prefix, buffer);
|
||||
}
|
||||
}
|
||||
if (is_include) filelist_append(includes, append);
|
||||
if (is_incbin) filelist_append(incbins, append);
|
||||
if (Options.build_prefix && !include) free(append);
|
||||
|
||||
if (include && is_include) scan_file(includes, incbins, include);
|
||||
|
||||
if (include) free(include);
|
||||
buffer += length + 1;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
if (!buffer) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
free(orig);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int i = 0;
|
||||
struct option long_options[] = {
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"output", required_argument, 0, 'o'},
|
||||
{"strict", no_argument, 0, 's'},
|
||||
{"include", required_argument, 0, 'i'},
|
||||
{"build-prefix", required_argument, 0, 'b'},
|
||||
{"target", required_argument, 0, 't'},
|
||||
{0}
|
||||
};
|
||||
int opt = -1;
|
||||
while ((opt = getopt_long(argc, argv, "ho:si:b:t:", long_options, &i)) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
Options.help = true;
|
||||
break;
|
||||
case 'o':
|
||||
Options.output = optarg;
|
||||
break;
|
||||
case 's':
|
||||
Options.strict = true;
|
||||
break;
|
||||
case 'i':
|
||||
options_add_file(optarg);
|
||||
break;
|
||||
case 'b':
|
||||
Options.build_prefix = optarg;
|
||||
break;
|
||||
case 't':
|
||||
Options.target = optarg;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (Options.help) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
if (argc < 1) {
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char *includes = NULL;
|
||||
char *incbins = NULL;
|
||||
char *filename = find_file(argv[0]);
|
||||
if (filename) scan_file(&includes, &incbins, filename);
|
||||
|
||||
FILE *f = stdout;
|
||||
if (Options.output) {
|
||||
f = fopen(Options.output, "w");
|
||||
if (!f) {
|
||||
perror("fopen");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (Options.target) {
|
||||
fprintf(f, "%s:", Options.target);
|
||||
}
|
||||
if (includes) fprintf(f, " %s", includes);
|
||||
if (incbins) fprintf(f, " %s", incbins);
|
||||
fprintf(f, "\n");
|
||||
|
||||
if (Options.target && Options.output) {
|
||||
fprintf(f, "%s:", Options.output);
|
||||
if (includes) fprintf(f, " %s", includes);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Get all the dependencies of RGBDS assembly files recursively,
|
||||
and output them using Make dependency syntax.
|
||||
"""
|
||||
|
||||
# Script adapted from the Telefang disassembly / fan translation project.
|
||||
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
if sys.version_info[0] < 3:
|
||||
from codecs import open
|
||||
|
||||
INCLUDE_RE = re.compile(r"^(?:[a-zA-Z0-9_.]+:?:?)?\s*(INC(?:LUDE|BIN))", re.IGNORECASE)
|
||||
|
||||
def dependencies_in(asm_file_paths, build_dirs=[]):
|
||||
asm_file_paths = list(asm_file_paths)
|
||||
dependencies = {}
|
||||
|
||||
for path in asm_file_paths:
|
||||
if path not in dependencies:
|
||||
asm_dependencies, bin_dependencies = shallow_dependencies_of(path, build_dirs)
|
||||
dependencies[path] = asm_dependencies | bin_dependencies
|
||||
asm_file_paths += asm_dependencies
|
||||
|
||||
return dependencies
|
||||
|
||||
def shallow_dependencies_of(asm_file_path, build_dirs=[]):
|
||||
asm_dependencies = set()
|
||||
bin_dependencies = set()
|
||||
|
||||
with open(asm_file_path, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
m = INCLUDE_RE.match(line)
|
||||
if m is None:
|
||||
continue
|
||||
|
||||
keyword = m.group(1).upper()
|
||||
line = line.split(';', 1)[0]
|
||||
# RGBDS treats absolute(-looking) paths as relative,
|
||||
# so leading slashes should be stripped.
|
||||
path = line[line.index('"') + 1:line.rindex('"')].lstrip('/')
|
||||
if keyword == 'INCLUDE':
|
||||
asm_dependencies.add(path)
|
||||
else:
|
||||
if os.path.isfile(path) or not build_dirs:
|
||||
bin_dependencies.add(path)
|
||||
else:
|
||||
bin_dependencies.update(os.path.join(d, path) for d in build_dirs)
|
||||
|
||||
return asm_dependencies, bin_dependencies
|
||||
|
||||
def main(argv):
|
||||
script_name = os.path.basename(__file__)
|
||||
parser = argparse.ArgumentParser(prog=script_name, add_help=False)
|
||||
parser.add_argument('-h', '--help', action='help', help="Show this help and exit.")
|
||||
parser.add_argument('-b', metavar="build directories", action='append', default=[],
|
||||
help="Build directory to generate dependencies for "
|
||||
"if files don't exist at the exact path specified. "
|
||||
"Multiple build directories may be specified.")
|
||||
parser.add_argument('files', metavar='file', nargs='+',
|
||||
help="An assembly file to generate dependencies for.")
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
for path, dependencies in dependencies_in(args.files, args.b).items():
|
||||
# It seems that if A depends on B which depends on C, and
|
||||
# C is modified, Make needs you to change the modification
|
||||
# time of B too. That's the reason for the "@touch $@".
|
||||
# This does mean mtimes on .asm files are updated when building,
|
||||
# but the alternative opens up a whole can of problems.
|
||||
if dependencies:
|
||||
print("{}: {}\n\t@touch $@".format(path, ' '.join(dependencies)))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
Loading…
Reference in New Issue
Block a user