From 61f12d6151fdec7264f115d4cf9664a7a751ffaf Mon Sep 17 00:00:00 2001 From: jeffman Date: Sun, 6 Jan 2019 19:40:02 -0500 Subject: [PATCH] New build script Remove Amalgamator, CompileTool, and old insert.bat. Also fix some path issues with ScriptTool. --- build-tools.bat | 3 +- build.ps1 | 441 ++++++++++++++++++ compiled/Amalgamator/Amalgamator.sln | 22 - .../Amalgamator/Amalgamator.csproj | 66 --- compiled/Amalgamator/Amalgamator/App.config | 6 - compiled/Amalgamator/Amalgamator/Program.cs | 331 ------------- .../Amalgamator/Properties/AssemblyInfo.cs | 36 -- .../Amalgamator/Amalgamator/packages.config | 6 - insert.bat | 27 -- src/m2-hack.asm | 2 +- tools/CompileTool/CompileTool.csproj | 8 - tools/CompileTool/Program.cs | 12 - tools/M12Tools.sln | 10 +- tools/ScriptTool/Asset.cs | 26 ++ tools/ScriptTool/Compiler.cs | 4 +- tools/ScriptTool/EbControlCode.cs | 2 +- tools/ScriptTool/M12ControlCode.cs | 2 +- tools/ScriptTool/Program.cs | 6 +- 18 files changed, 478 insertions(+), 532 deletions(-) create mode 100644 build.ps1 delete mode 100644 compiled/Amalgamator/Amalgamator.sln delete mode 100644 compiled/Amalgamator/Amalgamator/Amalgamator.csproj delete mode 100644 compiled/Amalgamator/Amalgamator/App.config delete mode 100644 compiled/Amalgamator/Amalgamator/Program.cs delete mode 100644 compiled/Amalgamator/Amalgamator/Properties/AssemblyInfo.cs delete mode 100644 compiled/Amalgamator/Amalgamator/packages.config delete mode 100644 insert.bat delete mode 100644 tools/CompileTool/CompileTool.csproj delete mode 100644 tools/CompileTool/Program.cs create mode 100644 tools/ScriptTool/Asset.cs diff --git a/build-tools.bat b/build-tools.bat index a239ed8..7d5804c 100644 --- a/build-tools.bat +++ b/build-tools.bat @@ -1,3 +1,2 @@ dotnet build tools/ScriptTool -o bin/ScriptTool -dotnet build tools/SymbolTableBuilder -o bin/SymbolTableBuilder -dotnet build tools/CompileTool -o bin/CompileTool \ No newline at end of file +dotnet build tools/SymbolTableBuilder -o bin/SymbolTableBuilder \ No newline at end of file diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..b5dcd85 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,441 @@ +#Region Variables +$input_rom_file = "bin/m12fresh.gba" +$output_rom_file = "bin/m12.gba" +$eb_rom_file = "bin/eb.smc" +$working_dir = "working" +$src_dir = "src" + +$input_c_files = + "src/c/ext.c", + "src/c/vwf.c" + +$base_c_address = 0x8100000; + +$output_rom_sym_file = [IO.Path]::ChangeExtension($output_rom_file, "sym") + +$scripttool_cmd = "bin/ScriptTool/ScriptTool.dll" +$scripttool_args = + "-compile", + "-main", + "-misc", + $working_dir, + $eb_rom_file, + $input_rom_file + +If ($IsWindows) { $asm_cmd = "bin/armips.exe" } +ElseIf ($IsLinux) { $asm_cmd = "bin/armips" } +Else +{ + Write-Host "TODO: what's the Mac version of this?" + Exit -1 +} + +$symbuilder_cmd = "bin/SymbolTableBuilder/SymbolTableBuilder.dll" + +# armips will be rooted in working_dir for these, so the includes files have an implicit "working/" in front +$includes_asm_file = "m12-includes.asm" +$includes_sym_file = [IO.Path]::ChangeExtension($includes_asm_file, "sym") + +$gcc_cmd = "arm-none-eabi-gcc" +$gcc_args = + "-c", + "-O3", + "-fno-ipa-cp", + "-fno-inline", + "-march=armv4t", + "-mtune=arm7tdmi", + "-mthumb", + "-ffixed-r12", + "-mno-long-calls" + +$ld_cmd = "arm-none-eabi-ld" +$combined_obj_file = "src/c/combined.o" +$linked_obj_file = "src/c/linked.o" +$combine_script = "src/c/combine.ld" +$link_script = "src/c/link.ld" +$undefine_obj_file = "src/c/ext.o" + +$combine_script_contents = +"SECTIONS { .text 0x$($base_c_address.ToString('X')) : { *(.text .rodata) } }" + +$link_script_contents = +"SECTIONS { .text 0x$($base_c_address.ToString('X')) : { *(.text .data .rodata) } }" + +$objdump_cmd = "arm-none-eabi-objdump" +$compiled_asm_file = "src/m2-compiled.asm" + +# armips will be rooted in src_dir for these, so the includes files have an implicit "working/" in front +$hack_asm_file = "m2-hack.asm" +$hack_sym_file = [IO.Path]::ChangeExtension($hack_asm_file, "sym") + +$readelf_cmd = "arm-none-eabi-readelf" + +#EndRegion Variables + +#Region Functions +class Symbol +{ + [string]$Name + [int]$Value + [int]$Size + [bool]$IsLocal + [bool]$IsGlobal + [bool]$IsWeak + [bool]$IsConstructor + [bool]$IsWarning + [bool]$IsIndirect + [bool]$IsDebugging + [bool]$IsDynamic + [bool]$IsFunction + [bool]$IsFile + [bool]$IsObject + [string]$Section + [bool]$IsAbsolute + [bool]$IsUndefined +} + +class SectionInfo +{ + [string]$Name + [int]$Address + [int]$Offset + [int]$Size +} + +Function Get-Symbols ([string]$obj_file) +{ + return & $objdump_cmd -t $obj_file | ForEach-Object { New-Symbol $_ } | Where-Object { $_ -ne $null } +} + +# Converts a symbol from objdump's string representation to a rich object representation +$symbol_regex = "(?'value'[0-9a-fA-F]{8})\s(?'flags'.{7})\s(?'section'\S+)\s+(?'size'[0-9a-fA-F]{8})\s(?'name'\S+)" +Function New-Symbol([string]$symbol_string) +{ + if ($symbol_string -match $symbol_regex) + { + $symbol = [Symbol]::new() + $symbol.Name = $Matches.name + $symbol.Value = [int]::Parse($Matches.value, [System.Globalization.NumberStyles]::HexNumber) + $symbol.Size = [int]::Parse($Matches.size, [System.Globalization.NumberStyles]::HexNumber) + $symbol.Section = $Matches.section + $symbol.IsAbsolute = $symbol.Section -eq "*ABS*" + $symbol.IsUndefined = $symbol.Section -eq "*UND*" + + $flags = $Matches.flags + $symbol.IsLocal = $flags.Contains("l") -or $flags.Contains("!") + $symbol.IsGlobal = $flags.Contains("g") -or $flags.Contains("!") + $symbol.IsWeak = $flags.Contains("w") + $symbol.IsConstructor = $flags.Contains("C") + $symbol.IsWarning = $flags.Contains("W") + $symbol.IsIndirect = $flags.Contains("I") + $symbol.IsDebugging = $flags.Contains("d") + $symbol.IsDynamic = $flags.Contains("D") + $symbol.IsFunction = $flags.Contains("F") + $symbol.IsFile = $flags.Contains("f") + $symbol.IsObject = $flags.Contains("O") + + return $symbol + } + else + { + return $null + } +} + +Function Get-SymfileSymbols([string]$symbol_file) +{ + return Get-Content $symbol_file | ForEach-Object { New-SymfileSymbol $_ } | Where-Object { $_ -ne $null } +} + +$symfile_symbol_regex = "(?'value'[0-9a-fA-F]{8})\s+(?'name'(?>\.|@@|[a-zA-Z0-9_])[a-zA-Z0-9_]+):{0,1}(?'size'[0-9a-fA-F]+){0,1}" +Function New-SymfileSymbol([string]$symbol_string) +{ + if ($symbol_string -match $symfile_symbol_regex) + { + $symbol = [Symbol]::new() + $symbol.Name = $Matches.name + $symbol.Value = [int]::Parse($Matches.value, [System.Globalization.NumberStyles]::HexNumber) + + if ($null -ne $Matches.size) + { + $symbol.Size = [int]::Parse($Matches.size, [System.Globalization.NumberStyles]::HexNumber) + } + else + { + $symbol.Size = 0 + } + + $symbol.IsLocal = $symbol.Name.StartsWith("@@") + $symbol.IsGlobal = -not $symbol.Name.StartsWith(".") -and -not $symbol.IsLocal + + return $symbol + } + else + { + return $null + } +} + +function Get-SectionInfo([string]$object_file) +{ + $hash = @{} + & $readelf_cmd -S $object_file | ForEach-Object { + $section = New-Section $_ + if ($null -ne $section) + { + $hash[$section.Name] = $section + } + } + return $hash +} + +$section_regex = "\s?\[\s?\d+]\s(?'name'\S+)\s+\S+\s+(?'address'[0-9a-fA-F]+)\s(?'offset'[0-9a-fA-F]+)\s(?'size'[0-9a-fA-F]+)" +function New-Section([string]$section_string) +{ + if ($section_string -match $section_regex) + { + $section = [SectionInfo]::new() + $section.Name = $Matches.name + $section.Address = [int]::Parse($Matches.address, [System.Globalization.NumberStyles]::HexNumber) + $section.Offset = [int]::Parse($Matches.offset, [System.Globalization.NumberStyles]::HexNumber) + $section.Size = [int]::Parse($Matches.size, [System.Globalization.NumberStyles]::HexNumber) + + return $section + } + else + { + return $null + } +} +#EndRegion Functions + +<# +This is a complicated build script that does complicated things, but it's +that way for a reason. + +- We want to use ASM and C code files simultaneously +- The ASM code defines symbols that we want to reference from C +- The C code defines symbols that we want to reference from ASM +- The game text defines symbols that we want to reference from C + +The ASM and C code therefore depend on each other. The way around this catch-22 +is to separate the compiling and linking stages of the C code. + +1) Compile the game text + + Inputs: + - Text/script files + + Outputs: + - BIN files containing compiled text data + - ASM files that relocate text pointers (m12-includes.asm) + +2) Assemble output from step 1 + + Inputs: + - Output from step 1 + - Fresh M12 ROM file + + Outputs: + - ROM file with text inserted and repointed + - m12-includes.sym file containing generated symbols (e.g. individual strings from m12-other.json) + +3) Compile C code + + Inputs: + - C files + + Outputs: + - O files (one for each C file) + + Remarks: + - All symbols not defined in the C code itself must be marked extern. We will link it + in a later step. This includes the string symbols from step 2. + - Due to an assembler limitation, extern symbols cannot contain capital letters. + - There's a weird quirk with the linker that requires extra care when using external + functions. You need to declare them as extern AND implement them using the ((naked)) + attribute, e.g. + + (in a header file) extern int m2_drawwindow(WINDOW* window); + (in a code file) int __attribute__((naked)) m2_drawwindow(WINDOW* window) {} + + See: http://stackoverflow.com/a/43283331/1188632 + + This will cause a duplicate definition of the function symbol, since it's *really* + defined in the ASM files somewhere, but we're redefining it again in C. So we also need + to "undefine" these symbols later on when linking. + + To make it a bit easier to do all that, place all such implementations in ext.c. + +4) First link stage + + Inputs: + - O files from step 3 + - Base address + + Outputs: + - Single O file positioned to the base address + - m2-compiled.asm, containing C symbol definitions + + Remarks: + - This is an incremental link; there will still be undefined symbols. + - However, with this combined O file, the code layout will not change and we can now + define symbols from the C code to be used in the ASM code. + - The symbols will be passed to the assembler next, so export them as an ASM file + with one ".definelabel" entry for each defined symbol. They need to be halfword-aligned. + This file is called "m2-compiled.asm" by default and is referenced by m2-hack.asm. + - Exclude the symbols defined in ext.c from m2-compiled.asm. + +5) Assemble ASM code + + Inputs: + - m2-hack.asm + - m2-compiled.asm + - All other ASM and data files from src/ (but not the generated ones from working/) + - M12 ROM file from step 2 + + Outputs: + - M12 ROM file with all ASM code and data included + - m2-hack.sym with all symbols defined thusfar + +6) Generate final linker script + + Inputs: + - Base address (same as step 4) + - m2-hack.sym (from step 5) + - m12-includes.sym (from step 2) + + Outputs: + - Linker script file + + Remarks: + - The linker script must define each symbol that's still undefined; if everything is + happy at this point, then they should all be contained within the two input SYM files. + +7) Final link stage + + Inputs: + - O file from step 4 + - Linker script from step 6 + + Outputs: + - Single O file with all symbols defined + +8) Copy code to ROM + + Inputs: + - O file from step 7 + - M12 ROM file from step 5 + + Outputs: + - M12 ROM file with all code and data included + +9) Build final symbol file + + Inputs: + - m2-hack.sym + - m12-includes.sym + + Outputs: + - m12.sym + + Remarks: + - This is a merged symbol file for debugging convenience. + - The assembler generates a dummy .byt symbol at the base address; remove it. + - Use SymbolTableBuilder to do the merge, plus remove the undesirable namespaces that + the assembler inserts. +#> + +# ------------------------- COMPILE GAME TEXT ----------------------- + +"Copying $input_rom_file to $output_rom_file..." +Copy-Item -Path $input_rom_file -Destination $output_rom_file + +"Compiling game text..." +& dotnet $scripttool_cmd $scripttool_args +if ($LASTEXITCODE -ne 0) { exit -1 } + +# ------------------------ ASSEMBLE GAME TEXT ----------------------- + +"Assembling game text..." +& $asm_cmd -root $working_dir -sym $includes_sym_file $includes_asm_file +if ($LASTEXITCODE -ne 0) { exit -1 } + +# ----------------------------- COMPILE C --------------------------- + +$obj_files = @() + +# Invoke gcc on each file individually so that we can specify the output file +foreach ($input_c_file in $input_c_files) +{ + $obj_file = [IO.Path]::ChangeExtension($input_c_file, "o") + $obj_files += $obj_file + + "Compiling $input_c_file..." + & $gcc_cmd $gcc_args -o $obj_file $input_c_file + if ($LASTEXITCODE -ne 0) { exit -1 } +} + +# ----------------------------- 1ST LINK ---------------------------- + +"Writing $combine_script..." +$combine_script_contents | Out-File -FilePath $combine_script + +"Linking $obj_files..." +& $ld_cmd -i -T $combine_script -o $combined_obj_file $obj_files +if ($LASTEXITCODE -ne 0) { exit -1 } + +# Export all C symbols to m2-compiled.asm, except for those in ext.c +"Reading symbols from $combined_obj_file..." +$combined_symbols = Get-Symbols $combined_obj_file +if ($LASTEXITCODE -ne 0) { exit -1 } + +"Reading symbols from $undefine_obj_file..." +$ext_symbols = Get-Symbols $undefine_obj_file +if ($LASTEXITCODE -ne 0) { exit -1 } + +"Exporting C symbols to $compiled_asm_file..." +$ext_symbols_names = $ext_symbols | Where-Object { $_.IsFunction -and $_.IsGlobal -and (-not $_.IsUndefined) } | ForEach-Object { $_.Name } +$exported_symbols = $combined_symbols | Where-Object { $_.IsFunction -and $_.IsGlobal -and (-not $_.IsUndefined) -and ($ext_symbols_names -notcontains $_.Name) } +$exported_symbols | Sort-Object -Property Name | ForEach-Object { ".definelabel $($_.Name),0x$($_.Value.ToString("X"))" } | Set-Content -Path $compiled_asm_file + +# ------------------------ ASSEMBLE HACK CODE ----------------------- + +"Assembling $hack_asm_file..." +& $asm_cmd -root $src_dir -sym $hack_sym_file $hack_asm_file +if ($LASTEXITCODE -ne 0) { exit -1 } + +# ------------------- GENERATE FINAL LINKER SCRIPT ------------------ + +"Writing $link_script..." +$hack_symbols = Get-SymfileSymbols "$([IO.Path]::Combine($src_dir, $hack_sym_file))" +$includes_symbols = Get-SymfileSymbols "$([IO.Path]::Combine($working_dir, $includes_sym_file))" +$asm_symbols = ($hack_symbols + $includes_symbols) | Where-Object { $_.IsGlobal } +Set-Content -Path $link_script -Value $link_script_contents +$asm_symbols | ForEach-Object { Add-Content -Path $link_script -Value "$($_.Name) = 0x$($_.Value.ToString("X"));" } + +# ---------------------------- FINAL LINK --------------------------- + +"Linking to $linked_obj_file..." +& $ld_cmd -T $link_script -o $linked_obj_file $combined_obj_file +if ($LASTEXITCODE -ne 0) { exit -1 } + +# -------------------- COPY COMPILED C CODE TO ROM ------------------ + +"Copying compiled code to $output_rom_file..." +$sections = Get-SectionInfo $linked_obj_file +if ($LASTEXITCODE -ne 0) { exit -1 } +$text_section = $sections[".text"] +$linked_bytes = [IO.File]::ReadAllBytes($linked_obj_file) +$rom_bytes = [IO.File]::ReadAllBytes($output_rom_file) +[System.Array]::Copy($linked_bytes, $text_section.Offset, $rom_bytes, $text_section.Address - 0x8000000, $text_section.Size) +[IO.File]::WriteAllBytes($output_rom_file, $rom_bytes) + +# -------------------------- GENERATE SYMBOLS ----------------------- + +"Generating $output_rom_sym_file..." +& dotnet $symbuilder_cmd $output_rom_sym_file "$([IO.Path]::Combine($src_dir, $hack_sym_file))" "$([IO.Path]::Combine($working_dir, $includes_sym_file))" +if ($LASTEXITCODE -ne 0) { exit -1 } + +"Finished" +exit 0 diff --git a/compiled/Amalgamator/Amalgamator.sln b/compiled/Amalgamator/Amalgamator.sln deleted file mode 100644 index f3ed1d4..0000000 --- a/compiled/Amalgamator/Amalgamator.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26403.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amalgamator", "Amalgamator\Amalgamator.csproj", "{24518872-E9D3-4C09-BE31-A2B52A6634F2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {24518872-E9D3-4C09-BE31-A2B52A6634F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {24518872-E9D3-4C09-BE31-A2B52A6634F2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {24518872-E9D3-4C09-BE31-A2B52A6634F2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {24518872-E9D3-4C09-BE31-A2B52A6634F2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/compiled/Amalgamator/Amalgamator/Amalgamator.csproj b/compiled/Amalgamator/Amalgamator/Amalgamator.csproj deleted file mode 100644 index 101a083..0000000 --- a/compiled/Amalgamator/Amalgamator/Amalgamator.csproj +++ /dev/null @@ -1,66 +0,0 @@ - - - - - Debug - AnyCPU - {24518872-E9D3-4C09-BE31-A2B52A6634F2} - Exe - Amalgamator - Amalgamator - v4.6.1 - 512 - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\ELFSharp.1.0.4\lib\net40\ELFSharp.dll - - - ..\packages\FluentCommandLineParser.1.4.3\lib\net35\FluentCommandLineParser.dll - - - ..\packages\JonSkeet.MiscUtil.0.1\lib\net35-Client\MiscUtil.dll - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/compiled/Amalgamator/Amalgamator/App.config b/compiled/Amalgamator/Amalgamator/App.config deleted file mode 100644 index 731f6de..0000000 --- a/compiled/Amalgamator/Amalgamator/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/compiled/Amalgamator/Amalgamator/Program.cs b/compiled/Amalgamator/Amalgamator/Program.cs deleted file mode 100644 index ec4c371..0000000 --- a/compiled/Amalgamator/Amalgamator/Program.cs +++ /dev/null @@ -1,331 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.IO; -using System.Diagnostics; -using Fclp; -using ELFSharp.ELF; -using ELFSharp.ELF.Sections; - -namespace Amalgamator -{ - class Program - { - const string GccExec = "arm-none-eabi-gcc"; - const string LdExec = "arm-none-eabi-ld"; - const string CompiledAsmFile = "m2-compiled.asm"; - const string ArmipsExec = "armips.exe"; - const string HackFile = "m2-hack.asm"; - const string ArmipsSymFile = "armips-symbols.sym"; - const string IncludesSymFile = "working\\includes-symbols.sym"; - const string LinkerScript = "linker.ld"; - const string LinkedObjectFile = "linked.o"; - - const string ArmipsSymbolRegex = @"([0-9a-fA-F]{8}) ([^\.@]\S+)"; - - static int Main(string[] args) - { - int exitCode = MainInternal(args); - return exitCode; - } - - // I'm having Main wrap everything so that it's easier to break on error before the program returns - static int MainInternal(string[] args) - { - var options = GetOptions(args); - if (options == null) - return 1; - - var functionSymbols = new Dictionary(); - var undefinedSymbols = new HashSet(); - var linkerScript = new StringBuilder(); - - foreach (string codeFile in options.CodeFiles) - { - bool success = CompileCodeFile(codeFile); - if (!success) - { - Console.Error.WriteLine($"Error compiling {codeFile}"); - return 2; - } - - // Skip the dummy function stubs from ext.o - if (codeFile == "ext.c") - continue; - - var elf = ELFReader.Load(GetObjectFileName(codeFile)); - var symbols = elf.GetSection(".symtab") as SymbolTable; - - foreach (var symbol in symbols.Entries.Where( - s => s.Type == SymbolType.Function && s.Binding.HasFlag(SymbolBinding.Global))) - { - functionSymbols.Add(symbol.Name, symbol.Value); - } - - foreach (var symbol in symbols.Entries.Where( - s => s.Type == SymbolType.NotSpecified && s.Binding.HasFlag(SymbolBinding.Global) && s.PointedSectionIndex == 0)) - { - undefinedSymbols.Add(symbol.Name); - } - } - - GenerateCompiledLabelsFile(options.GetRootFile(CompiledAsmFile), - functionSymbols, options.CompiledAddress); - - if (!RunAssembler(options.RootDirectory)) - return 3; - - linkerScript.AppendLine($"SECTIONS {{ .text 0x{options.CompiledAddress:X} : {{ *(.text .data .rodata) }} }}"); - - foreach (var sym in EnumerateArmipsSymbols(options.GetRootFile(ArmipsSymFile)) - .Concat(EnumerateArmipsSymbols(options.GetRootFile(IncludesSymFile))) - .Where(s => undefinedSymbols.Contains(s.Key))) - { - linkerScript.AppendLine($"{sym.Key} = 0x{sym.Value:X};"); - } - - File.WriteAllText(LinkerScript, linkerScript.ToString()); - - if (!RunLinker(options.RootDirectory, options.CodeFiles.Select(c => GetObjectFileName(c)))) - return 4; - - byte[] code = GenerateCompiledBinfile(); - if (code == null) - return 5; - - RemoveUnwantedCodeDataSymbol(options.GetRootFile(ArmipsSymFile), options.CompiledAddress); - IncludeBinfile(options.GetRootFile(options.RomName), code, options.CompiledAddress); - - return 0; - } - - static Options GetOptions(string[] args) - { - var options = new Options(); - var parser = new FluentCommandLineParser(); - - parser.Setup('c', "compiled-address") - .Callback(i => options.CompiledAddress = (int)new System.ComponentModel.Int32Converter().ConvertFromString(i)) - .Required(); - - parser.Setup('d', "root-directory") - .Callback(s => options.RootDirectory = s); - - parser.Setup('r', "rom-file") - .Callback(s => options.RomName = s) - .Required(); - - parser.Setup>('i', "input-files") - .Callback(s => options.CodeFiles = s) - .Required(); - - var result = parser.Parse(args); - - if (result.HasErrors) - Console.WriteLine(result.ErrorText); - - return result.HasErrors ? null : options; - } - - static int ParsePossiblyHexNumber(string str) - { - return (int)new System.ComponentModel.Int32Converter().ConvertFromString(str); - } - - static int RunLocalProcess(string fileName, params string[] args) - => RunProcess(fileName, null, args); - - static int RunProcess(string fileName, string workingDirectory, params string[] args) - => RunProcess(fileName, Process_OutputDataReceived, Process_ErrorDataReceived, workingDirectory, args); - - static int RunProcess(string fileName, - DataReceivedEventHandler outputCallback, - DataReceivedEventHandler errorCallback, - string workingDirectory, - params string[] args) - { - var process = new Process(); - process.StartInfo.FileName = Path.Combine((workingDirectory ?? ""), fileName); - process.StartInfo.Arguments = String.Join(" ", args); - if (workingDirectory != null) - { - process.StartInfo.WorkingDirectory = Path.GetFullPath(workingDirectory); - } - - Console.WriteLine($"Executing: {process.StartInfo.FileName} {process.StartInfo.Arguments}"); - - process.StartInfo.CreateNoWindow = true; - process.StartInfo.UseShellExecute = false; - - process.StartInfo.RedirectStandardError = true; - process.StartInfo.RedirectStandardOutput = true; - - if (errorCallback != null) - process.ErrorDataReceived += errorCallback; - - if (outputCallback != null) - process.OutputDataReceived += outputCallback; - - process.Start(); - process.BeginErrorReadLine(); - process.BeginOutputReadLine(); - process.WaitForExit(); - - return process.ExitCode; - } - - static int RunProcess(string fileName, out string standardOutput, params string[] args) - { - var outputBuilder = new StringBuilder(); - DataReceivedEventHandler outputCallback = (o, e) => outputBuilder.AppendLine(e.Data); - - int exitCode = RunProcess(fileName, outputCallback, null, null, args); - standardOutput = outputBuilder.ToString(); - - return exitCode; - } - - static string GetObjectFileName(string codeFileName) - { - return Path.GetFileNameWithoutExtension(codeFileName) + ".o"; - } - - static void Process_OutputDataReceived(object sender, DataReceivedEventArgs e) - { - if (!string.IsNullOrEmpty(e.Data)) - Console.Out.WriteLine(e.Data); - } - - static void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e) - { - if (!string.IsNullOrEmpty(e.Data)) - Console.Error.WriteLine(e.Data); - } - - static bool CompileCodeFile(string fileName) - { - string objectName = GetObjectFileName(fileName); - - return RunLocalProcess(GccExec, - "-o", objectName, - "-c", - "-O3", - "-fno-ipa-cp", - "-fno-inline", - fileName, - "-march=armv4t", - "-mtune=arm7tdmi", - "-mthumb", - "-ffixed-r12", - "-mno-long-calls") == 0; - } - - static IEnumerable> ParseAddressSymbolMatches(IEnumerable matches) - { - var symbols = new Dictionary(); - - foreach (var match in matches) - { - // There should be exactly three groups (one for the full match + 2 captured groups) - if (match.Groups.Count != 3) - continue; - - string addressString = match.Groups[1].Value; - string symbolName = match.Groups[2].Value; - int address = int.Parse(addressString, System.Globalization.NumberStyles.HexNumber); - - symbols.Add(symbolName, address); - } - - return symbols; - } - - static IEnumerable> EnumerateArmipsSymbols(string armipsSymbolsFile) - { - string armipsSymbols = File.ReadAllText(armipsSymbolsFile); - - var regex = new Regex(ArmipsSymbolRegex); - var matches = regex.Matches(armipsSymbols).Cast(); - return ParseAddressSymbolMatches(matches); - } - - static void GenerateCompiledLabelsFile(string fileName, - IEnumerable> functionSymbols, int compiledAddress) - { - using (var writer = File.CreateText(fileName)) - { - // Need to clear the 1 bit because armips requires aligned label addresses - foreach (var kv in functionSymbols) - writer.WriteLine($".definelabel {kv.Key},0x{(kv.Value & ~1) + compiledAddress:X}"); - } - } - - static bool RunAssembler(string rootDirectory) - { - return RunProcess(ArmipsExec, rootDirectory, HackFile, "-sym", ArmipsSymFile) == 0; - } - - static bool RunLinker(string rootDirectory, IEnumerable objectFiles) - { - return RunLocalProcess(LdExec, - "-o", LinkedObjectFile, - String.Join(" ", objectFiles), - "-T", LinkerScript) == 0; - } - - static byte[] GenerateCompiledBinfile() - { - var code = ExtractObjectSection(LinkedObjectFile, ".text"); - return code; - } - - static byte[] ExtractObjectSection(string objectFile, string sectionName) - { - var elf = ELFReader.Load(objectFile); - var section = elf.GetSection(sectionName); - return section.GetContents(); - } - - static void RemoveUnwantedCodeDataSymbol(string symbolFile, int compiledAddress) - { - var symbols = File.ReadAllLines(symbolFile).ToList(); - - int unwantedIndex = symbols.FindIndex(l => - l.StartsWith(compiledAddress.ToString("X8")) && - l.Substring(9, 5) == ".byt:"); - - if (unwantedIndex >= 0) - symbols.RemoveAt(unwantedIndex); - - File.WriteAllLines(symbolFile, symbols.ToArray()); - } - - static void IncludeBinfile(string outputFile, byte[] data, int compiledAddress) - { - byte[] output = File.ReadAllBytes(outputFile); - Array.Copy(data, 0, output, compiledAddress & 0x1FFFFFF, data.Length); - File.WriteAllBytes(outputFile, output); - } - } - - class Options - { - public int CompiledAddress { get; set; } - public string RootDirectory { get; set; } - public string RomName { get; set; } - public List CodeFiles { get; set; } - - public string GetRootFile(string fileName) - { - return Path.Combine(Path.GetFullPath(RootDirectory), fileName); - } - - public string GetQuotedRootFile(string fileName) - { - return "\"" + GetRootFile(fileName) + "\""; - } - } -} diff --git a/compiled/Amalgamator/Amalgamator/Properties/AssemblyInfo.cs b/compiled/Amalgamator/Amalgamator/Properties/AssemblyInfo.cs deleted file mode 100644 index 2cbc683..0000000 --- a/compiled/Amalgamator/Amalgamator/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Amalgamator")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Amalgamator")] -[assembly: AssemblyCopyright("Copyright © 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("24518872-e9d3-4c09-be31-a2b52a6634f2")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/compiled/Amalgamator/Amalgamator/packages.config b/compiled/Amalgamator/Amalgamator/packages.config deleted file mode 100644 index fb74b83..0000000 --- a/compiled/Amalgamator/Amalgamator/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/insert.bat b/insert.bat deleted file mode 100644 index 71c8ce5..0000000 --- a/insert.bat +++ /dev/null @@ -1,27 +0,0 @@ -@echo off - -echo Copying fresh ROM... -copy /Y m12fresh.gba m12.gba - -:: Compile text files -echo Compiling text files... -pushd ScriptTool\ScriptTool\bin\Debug -ScriptTool.exe -compile -main -misc "..\..\..\..\working" "..\..\..\..\eb.smc" "..\..\..\..\m12fresh.gba" -popd - -:: Assemble includes -echo Assembling includes... -pushd working -..\armips.exe m12-includes.asm -sym includes-symbols.sym -popd - -:: Compile all C and ASM code -echo Compiling and assembling... -pushd compiled -Amalgamator\Amalgamator\bin\Debug\Amalgamator.exe -r m12.gba -c 0x8100000 -d "../" -i vwf.c ext.c -popd -if errorlevel 1 (pause && goto :eof) - -SymbolTableBuilder\SymbolTableBuilder\bin\Debug\symbols.exe m12.sym armips-symbols.sym working/includes-symbols.sym - -echo Success! \ No newline at end of file diff --git a/src/m2-hack.asm b/src/m2-hack.asm index 106e79d..2b221ab 100644 --- a/src/m2-hack.asm +++ b/src/m2-hack.asm @@ -1,5 +1,5 @@ .gba -.open "m12.gba",0x8000000 +.open "../bin/m12.gba",0x8000000 //============================================================================== // Relocation hacks diff --git a/tools/CompileTool/CompileTool.csproj b/tools/CompileTool/CompileTool.csproj deleted file mode 100644 index 958d2f1..0000000 --- a/tools/CompileTool/CompileTool.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - Exe - netcoreapp3.0 - - - diff --git a/tools/CompileTool/Program.cs b/tools/CompileTool/Program.cs deleted file mode 100644 index 4784f90..0000000 --- a/tools/CompileTool/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace CompileTool -{ - class Program - { - static void Main(string[] args) - { - Console.WriteLine("Hello World!"); - } - } -} diff --git a/tools/M12Tools.sln b/tools/M12Tools.sln index 9d69b24..9808e19 100644 --- a/tools/M12Tools.sln +++ b/tools/M12Tools.sln @@ -1,10 +1,8 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.168 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28407.52 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CompileTool", "CompileTool\CompileTool.csproj", "{44399E43-DE61-41ED-85AE-5F3A9DA15970}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptTool", "ScriptTool\ScriptTool.csproj", "{29AB41A8-6405-4C4B-A374-BF1BD7AF1A27}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SymbolTableBuilder", "SymbolTableBuilder\SymbolTableBuilder.csproj", "{2E0E2F98-4EB7-48C7-968D-1BEDCADA37F2}" @@ -17,10 +15,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {44399E43-DE61-41ED-85AE-5F3A9DA15970}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {44399E43-DE61-41ED-85AE-5F3A9DA15970}.Debug|Any CPU.Build.0 = Debug|Any CPU - {44399E43-DE61-41ED-85AE-5F3A9DA15970}.Release|Any CPU.ActiveCfg = Release|Any CPU - {44399E43-DE61-41ED-85AE-5F3A9DA15970}.Release|Any CPU.Build.0 = Release|Any CPU {29AB41A8-6405-4C4B-A374-BF1BD7AF1A27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {29AB41A8-6405-4C4B-A374-BF1BD7AF1A27}.Debug|Any CPU.Build.0 = Debug|Any CPU {29AB41A8-6405-4C4B-A374-BF1BD7AF1A27}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/tools/ScriptTool/Asset.cs b/tools/ScriptTool/Asset.cs new file mode 100644 index 0000000..630aaba --- /dev/null +++ b/tools/ScriptTool/Asset.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace ScriptTool +{ + internal static class Asset + { + public static readonly string AssetPath; + + static Asset() + { + AssetPath = AppDomain.CurrentDomain.BaseDirectory; + } + + private static string GetFullPath(string path) + => Path.Combine(AssetPath, path); + + public static string ReadAllText(string path) + => File.ReadAllText(GetFullPath(path)); + + public static byte[] ReadAllBytes(string path) + => File.ReadAllBytes(GetFullPath(path)); + } +} diff --git a/tools/ScriptTool/Compiler.cs b/tools/ScriptTool/Compiler.cs index 14cc425..6094dd4 100644 --- a/tools/ScriptTool/Compiler.cs +++ b/tools/ScriptTool/Compiler.cs @@ -20,8 +20,8 @@ namespace ScriptTool static Compiler() { - byte[] widths = File.ReadAllBytes("m2-widths-main.bin"); - byte[] saturnWidths = File.ReadAllBytes("m2-widths-saturn.bin"); + byte[] widths = Asset.ReadAllBytes("m2-widths-main.bin"); + byte[] saturnWidths = Asset.ReadAllBytes("m2-widths-saturn.bin"); virtualWidths = new int[widths.Length / 2]; renderWidths = new int[widths.Length / 2]; virtualWidthsSaturn = new int[saturnWidths.Length / 2]; diff --git a/tools/ScriptTool/EbControlCode.cs b/tools/ScriptTool/EbControlCode.cs index 8afa90c..6a34bd8 100644 --- a/tools/ScriptTool/EbControlCode.cs +++ b/tools/ScriptTool/EbControlCode.cs @@ -36,7 +36,7 @@ namespace ScriptTool // Load codes Codes = JsonConvert.DeserializeObject>( - File.ReadAllText("eb-codelist.json")); + Asset.ReadAllText("eb-codelist.json")); } public bool IsMatch(byte[] rom, int address) diff --git a/tools/ScriptTool/M12ControlCode.cs b/tools/ScriptTool/M12ControlCode.cs index c423b7c..6a3487f 100644 --- a/tools/ScriptTool/M12ControlCode.cs +++ b/tools/ScriptTool/M12ControlCode.cs @@ -26,7 +26,7 @@ namespace ScriptTool static M12ControlCode() { Codes = JsonConvert.DeserializeObject>( - File.ReadAllText("m12-codelist.json")); + Asset.ReadAllText("m12-codelist.json")); } public bool IsMatch(byte[] rom, int address) diff --git a/tools/ScriptTool/Program.cs b/tools/ScriptTool/Program.cs index 47cb563..8587239 100644 --- a/tools/ScriptTool/Program.cs +++ b/tools/ScriptTool/Program.cs @@ -39,8 +39,8 @@ namespace ScriptTool return -1; } - m12CharLookup = JsonConvert.DeserializeObject>(File.ReadAllText("m12-char-lookup.json")); - ebCharLookup = JsonConvert.DeserializeObject>(File.ReadAllText("eb-char-lookup.json")); + m12CharLookup = JsonConvert.DeserializeObject>(Asset.ReadAllText("m12-char-lookup.json")); + ebCharLookup = JsonConvert.DeserializeObject>(Asset.ReadAllText("eb-char-lookup.json")); if (options.Command == CommandType.Decompile) { @@ -74,7 +74,7 @@ namespace ScriptTool using (IncludeFile = File.CreateText(Path.Combine(options.WorkingDirectory, "m12-includes.asm"))) { IncludeFile.WriteLine(".gba"); - IncludeFile.WriteLine(".open \"../m12.gba\",0x8000000"); + IncludeFile.WriteLine(".open \"../bin/m12.gba\",0x8000000"); // Compile main string tables if (options.DoMainText)