New build script
Remove Amalgamator, CompileTool, and old insert.bat. Also fix some path issues with ScriptTool.
This commit is contained in:
parent
fec378462d
commit
61f12d6151
|
@ -1,3 +1,2 @@
|
||||||
dotnet build tools/ScriptTool -o bin/ScriptTool
|
dotnet build tools/ScriptTool -o bin/ScriptTool
|
||||||
dotnet build tools/SymbolTableBuilder -o bin/SymbolTableBuilder
|
dotnet build tools/SymbolTableBuilder -o bin/SymbolTableBuilder
|
||||||
dotnet build tools/CompileTool -o bin/CompileTool
|
|
|
@ -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
|
|
@ -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
|
|
|
@ -1,66 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
|
||||||
<PropertyGroup>
|
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProjectGuid>{24518872-E9D3-4C09-BE31-A2B52A6634F2}</ProjectGuid>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<RootNamespace>Amalgamator</RootNamespace>
|
|
||||||
<AssemblyName>Amalgamator</AssemblyName>
|
|
||||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
|
||||||
<FileAlignment>512</FileAlignment>
|
|
||||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Debug\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="ELFSharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=22f480d29a3ced83, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\ELFSharp.1.0.4\lib\net40\ELFSharp.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="FluentCommandLineParser, Version=1.4.3.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\FluentCommandLineParser.1.4.3\lib\net35\FluentCommandLineParser.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="MiscUtil, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d3c42c4bfacf7596, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\JonSkeet.MiscUtil.0.1\lib\net35-Client\MiscUtil.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System" />
|
|
||||||
<Reference Include="System.Core" />
|
|
||||||
<Reference Include="System.Xml.Linq" />
|
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
|
||||||
<Reference Include="Microsoft.CSharp" />
|
|
||||||
<Reference Include="System.Data" />
|
|
||||||
<Reference Include="System.Net.Http" />
|
|
||||||
<Reference Include="System.Xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="Program.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="App.config" />
|
|
||||||
<None Include="packages.config" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
|
||||||
<PropertyGroup>
|
|
||||||
<PostBuildEvent>
|
|
||||||
</PostBuildEvent>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<configuration>
|
|
||||||
<startup>
|
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
|
|
||||||
</startup>
|
|
||||||
</configuration>
|
|
|
@ -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<string, uint>();
|
|
||||||
var undefinedSymbols = new HashSet<string>();
|
|
||||||
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<uint>;
|
|
||||||
|
|
||||||
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<string>('c', "compiled-address")
|
|
||||||
.Callback(i => options.CompiledAddress = (int)new System.ComponentModel.Int32Converter().ConvertFromString(i))
|
|
||||||
.Required();
|
|
||||||
|
|
||||||
parser.Setup<string>('d', "root-directory")
|
|
||||||
.Callback(s => options.RootDirectory = s);
|
|
||||||
|
|
||||||
parser.Setup<string>('r', "rom-file")
|
|
||||||
.Callback(s => options.RomName = s)
|
|
||||||
.Required();
|
|
||||||
|
|
||||||
parser.Setup<List<string>>('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<KeyValuePair<string, int>> ParseAddressSymbolMatches(IEnumerable<Match> matches)
|
|
||||||
{
|
|
||||||
var symbols = new Dictionary<string, int>();
|
|
||||||
|
|
||||||
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<KeyValuePair<string, int>> EnumerateArmipsSymbols(string armipsSymbolsFile)
|
|
||||||
{
|
|
||||||
string armipsSymbols = File.ReadAllText(armipsSymbolsFile);
|
|
||||||
|
|
||||||
var regex = new Regex(ArmipsSymbolRegex);
|
|
||||||
var matches = regex.Matches(armipsSymbols).Cast<Match>();
|
|
||||||
return ParseAddressSymbolMatches(matches);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GenerateCompiledLabelsFile(string fileName,
|
|
||||||
IEnumerable<KeyValuePair<string, uint>> 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<string> 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<string> CodeFiles { get; set; }
|
|
||||||
|
|
||||||
public string GetRootFile(string fileName)
|
|
||||||
{
|
|
||||||
return Path.Combine(Path.GetFullPath(RootDirectory), fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetQuotedRootFile(string fileName)
|
|
||||||
{
|
|
||||||
return "\"" + GetRootFile(fileName) + "\"";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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")]
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<packages>
|
|
||||||
<package id="ELFSharp" version="1.0.4" targetFramework="net461" />
|
|
||||||
<package id="FluentCommandLineParser" version="1.4.3" targetFramework="net461" />
|
|
||||||
<package id="JonSkeet.MiscUtil" version="0.1" targetFramework="net461" />
|
|
||||||
</packages>
|
|
27
insert.bat
27
insert.bat
|
@ -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!
|
|
|
@ -1,5 +1,5 @@
|
||||||
.gba
|
.gba
|
||||||
.open "m12.gba",0x8000000
|
.open "../bin/m12.gba",0x8000000
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
// Relocation hacks
|
// Relocation hacks
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
|
@ -1,12 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace CompileTool
|
|
||||||
{
|
|
||||||
class Program
|
|
||||||
{
|
|
||||||
static void Main(string[] args)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Hello World!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +1,8 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio Version 16
|
||||||
VisualStudioVersion = 15.0.28307.168
|
VisualStudioVersion = 16.0.28407.52
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
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}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptTool", "ScriptTool\ScriptTool.csproj", "{29AB41A8-6405-4C4B-A374-BF1BD7AF1A27}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SymbolTableBuilder", "SymbolTableBuilder\SymbolTableBuilder.csproj", "{2E0E2F98-4EB7-48C7-968D-1BEDCADA37F2}"
|
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
|
Release|Any CPU = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
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.ActiveCfg = Debug|Any CPU
|
||||||
{29AB41A8-6405-4C4B-A374-BF1BD7AF1A27}.Debug|Any CPU.Build.0 = 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
|
{29AB41A8-6405-4C4B-A374-BF1BD7AF1A27}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,8 +20,8 @@ namespace ScriptTool
|
||||||
|
|
||||||
static Compiler()
|
static Compiler()
|
||||||
{
|
{
|
||||||
byte[] widths = File.ReadAllBytes("m2-widths-main.bin");
|
byte[] widths = Asset.ReadAllBytes("m2-widths-main.bin");
|
||||||
byte[] saturnWidths = File.ReadAllBytes("m2-widths-saturn.bin");
|
byte[] saturnWidths = Asset.ReadAllBytes("m2-widths-saturn.bin");
|
||||||
virtualWidths = new int[widths.Length / 2];
|
virtualWidths = new int[widths.Length / 2];
|
||||||
renderWidths = new int[widths.Length / 2];
|
renderWidths = new int[widths.Length / 2];
|
||||||
virtualWidthsSaturn = new int[saturnWidths.Length / 2];
|
virtualWidthsSaturn = new int[saturnWidths.Length / 2];
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace ScriptTool
|
||||||
|
|
||||||
// Load codes
|
// Load codes
|
||||||
Codes = JsonConvert.DeserializeObject<List<EbControlCode>>(
|
Codes = JsonConvert.DeserializeObject<List<EbControlCode>>(
|
||||||
File.ReadAllText("eb-codelist.json"));
|
Asset.ReadAllText("eb-codelist.json"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsMatch(byte[] rom, int address)
|
public bool IsMatch(byte[] rom, int address)
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace ScriptTool
|
||||||
static M12ControlCode()
|
static M12ControlCode()
|
||||||
{
|
{
|
||||||
Codes = JsonConvert.DeserializeObject<List<M12ControlCode>>(
|
Codes = JsonConvert.DeserializeObject<List<M12ControlCode>>(
|
||||||
File.ReadAllText("m12-codelist.json"));
|
Asset.ReadAllText("m12-codelist.json"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsMatch(byte[] rom, int address)
|
public bool IsMatch(byte[] rom, int address)
|
||||||
|
|
|
@ -39,8 +39,8 @@ namespace ScriptTool
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
m12CharLookup = JsonConvert.DeserializeObject<Dictionary<byte, string>>(File.ReadAllText("m12-char-lookup.json"));
|
m12CharLookup = JsonConvert.DeserializeObject<Dictionary<byte, string>>(Asset.ReadAllText("m12-char-lookup.json"));
|
||||||
ebCharLookup = JsonConvert.DeserializeObject<Dictionary<byte, string>>(File.ReadAllText("eb-char-lookup.json"));
|
ebCharLookup = JsonConvert.DeserializeObject<Dictionary<byte, string>>(Asset.ReadAllText("eb-char-lookup.json"));
|
||||||
|
|
||||||
if (options.Command == CommandType.Decompile)
|
if (options.Command == CommandType.Decompile)
|
||||||
{
|
{
|
||||||
|
@ -74,7 +74,7 @@ namespace ScriptTool
|
||||||
using (IncludeFile = File.CreateText(Path.Combine(options.WorkingDirectory, "m12-includes.asm")))
|
using (IncludeFile = File.CreateText(Path.Combine(options.WorkingDirectory, "m12-includes.asm")))
|
||||||
{
|
{
|
||||||
IncludeFile.WriteLine(".gba");
|
IncludeFile.WriteLine(".gba");
|
||||||
IncludeFile.WriteLine(".open \"../m12.gba\",0x8000000");
|
IncludeFile.WriteLine(".open \"../bin/m12.gba\",0x8000000");
|
||||||
|
|
||||||
// Compile main string tables
|
// Compile main string tables
|
||||||
if (options.DoMainText)
|
if (options.DoMainText)
|
||||||
|
|
Loading…
Reference in New Issue