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)