2021-01-14 12:22:38 +01:00
#!/usr/bin/env python3
2021-01-14 04:56:02 +01:00
import re
2021-01-14 11:25:55 +01:00
import os , sys
2021-01-14 04:56:02 +01:00
from glob import glob
import ninja_syntax
from argparse import ArgumentParser
import asyncio
from subprocess import PIPE
2021-01-16 16:18:06 +01:00
import subprocess
2021-01-14 04:56:02 +01:00
2021-01-24 16:19:48 +01:00
sys . path . append ( os . path . dirname ( __file__ ) + " /tools/splat " )
2021-01-14 12:22:38 +01:00
import split
2021-02-06 20:05:57 +01:00
from segtypes . n64 . code import Subsegment
2021-01-14 04:56:02 +01:00
2021-01-27 11:56:57 +01:00
INCLUDE_ASM_RE = re . compile ( r " ___INCLUDE_ASM \ ([^,]+, ([^,]+), ([^,)]+) " ) # note _ prefix
2021-02-22 10:21:23 +01:00
SUPPORTED_VERSIONS = [ " us " , " jp " ]
TARGET = " papermario "
2021-01-15 00:55:05 +01:00
2021-01-14 04:56:02 +01:00
def obj ( path : str ) :
2021-02-22 10:21:23 +01:00
if not path . startswith ( " ver/ " ) :
path = f " ver/ { version } /build/ { path } "
path = re . sub ( r " /assets/ " , " /build/ " , path ) # XXX what about other asset dirs?
2021-01-14 11:25:55 +01:00
return path + " .o "
2021-02-22 10:21:23 +01:00
def read_splat ( splat_config : str , version : str ) :
2021-01-14 11:25:55 +01:00
import argparse
import yaml
2021-02-05 11:02:53 +01:00
from segtypes . n64 . code import N64SegCode
2021-03-23 03:29:47 +01:00
from util import options
2021-01-14 11:25:55 +01:00
# Load config
with open ( splat_config ) as f :
config = yaml . safe_load ( f . read ( ) )
2021-03-23 03:29:47 +01:00
options . initialize ( config )
2021-01-14 11:25:55 +01:00
assert options . get ( " ld_o_replace_extension " , True ) == False
# Initialize segments
2021-03-23 03:29:47 +01:00
all_segments = split . initialize_segments ( splat_config , config [ " segments " ] )
2021-01-14 11:25:55 +01:00
objects = set ( )
2021-01-14 12:13:33 +01:00
segments = { }
2021-01-14 11:25:55 +01:00
for segment in all_segments :
for subdir , path , obj_type , start in segment . get_ld_files ( ) :
2021-02-22 10:21:23 +01:00
# src workaround
if subdir . startswith ( " ../../ " ) :
subdir = subdir [ 6 : ]
2021-02-06 04:36:46 +01:00
if path . endswith ( " .c " ) or path . endswith ( " .s " ) or path . endswith ( " .data " ) or path . endswith ( " .rodata " ) :
path = subdir + " / " + path
else :
assert subdir == " assets " , subdir + " " + path
2021-02-22 10:21:23 +01:00
subdir = " ver/ " + version + " /assets "
2021-01-14 11:25:55 +01:00
2021-01-14 12:13:33 +01:00
objects . add ( path )
segments [ path ] = segment
2021-01-14 11:25:55 +01:00
2021-02-05 11:02:53 +01:00
if isinstance ( segment , N64SegCode ) :
2021-02-06 20:05:57 +01:00
for split_file in segment . subsegments :
if split_file . type in [ " i4 " , " i8 " , " ia4 " , " ia8 " , " ia16 " , " rgba16 " , " rgba32 " , " ci4 " , " ci8 " , " palette " ] :
2021-02-05 11:02:53 +01:00
path = os . path . join (
2021-02-06 04:36:46 +01:00
#segment.get_subdir(split_file["subtype"]),
2021-02-06 20:05:57 +01:00
split_file . name + " . " + split_file . get_ext ( )
2021-02-05 11:02:53 +01:00
)
if path in segments :
segments [ path ] = split_file
2021-01-14 12:13:33 +01:00
# note: `objects` lacks .o extensions
return objects , segments
2021-01-14 04:56:02 +01:00
2021-02-10 17:49:00 +01:00
def rm_recursive ( path ) :
from pathlib import Path
path = Path ( path )
2021-02-22 10:21:23 +01:00
if path . exists ( ) :
if path . is_dir ( ) :
for f in path . iterdir ( ) :
rm_recursive ( f )
2021-02-10 17:49:00 +01:00
2021-02-22 10:21:23 +01:00
path . rmdir ( )
else :
path . unlink ( )
2021-02-10 17:49:00 +01:00
2021-01-14 04:56:02 +01:00
async def shell ( cmd : str ) :
async with task_sem :
proc = await asyncio . create_subprocess_shell ( cmd , stdout = PIPE , stderr = PIPE )
stdout , stderr = await proc . communicate ( )
assert proc . returncode == 0 , f " { cmd } failed: { stderr } "
2021-01-14 05:16:42 +01:00
return stdout . decode ( " utf-8 " ) , stderr . decode ( " utf-8 " )
2021-01-14 04:56:02 +01:00
2021-02-22 10:21:23 +01:00
async def shell_status ( cmd : str ) :
async with task_sem :
proc = await asyncio . create_subprocess_shell ( cmd , stdout = PIPE , stderr = PIPE )
stdout , stderr = await proc . communicate ( )
2021-01-14 04:56:02 +01:00
2021-02-22 10:21:23 +01:00
return proc . returncode
2021-01-14 04:56:02 +01:00
2021-01-14 11:25:55 +01:00
def build_yay0_file ( bin_file : str ) :
2021-02-22 10:21:23 +01:00
yay0_file = f " ver/ { version } /build/ { os . path . splitext ( bin_file ) [ 0 ] } .Yay0 "
2021-02-06 04:36:46 +01:00
n . build ( yay0_file , " yay0compress " , find_asset ( bin_file ) , implicit = " tools/Yay0compress " )
2021-01-14 11:25:55 +01:00
build_bin_object ( yay0_file )
2021-01-14 04:56:02 +01:00
2021-01-14 11:25:55 +01:00
def build_bin_object ( bin_file : str ) :
n . build ( obj ( bin_file ) , " bin " , bin_file )
2021-01-14 04:56:02 +01:00
2021-01-14 12:13:33 +01:00
def build_image ( f : str , segment ) :
path , img_type , png = f . rsplit ( " . " , 2 )
2021-02-22 10:21:23 +01:00
out = f " ver/ { version } /build/ " + path + " . " + img_type + " .png "
2021-01-14 12:13:33 +01:00
flags = " "
2021-02-06 23:26:35 +01:00
if img_type != " palette " and not isinstance ( segment , dict ) :
2021-01-14 12:13:33 +01:00
if segment . flip_horizontal :
flags + = " --flip-x "
if segment . flip_vertical :
flags + = " --flip-y "
2021-02-06 04:36:46 +01:00
n . build ( out , " img " , find_asset ( path + " .png " ) , implicit = " tools/img/build.py " , variables = {
2021-01-14 12:13:33 +01:00
" img_type " : img_type ,
" img_flags " : flags ,
} )
build_bin_object ( out )
2021-01-14 11:49:14 +01:00
2021-01-16 16:18:06 +01:00
def cmd_exists ( cmd ) :
return subprocess . call ( " type " + cmd , shell = True ,
stdout = subprocess . PIPE , stderr = subprocess . PIPE ) == 0
2021-02-06 04:36:46 +01:00
def find_asset_dir ( path ) :
2021-02-22 10:21:23 +01:00
global ASSET_DIRS
2021-02-06 04:36:46 +01:00
for d in ASSET_DIRS :
if os . path . exists ( d + " / " + path ) :
return d
2021-02-06 23:26:35 +01:00
print ( " Unable to find asset: " + path )
2021-02-22 10:21:23 +01:00
print ( " The asset dump may be incomplete. Try: " )
print ( " ./configure.py --clean " )
2021-02-06 04:36:46 +01:00
exit ( 1 )
def find_asset ( path ) :
return find_asset_dir ( path ) + " / " + path
2021-01-14 04:56:02 +01:00
async def main ( ) :
2021-02-22 10:21:23 +01:00
global n , cpp , task_sem , num_tasks , num_tasks_done , ASSET_DIRS , version
2021-01-14 04:56:02 +01:00
2021-01-15 12:26:19 +01:00
task_sem = asyncio . Semaphore ( 8 )
2021-01-15 00:55:05 +01:00
parser = ArgumentParser ( description = " Paper Mario build.ninja generator " )
2021-02-22 10:21:23 +01:00
parser . add_argument ( " version " , nargs = " * " , default = [ ] , help = " Version(s) to configure for. Most tools will operate on the first-provided only. Supported versions: " + ' , ' . join ( SUPPORTED_VERSIONS ) )
2021-01-15 00:55:05 +01:00
parser . add_argument ( " --cpp " , help = " GNU C preprocessor command " )
2021-01-15 19:18:12 +01:00
parser . add_argument ( " --cflags " , default = " " , help = " Extra cc/cpp flags " )
2021-02-22 10:21:23 +01:00
parser . add_argument ( " --no-splat " , action = " store_true " , help = " Don ' t split assets from the baserom(s) " )
2021-02-10 17:49:00 +01:00
parser . add_argument ( " --clean " , action = " store_true " , help = " Delete assets and previously-built files " )
2021-03-23 03:29:47 +01:00
parser . add_argument ( " --depend-on-s " , action = " store_true " , help = " Configure dependencies on .s files for c files that include them " )
2021-01-14 04:56:02 +01:00
args = parser . parse_args ( )
2021-02-22 10:21:23 +01:00
versions = args . version
# default version behaviour is to only do those that exist
if len ( versions ) == 0 :
for version in SUPPORTED_VERSIONS :
rom = f " ver/ { version } /baserom.z64 "
if os . path . exists ( rom ) :
versions . append ( version )
if len ( versions ) == 0 :
print ( " error: no baserom.z64 files could be found in the ver/*/ directories. " )
exit ( 1 )
print ( " Configuring for versions: " + ' , ' . join ( versions ) )
print ( " " )
2021-01-14 04:56:02 +01:00
# on macOS, /usr/bin/cpp defaults to clang rather than gcc (but we need gcc's)
2021-01-15 12:26:19 +01:00
if args . cpp is None and sys . platform == " darwin " and " Free Software Foundation " not in ( await shell ( " cpp --version " ) ) [ 0 ] :
2021-01-15 00:55:05 +01:00
print ( " error: system C preprocessor is not GNU! " )
print ( " This is a known issue on macOS - only clang ' s cpp is installed by default. " )
print ( " Use ' brew ' to obtain GNU cpp, then run this script again with the --cpp option, e.g. " )
print ( " ./configure.py --cpp cpp-10 " )
2021-01-14 04:56:02 +01:00
exit ( 1 )
cpp = args . cpp or " cpp "
2021-02-10 17:49:00 +01:00
if args . clean :
print ( " Cleaning... " )
await shell ( " ninja -t clean " )
2021-02-22 10:21:23 +01:00
rm_recursive ( f " .splat_cache " )
for version in versions :
rm_recursive ( f " ver/ { version } /assets " )
rm_recursive ( f " ver/ { version } /build " )
rm_recursive ( f " ver/ { version } /.splat_cache " )
2021-02-10 17:49:00 +01:00
2021-01-27 11:56:57 +01:00
if not args . no_splat :
# compile splat dependencies
await shell ( " make -C tools/splat " )
2021-02-22 10:21:23 +01:00
has_any_rom = False
for version in versions :
rom = f " ver/ { version } /baserom.z64 "
has_rom = False
try :
with open ( rom , " rb " ) as f :
has_rom = True
has_any_rom = True
except IOError :
print ( f " error: could not find baserom file ' { rom } ' ! " )
if len ( versions ) > = 2 :
print ( f " You can avoid building version ' { version } ' by specifying versions on the command-line: " )
print ( f " ./configure.py { ' ' . join ( ver for ver in versions if ver != version ) } " )
exit ( 1 )
2021-01-27 11:56:57 +01:00
2021-02-22 10:21:23 +01:00
if has_rom :
print ( f " Splitting assets from { rom } " , end = " " )
split . main (
f " ver/ { version } /splat.yaml " ,
f " ver/ { version } " ,
rom ,
[ " ld " , " bin " , " Yay0 " , " PaperMarioMapFS " , " PaperMarioMessages " , " img " , " PaperMarioNpcSprites " ] ,
False ,
False ,
)
print ( " " )
2021-01-27 11:56:57 +01:00
2021-02-22 10:21:23 +01:00
print ( " Configuring build... " )
2021-01-15 00:55:05 +01:00
2021-01-14 04:56:02 +01:00
n = ninja_syntax . Writer ( open ( " build.ninja " , " w " ) , width = 120 )
2021-02-22 10:21:23 +01:00
cflags = " " + args . cflags
2021-02-04 14:54:08 +01:00
iconv = " tools/iconv.py UTF-8 SHIFT-JIS " if sys . platform == " darwin " else " iconv --from UTF-8 --to SHIFT-JIS "
cross = " mips-linux-gnu- "
2021-01-15 19:18:12 +01:00
2021-01-15 00:55:05 +01:00
n . variable ( " target " , TARGET )
2021-02-04 14:54:08 +01:00
n . variable ( " cross " , cross )
2021-01-14 04:56:02 +01:00
n . variable ( " python " , sys . executable )
2021-01-16 07:34:21 +01:00
if sys . platform == " darwin " :
os_dir = " mac "
elif sys . platform == " linux " :
if os . uname ( ) [ 4 ] == " aarch64 " :
os_dir = " arm "
else :
os_dir = " linux "
else :
print ( f " Unsupported platform { sys . platform } " )
sys . exit ( 1 )
2021-02-22 10:21:23 +01:00
# $version
2021-01-14 04:56:02 +01:00
n . rule ( " cc " ,
2021-02-22 10:21:23 +01:00
command = f " bash -o pipefail -c ' { cpp } -Iver/$version/build/include -Iinclude -Isrc -D _LANGUAGE_C -D _FINALROM -D VERSION=$version -ffreestanding -DF3DEX_GBI_2 -D_MIPS_SZLONG=32 { args . cflags } -MD -MF $out.d $in -o - | { iconv } | tools/ { os_dir } /cc1 -O2 -quiet -G 0 -mcpu=vr4300 -mfix4300 -mips3 -mgp32 -mfp32 -Wuninitialized -Wshadow { args . cflags } -o - | tools/ { os_dir } /mips-nintendo-nu64-as -EB -G 0 - -o $out ' " ,
2021-01-14 04:56:02 +01:00
description = " cc $in " ,
depfile = " $out.d " ,
deps = " gcc " )
n . rule ( " cc_dsl " ,
2021-02-22 10:21:23 +01:00
command = f " bash -o pipefail -c ' { cpp } -Iver/$version/build/include -Iinclude -Isrc -D _LANGUAGE_C -D _FINALROM -D VERSION=$version -ffreestanding -DF3DEX_GBI_2 -D_MIPS_SZLONG=32 { args . cflags } -MD -MF $out.d $in -o - | $python tools/compile_dsl_macros.py | { iconv } | tools/ { os_dir } /cc1 -O2 -quiet -G 0 -mcpu=vr4300 -mfix4300 -mips3 -mgp32 -mfp32 -Wuninitialized -Wshadow { args . cflags } -o - | tools/ { os_dir } /mips-nintendo-nu64-as -EB -G 0 - -o $out ' " ,
description = " dsl $in " ,
2021-01-14 04:56:02 +01:00
depfile = " $out.d " ,
deps = " gcc " )
n . newline ( )
2021-03-26 16:01:57 +01:00
# KMC gcc crashes if the argument string is too long, so preprocess input separately to minimize arguments
# KMC gcc doesn't support input on stdin, so a temp file has to be made for the preprocessor output
n . rule ( " cc_kmc " ,
command = f " bash -o pipefail -c ' { cpp } -Iver/$version/build/include -Iinclude -Iinclude/PR -Isrc -D _LANGUAGE_C -D _FINALROM -D VERSION=$version -ffreestanding -DF3DEX_GBI_2 -D_MIPS_SZLONG=32 { args . cflags } -MD -MF $out.d $in -o $out.i && export WINEPATH=tools/kmc/BIN && wine exew32 gcc -O3 -c -G0 -mgp32 -mfp32 -mips3 $out.i -o $out ' && { cross } strip $out -N $in " ,
description = " dsl $in " ,
depfile = " $out.d " ,
deps = " gcc " )
n . newline ( )
2021-01-14 04:56:02 +01:00
2021-02-04 14:54:08 +01:00
with open ( " tools/permuter_settings.toml " , " w " ) as f :
2021-02-22 10:21:23 +01:00
version = versions [ 0 ]
2021-03-10 05:39:12 +01:00
f . write ( f " compiler_command = \" { cpp } -Iver/ { version } /build/include -Iinclude -Isrc -DPERMUTER -D _LANGUAGE_C -D _FINALROM -D VERSION= { version } -ffreestanding -DF3DEX_GBI_2 -D_MIPS_SZLONG=32 { args . cflags } -D SCRIPT(...)= {{ }} | { iconv } | tools/ { os_dir } /cc1 -O2 -quiet -G 0 -mcpu=vr4300 -mfix4300 -mips3 -mgp32 -mfp32 -Wuninitialized -Wshadow { args . cflags } -o - | tools/ { os_dir } /mips-nintendo-nu64-as -EB -G 0 - \" \n " )
2021-02-04 14:54:08 +01:00
f . write ( f " assembler_command = \" { cross } as -march=vr4300 -mabi=32 \" \n " )
2021-03-19 11:25:14 +01:00
f . write ( " \n " )
f . write ( " [preserve_macros] \n " )
2021-03-19 19:17:02 +01:00
f . write ( " \" g[DS]P.* \" = \" void \" \n " )
2021-02-04 14:54:08 +01:00
2021-02-22 10:21:23 +01:00
# $version
2021-01-14 13:05:55 +01:00
n . rule ( " cpp " ,
2021-02-22 10:21:23 +01:00
command = f " { cpp } -P -DBUILD_DIR=ver/$version/build $in -o $out " ,
description = " cpp $in " ,
2021-01-14 13:05:55 +01:00
depfile = " $out.d " ,
deps = " gcc " )
n . newline ( )
2021-01-14 11:25:55 +01:00
n . rule ( " yay0compress " ,
command = f " tools/Yay0compress $in $out " ,
description = " compress $in " )
n . newline ( )
n . rule ( " bin " ,
command = " $ {cross} ld -r -b binary $in -o $out " ,
description = " bin $in " )
n . newline ( )
2021-01-14 11:49:14 +01:00
n . rule ( " as " ,
2021-03-26 16:01:57 +01:00
command = " $ {cross} as -EB -O0 -G0 -mtune=vr4300 -march=vr4300 -mabi=32 -Iinclude $in -o $out " ,
2021-01-14 11:49:14 +01:00
description = " assemble $in " )
n . newline ( )
2021-01-14 11:25:55 +01:00
2021-01-14 12:57:39 +01:00
# $img_type, $img_flags
2021-01-14 11:59:55 +01:00
n . rule ( " img " ,
2021-02-05 23:09:31 +01:00
command = " $python tools/img/build.py $img_type $in $out $img_flags " ,
2021-01-14 11:59:55 +01:00
description = " image $in " )
2021-02-05 23:09:31 +01:00
n . rule ( " img_header " ,
command = " $python tools/img/header.py $in $out " ,
description = " image_header $in " )
2021-01-14 11:59:55 +01:00
n . newline ( )
2021-01-14 13:25:28 +01:00
# $sprite_id, $sprite_dir, $sprite_name
2021-01-14 12:57:39 +01:00
n . rule ( " sprite_animations_h " ,
command = " $python tools/gen_sprite_animations_h.py $out $sprite_dir $sprite_id " ,
2021-01-14 13:25:28 +01:00
description = " sprite_animations_h $sprite_name ($sprite_id) " )
n . rule ( " npc_sprite " ,
command = " $python tools/compile_npc_sprite.py $out $sprite_dir " ,
description = " npc_sprite $sprite_name ($sprite_id) " )
n . rule ( " npc_sprites " ,
command = " $python tools/compile_npc_sprites.py $out $in " ,
description = " package npc sprites " )
2021-01-14 13:05:55 +01:00
n . newline ( )
n . rule ( " ld_addrs_h " ,
command = " grep -E \" [^ \ . ]+ = \" $in -o | sed ' s/^/extern void* /; s/ =/;/ ' > $out " ,
description = " ld_addrs_h $in " )
2021-01-14 12:57:39 +01:00
n . newline ( )
2021-01-14 20:17:14 +01:00
n . rule ( " msg_combine " ,
2021-02-10 02:36:01 +01:00
command = " $python tools/msg/combine.py $out $in " ,
2021-01-14 20:17:14 +01:00
description = " combine messages " )
n . rule ( " msg " ,
command = " $python tools/msg/parse_compile.py $in $out " ,
description = " msg $in " )
n . newline ( )
2021-01-15 00:55:05 +01:00
n . rule ( " assets " ,
command = " $python tools/build_assets_bin.py $out $in " ,
description = " combine assets " )
n . newline ( )
2021-02-22 10:21:23 +01:00
# $version
2021-01-15 00:55:05 +01:00
n . rule ( " link " ,
2021-02-22 10:21:23 +01:00
command = " $ {cross} ld -T ver/$version/undefined_syms.txt -T ver/$version/undefined_syms_auto.txt -T ver/$version/undefined_funcs_auto.txt -T ver/$version/dead_syms.txt -Map ver/$version/build/$target.map --no-check-sections -T $in -o $out " ,
2021-01-15 00:55:05 +01:00
description = " link $out " )
n . newline ( )
n . rule ( " rom " ,
command = " $ {cross} objcopy $in $out -O binary && tools/n64crc $out " ,
description = " rom $in " )
n . newline ( )
2021-02-22 10:21:23 +01:00
# $version
n . rule ( " checksum " ,
command = f " sha1sum -c ver/$version/checksum.sha1 && touch $out " ,
description = " compare " )
2021-01-15 00:55:05 +01:00
n . newline ( )
n . rule ( " cc_modern_exe " , command = " cc $in -O3 -o $out " )
n . newline ( )
2021-02-22 10:21:23 +01:00
for version in versions :
objects , segments = read_splat ( f " ver/ { version } /splat.yaml " , version ) # no .o extensions!
#c_files = (f for f in objects if f.endswith(".c"))
n . build ( f " ver/ { version } /build/$target.ld " , " cpp " , f " ver/ { version } /$target.ld " , variables = { " version " : version } )
n . build ( f " ver/ { version } /build/$target.elf " , " link " , f " ver/ { version } /build/$target.ld " , implicit = [ obj ( o ) for o in objects ] , implicit_outputs = f " ver/ { version } /$target.map " , variables = { " version " : version } )
n . build ( f " ver/ { version } /build/$target.z64 " , " rom " , f " ver/ { version } /build/$target.elf " , implicit = " tools/n64crc " )
n . build ( f " ver/ { version } /build/ok " , " checksum " , implicit = f " ver/ { version } /build/$target.z64 " , variables = { " version " : version } )
n . build ( version , " phony " , f " ver/ { version } /build/ok " )
n . build ( f " $target. { version } .z64 " , " phony " , f " ver/ { version } /build/$target.z64 " )
CFG = { }
with open ( f " ver/ { version } /build.cfg " , " r " ) as f :
for line in f . readlines ( ) :
if line . strip ( ) != " " :
key , value = [ part . strip ( ) for part in line . split ( " = " , 1 ) ]
CFG [ key ] = value
ASSET_DIRS = CFG . get ( " asset_dirs " , " assets " ) . split ( " " )
NPC_SPRITES = CFG . get ( " npc_sprites " , " " ) . split ( " " )
MAPS = CFG . get ( " maps " , " " ) . split ( " " )
TEXTURE_ARCHIVES = CFG . get ( " texture_archives " , " " ) . split ( " " )
BACKGROUNDS = CFG . get ( " backgrounds " , " " ) . split ( " " )
PARTY_IMAGES = CFG . get ( " party_images " , " " ) . split ( " " )
ASSETS = sum ( [ [ f " { map_name } _shape " , f " { map_name } _hit " ] for map_name in MAPS ] , [ ] ) + TEXTURE_ARCHIVES + BACKGROUNDS + [ " title_data " ] + PARTY_IMAGES
generated_headers = [ ]
def add_generated_header ( h : str ) :
generated_headers . append ( h )
if not os . path . exists ( h ) :
# mkdir -p
os . makedirs ( os . path . dirname ( h ) , exist_ok = True )
# touch it so cpp doesn't complain if its #included
open ( h , " w " ) . close ( )
# mark it as really old so ninja builds it
os . utime ( h , ( 0 , 0 ) )
return h
n . build ( add_generated_header ( f " ver/ { version } /build/include/ld_addrs.h " ) , " ld_addrs_h " , f " ver/ { version } /build/$target.ld " )
# messages
msg_files = set ( )
for d in ASSET_DIRS :
for f in glob ( d + " /msg/**/*.msg " , recursive = True ) :
msg_files . add ( find_asset ( f [ len ( d ) + 1 : ] ) )
msg_files = list ( msg_files )
for msg_file in msg_files :
n . build (
f " ver/ { version } /build/ { msg_file . split ( ' / ' , 1 ) [ 1 ] } .bin " ,
" msg " ,
msg_file ,
implicit = " tools/msg/parse_compile.py " ,
)
msg_bins = [ f " ver/ { version } /build/ { msg_file . split ( ' / ' , 1 ) [ 1 ] } .bin " for msg_file in msg_files ]
2021-01-14 20:17:14 +01:00
n . build (
2021-02-22 10:21:23 +01:00
[ f " ver/ { version } /build/msg.bin " , add_generated_header ( f " ver/ { version } /build/include/message_ids.h " ) ] ,
" msg_combine " ,
msg_bins ,
implicit = " tools/msg/combine.py " ,
2021-01-14 20:17:14 +01:00
)
2021-02-22 10:21:23 +01:00
n . build ( f " ver/ { version } /build/msg.o " , " bin " , f " ver/ { version } /build/msg.bin " )
# sprites
npc_sprite_yay0s = [ ]
for sprite_id , sprite_name in enumerate ( NPC_SPRITES , 1 ) :
if len ( sprite_name ) == 0 or sprite_name == " _ " :
continue
asset_dir = find_asset_dir ( f " sprite/npc/ { sprite_name } " )
sources = glob ( f " { asset_dir } /sprite/npc/ { sprite_name } /**/*.* " , recursive = True )
variables = {
" sprite_name " : sprite_name ,
" sprite_dir " : f " { asset_dir } /sprite/npc/ { sprite_name } " ,
" sprite_id " : sprite_id ,
}
# generated header
n . build (
add_generated_header ( f " ver/ { version } /build/include/sprite/npc/ { sprite_name } .h " ) ,
" sprite_animations_h " ,
implicit = sources + [ " tools/gen_sprite_animations_h.py " ] ,
variables = variables ,
)
# sprite bin/yay0
n . build (
f " ver/ { version } /build/sprite/npc/ { sprite_name } " ,
" npc_sprite " ,
implicit = sources + [ " tools/compile_npc_sprite.py " ] ,
variables = variables ,
)
yay0 = f " ver/ { version } /build/sprite/npc/ { sprite_name } .Yay0 "
npc_sprite_yay0s . append ( yay0 )
n . build (
yay0 ,
" yay0compress " ,
f " ver/ { version } /build/sprite/npc/ { sprite_name } " ,
implicit = [ " tools/Yay0compress " ] ,
)
n . newline ( )
# fast tasks
for f in objects :
segment = segments [ f ]
if f . endswith ( " .c " ) :
continue # these are handled later
elif f . endswith ( " .Yay0 " ) :
build_yay0_file ( os . path . splitext ( f ) [ 0 ] + " .bin " )
elif f . endswith ( " .bin " ) :
build_bin_object ( find_asset ( f ) )
elif f . endswith ( " .data " ) :
n . build ( obj ( f ) , " as " , f " ver/ { version } /asm/ " + f + " .s " )
elif f . endswith ( " .rodata " ) :
n . build ( obj ( f ) , " as " , f " ver/ { version } /asm/ " + f [ 2 : ] + " .s " )
elif f . endswith ( " .s " ) :
n . build ( obj ( f ) , " as " , f " ver/ { version } / " + f )
elif f . endswith ( " .png " ) :
if isinstance ( segment , Subsegment ) :
# image within a code section
out = f " ver/ { version } /build/ { f } .bin "
infile = find_asset ( re . sub ( r " \ .pal \ .png " , " .png " , f ) )
n . build ( out , " img " , infile , implicit = " tools/img/build.py " , variables = {
" img_type " : segment . type ,
2021-02-06 23:26:35 +01:00
" img_flags " : " " ,
} )
2021-02-22 10:21:23 +01:00
if " .pal.png " not in f :
n . build ( add_generated_header ( f " ver/ { version } /build/include/ " + f + " .h " ) , " img_header " , infile , implicit = " tools/img/header.py " )
2021-02-06 23:26:35 +01:00
2021-02-22 10:21:23 +01:00
n . build ( f " ver/ { version } /build/ { f } .o " , " bin " , out )
2021-02-06 23:26:35 +01:00
else :
2021-02-22 10:21:23 +01:00
build_image ( f , segment )
elif f == " sprite/npc " :
# combine sprites
n . build ( f " ver/ { version } /build/ { f } .bin " , " npc_sprites " , npc_sprite_yay0s , implicit = " tools/compile_npc_sprites.py " )
n . build ( obj ( f ) , " bin " , f " ver/ { version } /build/ { f } .bin " )
elif segment . type == " PaperMarioMessages " :
continue # done already above
elif segment . type == " PaperMarioMapFS " :
asset_files = [ ] # even indexes: uncompressed; odd indexes: compressed
for asset_name in ASSETS :
if asset_name . endswith ( " _tex " ) : # uncompressed
asset_files . append ( find_asset ( f " map/ { asset_name } .bin " ) )
asset_files . append ( find_asset ( f " map/ { asset_name } .bin " ) )
elif asset_name . startswith ( " party_ " ) :
source_file = f " ver/ { version } /build/ { asset_name } .bin "
asset_file = f " ver/ { version } /build/ { asset_name } .Yay0 "
n . build ( source_file , " img " , find_asset ( f " party/ { asset_name } .png " ) , implicit = " tools/img/build.py " , variables = {
" img_type " : " party " ,
" img_flags " : " " ,
} )
asset_files . append ( source_file )
asset_files . append ( asset_file )
n . build ( asset_file , " yay0compress " , source_file , implicit = " tools/Yay0compress " )
elif asset_name . endswith ( " _bg " ) :
source_file = f " ver/ { version } /build/ { asset_name } .bin "
asset_file = f " ver/ { version } /build/ { asset_name } .Yay0 "
n . build ( source_file , " img " , find_asset ( f " map/ { asset_name } .png " ) , implicit = " tools/img/build.py " , variables = {
" img_type " : " bg " ,
" img_flags " : " " ,
} )
asset_files . append ( source_file )
asset_files . append ( asset_file )
n . build ( asset_file , " yay0compress " , source_file , implicit = " tools/Yay0compress " )
elif asset_name . endswith ( " _shape " ) or asset_name . endswith ( " _hit " ) :
source_file = find_asset ( f " map/ { asset_name } .bin " )
asset_file = f " ver/ { version } /build/assets/ { asset_name } .Yay0 "
asset_files . append ( source_file )
asset_files . append ( asset_file )
n . build ( asset_file , " yay0compress " , source_file , implicit = " tools/Yay0compress " )
else :
source_file = find_asset ( f " { asset_name } .bin " )
asset_file = f " ver/ { version } /build/assets/ { asset_name } .Yay0 "
asset_files . append ( source_file )
asset_files . append ( asset_file )
n . build ( asset_file , " yay0compress " , source_file , implicit = " tools/Yay0compress " )
n . build ( f " ver/ { version } /build/assets.bin " , " assets " , asset_files )
n . build ( obj ( f ) , " bin " , f " ver/ { version } /build/assets.bin " )
else :
print ( " warning: dont know what to do with object " + f )
n . newline ( )
2021-01-15 00:55:05 +01:00
2021-02-22 10:21:23 +01:00
n . build ( " generated_headers_ " + version , " phony " , generated_headers )
n . newline ( )
2021-01-15 00:55:05 +01:00
2021-02-22 10:21:23 +01:00
for c_file in glob ( " src/**/*.c " , recursive = True ) :
if c_file . endswith ( " .inc.c " ) :
continue
2021-01-14 11:25:55 +01:00
2021-02-22 10:21:23 +01:00
status = await shell_status ( f " grep -q SCRIPT \ ( { c_file } " )
for version in versions :
s_glob = " ver/ " + version + " / " + re . sub ( " src/ " , " asm/nonmatchings/ " , c_file ) [ : - 2 ] + " /*.s "
2021-03-26 16:01:57 +01:00
rule = " cc_kmc " if c_file . startswith ( " src/ultra/ " ) else ( " cc_dsl " if status == 0 else " cc " )
2021-02-22 10:21:23 +01:00
n . build (
obj ( c_file ) ,
2021-03-26 16:01:57 +01:00
rule ,
2021-02-22 10:21:23 +01:00
c_file ,
2021-03-23 03:29:47 +01:00
implicit = None if not args . depend_on_s else glob ( s_glob ) ,
2021-02-22 10:21:23 +01:00
order_only = " generated_headers_ " + version ,
variables = { " version " : version }
)
2021-01-15 00:55:05 +01:00
2021-01-14 11:25:55 +01:00
print ( " " )
2021-01-14 13:49:13 +01:00
2021-01-15 12:36:53 +01:00
# c tools that need to be compiled
2021-01-14 13:49:13 +01:00
n . build ( " tools/Yay0compress " , " cc_modern_exe " , " tools/Yay0compress.c " )
2021-01-15 12:36:53 +01:00
n . build ( " tools/n64crc " , " cc_modern_exe " , " tools/n64crc.c " )
2021-01-15 00:55:05 +01:00
n . newline ( )
2021-02-22 10:21:23 +01:00
n . build ( " all " , " phony " , versions )
n . default ( " all " )
# update ver/current to versions[0]
try :
os . remove ( " ver/current " )
except Exception :
pass
os . symlink ( versions [ 0 ] , " ver/current " )
n . build ( " ver/current/build/papermario.z64 " , " phony " , " ver/ " + versions [ 0 ] + " /build/papermario.z64 " )
2021-01-15 00:55:05 +01:00
print ( " Build configuration complete! Now run " )
print ( " ninja " )
2021-02-22 10:21:23 +01:00
print ( " to compile " + ' , ' . join ( f ' \' { TARGET } . { version } .z64 \' ' for version in versions ) + " . " )
2021-01-14 04:56:02 +01:00
if __name__ == " __main__ " :
2021-02-01 21:01:34 +01:00
loop = asyncio . get_event_loop ( )
loop . run_until_complete ( main ( ) )