├── .gitattributes ├── .github └── workflows │ └── dotnet.yml ├── .gitignore ├── Kamek ├── AddressMapper.cs ├── App.config ├── CodeFiles │ ├── Alf.cs │ ├── CodeFile.cs │ └── Dol.cs ├── Commands │ ├── BranchCommand.cs │ ├── Command.cs │ ├── PatchExitCommand.cs │ ├── RelocCommand.cs │ └── WriteCommand.cs ├── Elf.cs ├── Hooks │ ├── BranchHook.cs │ ├── Hook.cs │ ├── PatchExitHook.cs │ └── WriteHook.cs ├── Kamek.csproj ├── Kamek.sln ├── KamekFile.cs ├── Linker.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── Util.cs ├── VersionInfo.cs └── Word.cs ├── README.md ├── examples ├── 1-nsmbw-osreport.cpp ├── 2-assembly.S ├── build-1.bat ├── build-1.sh ├── build-2.bat ├── externals-nsmbw-eu-v1.txt └── versions-nsmbw.txt ├── k_stdlib ├── base │ ├── c_stdlib.h │ ├── hooks.h │ └── rvl_sdk.h ├── egg.h ├── kamek.h ├── kamek_asm.S └── nw4r.h ├── loader ├── README.md ├── build_nsmbw.bat ├── build_nsmbw.sh ├── kamekLoader.cpp ├── kamekLoader.h └── nsmbw.cpp ├── preproc_demo.cpp └── shield-fix ├── addresses.txt ├── build.bat └── shield-fix.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: Build with .NET 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: windows-latest 9 | strategy: 10 | matrix: 11 | os: 12 | - win-x64 13 | - osx-x64 14 | - linux-x64 15 | 16 | steps: 17 | - uses: actions/checkout@v3.3.0 18 | - name: Setup .NET 19 | uses: actions/setup-dotnet@v3.0.3 20 | with: 21 | dotnet-version: '6.0.x' 22 | - name: Build Kamek for ${{ matrix.os }} 23 | shell: cmd 24 | run: | 25 | cd ${{ github.workspace }} 26 | mkdir build 27 | cd Kamek 28 | dotnet restore 29 | dotnet build -p:Configuration=Release 30 | pushd %CD% 31 | cd bin\Release\net6.0 32 | mkdir publish 33 | set out=%CD%\publish 34 | set rid=${{ matrix.os }} 35 | popd 36 | dotnet publish -c Release -r %rid% --self-contained true -p:PublishSingleFile=true -o %out%\%rid% 37 | pushd %CD% 38 | cd %out%\%rid% 39 | rm *.pdb 40 | cp Kamek* ${{ github.workspace }}\build 41 | - uses: actions/upload-artifact@v3.1.2 42 | with: 43 | name: ${{ matrix.os }} 44 | path: ${{ github.workspace }}/build 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Kamek build files 5 | *.o 6 | *.dol 7 | gecko.txt 8 | loader.bin 9 | loader.txt 10 | loader.xml 11 | *-loader.bin 12 | *-loader.txt 13 | *-loader.xml 14 | *-dynamic.*.bin 15 | cw/ 16 | 17 | # clangd flags 18 | compile_flags.txt 19 | 20 | # User-specific files 21 | *.suo 22 | *.user 23 | *.userosscache 24 | *.sln.docstates 25 | .vs/ 26 | 27 | # Build results 28 | [Dd]ebug/ 29 | [Dd]ebugPublic/ 30 | [Rr]elease/ 31 | [Rr]eleases/ 32 | x64/ 33 | x86/ 34 | build/ 35 | bld/ 36 | [Bb]in/ 37 | [Oo]bj/ 38 | 39 | # Roslyn cache directories 40 | *.ide/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | #NUNIT 47 | *.VisualState.xml 48 | TestResult.xml 49 | 50 | # Build Results of an ATL Project 51 | [Dd]ebugPS/ 52 | [Rr]eleasePS/ 53 | dlldata.c 54 | 55 | *_i.c 56 | *_p.c 57 | *_i.h 58 | *.ilk 59 | *.meta 60 | *.obj 61 | *.pch 62 | *.pdb 63 | *.pgc 64 | *.pgd 65 | *.rsp 66 | *.sbr 67 | *.tlb 68 | *.tli 69 | *.tlh 70 | *.tmp 71 | *.tmp_proj 72 | *.log 73 | *.vspscc 74 | *.vssscc 75 | .builds 76 | *.pidb 77 | *.svclog 78 | *.scc 79 | 80 | # Chutzpah Test files 81 | _Chutzpah* 82 | 83 | # Visual C++ cache files 84 | ipch/ 85 | *.aps 86 | *.ncb 87 | *.opensdf 88 | *.sdf 89 | *.cachefile 90 | 91 | # Visual Studio profiler 92 | *.psess 93 | *.vsp 94 | *.vspx 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding addin-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | 120 | # MightyMoose 121 | *.mm.* 122 | AutoTest.Net/ 123 | 124 | # Web workbench (sass) 125 | .sass-cache/ 126 | 127 | # Installshield output folder 128 | [Ee]xpress/ 129 | 130 | # DocProject is a documentation generator add-in 131 | DocProject/buildhelp/ 132 | DocProject/Help/*.HxT 133 | DocProject/Help/*.HxC 134 | DocProject/Help/*.hhc 135 | DocProject/Help/*.hhk 136 | DocProject/Help/*.hhp 137 | DocProject/Help/Html2 138 | DocProject/Help/html 139 | 140 | # Click-Once directory 141 | publish/ 142 | 143 | # Publish Web Output 144 | *.[Pp]ublish.xml 145 | *.azurePubxml 146 | # TODO: Comment the next line if you want to checkin your web deploy settings 147 | # but database connection strings (with potential passwords) will be unencrypted 148 | *.pubxml 149 | *.publishproj 150 | 151 | # NuGet Packages 152 | *.nupkg 153 | # The packages folder can be ignored because of Package Restore 154 | **/packages/* 155 | # except build/, which is used as an MSBuild target. 156 | !**/packages/build/ 157 | # If using the old MSBuild-Integrated Package Restore, uncomment this: 158 | #!**/packages/repositories.config 159 | 160 | # Windows Azure Build Output 161 | csx/ 162 | *.build.csdef 163 | 164 | # Windows Store app package directory 165 | AppPackages/ 166 | 167 | # Others 168 | sql/ 169 | *.Cache 170 | ClientBin/ 171 | [Ss]tyle[Cc]op.* 172 | ~$* 173 | *~ 174 | *.dbmdl 175 | *.dbproj.schemaview 176 | *.pfx 177 | *.publishsettings 178 | node_modules/ 179 | 180 | # RIA/Silverlight projects 181 | Generated_Code/ 182 | 183 | # Backup & report files from converting an old project file 184 | # to a newer Visual Studio version. Backup files are not needed, 185 | # because we have git ;-) 186 | _UpgradeReport_Files/ 187 | Backup*/ 188 | UpgradeLog*.XML 189 | UpgradeLog*.htm 190 | 191 | # SQL Server files 192 | *.mdf 193 | *.ldf 194 | 195 | # Business Intelligence projects 196 | *.rdl.data 197 | *.bim.layout 198 | *.bim_*.settings 199 | 200 | # Microsoft Fakes 201 | FakesAssemblies/ 202 | 203 | # ========================= 204 | # Operating System Files 205 | # ========================= 206 | 207 | # OSX 208 | # ========================= 209 | 210 | .DS_Store 211 | .AppleDouble 212 | .LSOverride 213 | 214 | # Thumbnails 215 | ._* 216 | 217 | # Files that might appear on external disk 218 | .Spotlight-V100 219 | .Trashes 220 | 221 | # Directories potentially created on remote AFP share 222 | .AppleDB 223 | .AppleDesktop 224 | Network Trash Folder 225 | Temporary Items 226 | .apdisk 227 | 228 | # Windows 229 | # ========================= 230 | 231 | # Windows image file caches 232 | Thumbs.db 233 | ehthumbs.db 234 | 235 | # Folder config file 236 | Desktop.ini 237 | 238 | # Recycle Bin used on file shares 239 | $RECYCLE.BIN/ 240 | 241 | # Windows Installer files 242 | *.cab 243 | *.msi 244 | *.msm 245 | *.msp 246 | 247 | # Windows shortcuts 248 | *.lnk 249 | -------------------------------------------------------------------------------- /Kamek/AddressMapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | public class AddressMapper 5 | { 6 | public AddressMapper Base = null; 7 | 8 | 9 | struct Mapping 10 | { 11 | public uint start, end; 12 | public int delta; 13 | 14 | public bool Overlaps(Mapping other) 15 | { 16 | return (end >= other.start) && (start <= other.end); 17 | } 18 | 19 | public override string ToString() 20 | { 21 | return string.Format("{0:8X}-{1:8X}: {2}0x{3:X}", start, end, (delta >= 0) ? '+' : '-', Math.Abs(delta)); 22 | } 23 | } 24 | 25 | private List _mappings = new List(); 26 | 27 | public void AddMapping(uint start, uint end, int delta) 28 | { 29 | if (start > end) 30 | throw new ArgumentException(string.Format("cannot map {0:8X}-{1:8X} as start is higher than end", start, end)); 31 | 32 | var newMapping = new Mapping() { start = start, end = end, delta = delta }; 33 | 34 | foreach (var mapping in _mappings) 35 | { 36 | if (mapping.Overlaps(newMapping)) 37 | throw new ArgumentException(string.Format("new mapping {0} overlaps with existing mapping {1}", newMapping, mapping)); 38 | } 39 | 40 | _mappings.Add(newMapping); 41 | } 42 | 43 | 44 | public uint Remap(uint input) 45 | { 46 | if (Base != null) 47 | input = Base.Remap(input); 48 | 49 | foreach (var mapping in _mappings) 50 | { 51 | if (input >= mapping.start && input <= mapping.end) 52 | return (uint)(input + mapping.delta); 53 | } 54 | 55 | return input; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Kamek/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Kamek/CodeFiles/Alf.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | 5 | namespace Kamek.CodeFiles 6 | { 7 | class Alf : CodeFile 8 | { 9 | private const uint MAGIC = 0x464F4252; 10 | 11 | public struct Symbol 12 | { 13 | public uint Address; 14 | public uint Size; 15 | public byte[] MangledName; 16 | public byte[] DemangledName; 17 | public bool IsData; 18 | public uint? Unk10; 19 | } 20 | 21 | public struct Section : IComparable
22 | { 23 | public uint LoadAddress; 24 | public uint Size; // may be smaller than Data.Length, for bss 25 | public byte[] Data; 26 | public List Symbols; 27 | 28 | public uint EndAddress { get { return (uint)(LoadAddress + Size); } } 29 | 30 | public int CompareTo(Section other) 31 | { 32 | return (int)LoadAddress - (int)other.LoadAddress; 33 | } 34 | } 35 | 36 | public uint Version; // 104 or 105 37 | public List
Sections; 38 | public uint EntryPoint; 39 | 40 | 41 | public Alf(Stream input) 42 | { 43 | var br = new BinaryReader(input); 44 | 45 | uint magic = br.ReadLittleUInt32(); 46 | Version = br.ReadLittleUInt32(); 47 | EntryPoint = br.ReadLittleUInt32(); 48 | uint numSections = br.ReadLittleUInt32(); 49 | 50 | if (magic != MAGIC) 51 | throw new InvalidOperationException("wrong ALF magic"); 52 | if (Version != 104 && Version != 105) 53 | throw new InvalidOperationException("unrecognized ALF version"); 54 | 55 | Sections = new List
(); 56 | 57 | for (int i = 0; i < numSections; i++) 58 | { 59 | Section section = new Section(); 60 | section.LoadAddress = br.ReadLittleUInt32(); 61 | int storedSize = br.ReadLittleInt32(); 62 | section.Size = br.ReadLittleUInt32(); 63 | section.Data = br.ReadBytes(storedSize); 64 | section.Symbols = new List(); 65 | Sections.Add(section); 66 | } 67 | 68 | br.ReadLittleUInt32(); // table size, ignored 69 | uint numSymbols = br.ReadLittleUInt32(); 70 | 71 | for (int i = 0; i < numSymbols; i++) 72 | { 73 | Symbol symbol = new Symbol(); 74 | 75 | int mangledNameSize = br.ReadLittleInt32(); 76 | symbol.MangledName = br.ReadBytes(mangledNameSize); 77 | 78 | int demangledNameSize = br.ReadLittleInt32(); 79 | symbol.DemangledName = br.ReadBytes(demangledNameSize); 80 | 81 | symbol.Address = br.ReadLittleUInt32(); 82 | symbol.Size = br.ReadLittleUInt32(); 83 | symbol.IsData = br.ReadLittleUInt32() == 1; 84 | int sectionID = br.ReadLittleInt32(); 85 | 86 | if (Version == 105) 87 | symbol.Unk10 = br.ReadLittleUInt32(); 88 | else 89 | symbol.Unk10 = null; 90 | 91 | Sections[sectionID - 1].Symbols.Add(symbol); 92 | } 93 | } 94 | 95 | 96 | public override void Write(Stream output) 97 | { 98 | var bw = new BinaryWriter(output); 99 | 100 | // Header 101 | bw.WriteLE(MAGIC); 102 | bw.WriteLE(Version); 103 | bw.WriteLE(EntryPoint); 104 | bw.WriteLE((uint)Sections.Count); 105 | 106 | // Sort the sections 107 | Sections.Sort(); 108 | 109 | // Write all sections 110 | foreach (var section in Sections) 111 | { 112 | bw.WriteLE(section.LoadAddress); 113 | bw.WriteLE((uint)section.Data.Length); 114 | bw.WriteLE(section.Size); 115 | bw.Write(section.Data); 116 | } 117 | 118 | // Write the symbol table 119 | long savedPosition = output.Position; 120 | bw.WriteLE((uint)0); // table size 121 | bw.WriteLE((uint)0); // total number of entries 122 | 123 | int symbolCount = 0; 124 | for (int i = 0; i < Sections.Count; i++) 125 | { 126 | Section section = Sections[i]; 127 | 128 | symbolCount += section.Symbols.Count; 129 | 130 | foreach (var symbol in section.Symbols) 131 | { 132 | bw.WriteLE((uint)symbol.MangledName.Length); 133 | bw.Write(symbol.MangledName); 134 | bw.WriteLE((uint)symbol.DemangledName.Length); 135 | bw.Write(symbol.DemangledName); 136 | bw.WriteLE(symbol.Address); 137 | bw.WriteLE(symbol.Size); 138 | bw.WriteLE((uint)(symbol.IsData ? 1 : 0)); 139 | bw.WriteLE((uint)(i + 1)); 140 | if (Version == 105) 141 | bw.WriteLE(symbol.Unk10.GetValueOrDefault()); 142 | } 143 | } 144 | 145 | // 8 extra null bytes for some reason -- don't know why ALF does this ¯\_(ツ)_/¯ 146 | bw.WriteLE((uint)0); 147 | bw.WriteLE((uint)0); 148 | 149 | long tableSize = output.Position - savedPosition - 4; 150 | output.Position = savedPosition; 151 | bw.WriteLE((uint)tableSize); 152 | bw.WriteLE((uint)symbolCount); 153 | } 154 | 155 | 156 | public bool ResolveAddress(uint address, out int sectionID, out uint offset) 157 | { 158 | for (int i = 0; i < Sections.Count; i++) 159 | { 160 | if (address >= Sections[i].LoadAddress && address < Sections[i].EndAddress) 161 | { 162 | sectionID = i; 163 | offset = address - Sections[i].LoadAddress; 164 | return true; 165 | } 166 | } 167 | 168 | sectionID = -1; 169 | offset = 0; 170 | return false; 171 | } 172 | 173 | 174 | public override uint ReadUInt32(uint address) 175 | { 176 | int sectionID; 177 | uint offset; 178 | if (!ResolveAddress(address, out sectionID, out offset)) 179 | throw new InvalidOperationException("address out of range in ALF file"); 180 | 181 | return Util.ExtractUInt32(Sections[sectionID].Data, offset); 182 | } 183 | 184 | public override void WriteUInt32(uint address, uint value) 185 | { 186 | int sectionID; 187 | uint offset; 188 | if (!ResolveAddress(address, out sectionID, out offset)) 189 | throw new InvalidOperationException("address out of range in ALF file"); 190 | 191 | Util.InjectUInt32(Sections[sectionID].Data, offset, value); 192 | } 193 | 194 | 195 | public override ushort ReadUInt16(uint address) 196 | { 197 | int sectionID; 198 | uint offset; 199 | if (!ResolveAddress(address, out sectionID, out offset)) 200 | throw new InvalidOperationException("address out of range in ALF file"); 201 | 202 | return Util.ExtractUInt16(Sections[sectionID].Data, offset); 203 | } 204 | 205 | public override void WriteUInt16(uint address, ushort value) 206 | { 207 | int sectionID; 208 | uint offset; 209 | if (!ResolveAddress(address, out sectionID, out offset)) 210 | throw new InvalidOperationException("address out of range in ALF file"); 211 | 212 | Util.InjectUInt16(Sections[sectionID].Data, offset, value); 213 | } 214 | 215 | 216 | public override byte ReadByte(uint address) 217 | { 218 | int sectionID; 219 | uint offset; 220 | if (!ResolveAddress(address, out sectionID, out offset)) 221 | throw new InvalidOperationException("address out of range in ALF file"); 222 | 223 | return Sections[sectionID].Data[offset]; 224 | } 225 | 226 | public override void WriteByte(uint address, byte value) 227 | { 228 | int sectionID; 229 | uint offset; 230 | if (!ResolveAddress(address, out sectionID, out offset)) 231 | throw new InvalidOperationException("address out of range in ALF file"); 232 | 233 | Sections[sectionID].Data[offset] = value; 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /Kamek/CodeFiles/CodeFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Kamek.CodeFiles 9 | { 10 | abstract class CodeFile 11 | { 12 | public abstract void Write(Stream output); 13 | public abstract uint ReadUInt32(uint address); 14 | public abstract void WriteUInt32(uint address, uint value); 15 | public abstract ushort ReadUInt16(uint address); 16 | public abstract void WriteUInt16(uint address, ushort value); 17 | public abstract byte ReadByte(uint address); 18 | public abstract void WriteByte(uint address, byte value); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Kamek/CodeFiles/Dol.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Kamek.CodeFiles 5 | { 6 | class Dol : CodeFile 7 | { 8 | public struct Section 9 | { 10 | public uint LoadAddress; 11 | public byte[] Data; 12 | 13 | public uint EndAddress { get { return (uint)(LoadAddress + Data.Length); } } 14 | } 15 | 16 | public readonly Section[] Sections; 17 | public uint EntryPoint; 18 | public uint BssAddress, BssSize; 19 | 20 | 21 | public Dol(Stream input) 22 | { 23 | Sections = new Section[18]; 24 | var br = new BinaryReader(input); 25 | 26 | var fields = new uint[3 * 18]; 27 | for (int i = 0; i < fields.Length; i++) 28 | fields[i] = br.ReadBigUInt32(); 29 | 30 | for (int i = 0; i < 18; i++) 31 | { 32 | uint fileOffset = fields[i]; 33 | uint size = fields[36 + i]; 34 | Sections[i].LoadAddress = fields[18 + i]; 35 | 36 | long savedPosition = input.Position; 37 | input.Position = fileOffset; 38 | Sections[i].Data = br.ReadBytes((int) size); 39 | input.Position = savedPosition; 40 | } 41 | 42 | BssAddress = br.ReadBigUInt32(); 43 | BssSize = br.ReadBigUInt32(); 44 | EntryPoint = br.ReadBigUInt32(); 45 | } 46 | 47 | 48 | public override void Write(Stream output) 49 | { 50 | var bw = new BinaryWriter(output); 51 | 52 | // Generate the header 53 | var fields = new uint[3 * 18]; 54 | uint position = 0x100; 55 | for (int i = 0; i < 18; i++) 56 | { 57 | if (Sections[i].Data.Length > 0) 58 | { 59 | fields[i] = position; 60 | fields[i + 18] = Sections[i].LoadAddress; 61 | fields[i + 36] = (uint)Sections[i].Data.Length; 62 | position += (uint)((Sections[i].Data.Length + 0x1F) & ~0x1F); 63 | } 64 | } 65 | 66 | for (int i = 0; i < (3 * 18); i++) 67 | bw.WriteBE(fields[i]); 68 | bw.WriteBE(BssAddress); 69 | bw.WriteBE(BssSize); 70 | bw.WriteBE(EntryPoint); 71 | bw.Write(new byte[0x100 - 0xE4]); 72 | 73 | // Write all sections 74 | for (int i = 0; i < 18; i++) 75 | { 76 | bw.Write(Sections[i].Data); 77 | 78 | int paddedLength = ((Sections[i].Data.Length + 0x1F) & ~0x1F); 79 | int padding = paddedLength - Sections[i].Data.Length; 80 | if (padding > 0) 81 | bw.Write(new byte[padding]); 82 | } 83 | } 84 | 85 | 86 | public bool ResolveAddress(uint address, out int sectionID, out uint offset) 87 | { 88 | for (int i = 0; i < Sections.Length; i++) 89 | { 90 | if (address >= Sections[i].LoadAddress && address < Sections[i].EndAddress) 91 | { 92 | sectionID = i; 93 | offset = address - Sections[i].LoadAddress; 94 | return true; 95 | } 96 | } 97 | 98 | sectionID = -1; 99 | offset = 0; 100 | return false; 101 | } 102 | 103 | 104 | public override uint ReadUInt32(uint address) 105 | { 106 | int sectionID; 107 | uint offset; 108 | if (!ResolveAddress(address, out sectionID, out offset)) 109 | throw new InvalidOperationException("address out of range in DOL file"); 110 | 111 | return Util.ExtractUInt32(Sections[sectionID].Data, offset); 112 | } 113 | 114 | public override void WriteUInt32(uint address, uint value) 115 | { 116 | int sectionID; 117 | uint offset; 118 | if (!ResolveAddress(address, out sectionID, out offset)) 119 | throw new InvalidOperationException("address out of range in DOL file"); 120 | 121 | Util.InjectUInt32(Sections[sectionID].Data, offset, value); 122 | } 123 | 124 | 125 | public override ushort ReadUInt16(uint address) 126 | { 127 | int sectionID; 128 | uint offset; 129 | if (!ResolveAddress(address, out sectionID, out offset)) 130 | throw new InvalidOperationException("address out of range in DOL file"); 131 | 132 | return Util.ExtractUInt16(Sections[sectionID].Data, offset); 133 | } 134 | 135 | public override void WriteUInt16(uint address, ushort value) 136 | { 137 | int sectionID; 138 | uint offset; 139 | if (!ResolveAddress(address, out sectionID, out offset)) 140 | throw new InvalidOperationException("address out of range in DOL file"); 141 | 142 | Util.InjectUInt16(Sections[sectionID].Data, offset, value); 143 | } 144 | 145 | 146 | public override byte ReadByte(uint address) 147 | { 148 | int sectionID; 149 | uint offset; 150 | if (!ResolveAddress(address, out sectionID, out offset)) 151 | throw new InvalidOperationException("address out of range in DOL file"); 152 | 153 | return Sections[sectionID].Data[offset]; 154 | } 155 | 156 | public override void WriteByte(uint address, byte value) 157 | { 158 | int sectionID; 159 | uint offset; 160 | if (!ResolveAddress(address, out sectionID, out offset)) 161 | throw new InvalidOperationException("address out of range in DOL file"); 162 | 163 | Sections[sectionID].Data[offset] = value; 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /Kamek/Commands/BranchCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Kamek.Commands 9 | { 10 | class BranchCommand : Command 11 | { 12 | public readonly Word Target; 13 | 14 | public BranchCommand(Word source, Word target, bool isLink) 15 | : base(isLink ? Ids.BranchLink : Ids.Branch, source) 16 | { 17 | Target = target; 18 | } 19 | 20 | public override void WriteArguments(BinaryWriter bw) 21 | { 22 | Target.AssertNotAmbiguous(); 23 | bw.WriteBE(Target.Value); 24 | } 25 | 26 | public override string PackForRiivolution() 27 | { 28 | Address.Value.AssertAbsolute(); 29 | Target.AssertAbsolute(); 30 | 31 | return string.Format("", Address.Value.Value, GenerateInstruction()); 32 | } 33 | 34 | public override string PackForDolphin() 35 | { 36 | Address.Value.AssertAbsolute(); 37 | Target.AssertAbsolute(); 38 | 39 | return string.Format("0x{0:X8}:dword:0x{1:X8}", Address.Value.Value, GenerateInstruction()); 40 | } 41 | 42 | public override IEnumerable PackGeckoCodes() 43 | { 44 | Address.Value.AssertAbsolute(); 45 | Target.AssertAbsolute(); 46 | 47 | ulong code = ((ulong)(Address.Value.Value & 0x1FFFFFF) << 32) | GenerateInstruction(); 48 | code |= 0x4000000UL << 32; 49 | 50 | return new ulong[1] { code }; 51 | } 52 | 53 | public override IEnumerable PackActionReplayCodes() 54 | { 55 | Address.Value.AssertAbsolute(); 56 | Target.AssertAbsolute(); 57 | 58 | ulong code = ((ulong)(Address.Value.Value & 0x1FFFFFF) << 32) | GenerateInstruction(); 59 | code |= 0x4000000UL << 32; 60 | 61 | return new ulong[1] { code }; 62 | } 63 | 64 | public override bool Apply(KamekFile file) 65 | { 66 | if (Address.Value.Type == file.BaseAddress.Type 67 | && file.Contains(Address.Value) 68 | && Address.Value.Type == Target.Type) 69 | { 70 | file.WriteUInt32(Address.Value, GenerateInstruction()); 71 | return true; 72 | } 73 | 74 | return false; 75 | } 76 | 77 | public override void ApplyToCodeFile(CodeFiles.CodeFile file) 78 | { 79 | Address.Value.AssertAbsolute(); 80 | Target.AssertAbsolute(); 81 | 82 | file.WriteUInt32(Address.Value.Value, GenerateInstruction()); 83 | } 84 | 85 | 86 | private uint GenerateInstruction() 87 | { 88 | long delta = Target - Address.Value; 89 | uint insn = (Id == Ids.BranchLink) ? 0x48000001U : 0x48000000U; 90 | insn |= ((uint)delta & 0x3FFFFFC); 91 | return insn; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Kamek/Commands/Command.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Kamek.Commands 9 | { 10 | abstract class Command 11 | { 12 | public enum Ids : byte 13 | { 14 | Null = 0, 15 | 16 | // these deliberately match the ELF relocations 17 | Addr32 = 1, 18 | Addr16Lo = 4, 19 | Addr16Hi = 5, 20 | Addr16Ha = 6, 21 | Rel24 = 10, 22 | 23 | // these are new 24 | WritePointer = 1, // same as Addr32 on purpose 25 | Write32 = 32, 26 | Write16 = 33, 27 | Write8 = 34, 28 | CondWritePointer = 35, 29 | CondWrite32 = 36, 30 | CondWrite16 = 37, 31 | CondWrite8 = 38, 32 | 33 | Branch = 64, 34 | BranchLink = 65, 35 | } 36 | 37 | public readonly Ids Id; 38 | 39 | private Word? _Address; 40 | public Word? Address 41 | { 42 | get => _Address; 43 | protected set 44 | { 45 | _Address = value; 46 | } 47 | } 48 | 49 | protected Command(Ids id, Word? address) 50 | { 51 | Id = id; 52 | Address = address; 53 | } 54 | 55 | public abstract void WriteArguments(BinaryWriter bw); 56 | public abstract string PackForRiivolution(); 57 | public abstract string PackForDolphin(); 58 | public abstract IEnumerable PackGeckoCodes(); 59 | public abstract IEnumerable PackActionReplayCodes(); 60 | public abstract bool Apply(KamekFile file); 61 | public abstract void ApplyToCodeFile(CodeFiles.CodeFile file); 62 | 63 | public virtual void CalculateAddress(KamekFile file) {} 64 | 65 | public void AssertAddressNonNull() 66 | { 67 | if (!Address.HasValue) 68 | throw new NullReferenceException(string.Format("{0} command must have an address in this context", Id)); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Kamek/Commands/PatchExitCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Kamek.Commands 9 | { 10 | class PatchExitCommand : Command 11 | { 12 | public readonly Word FunctionStart; 13 | public readonly Word Target; 14 | 15 | public PatchExitCommand(Word functionStart, Word target) 16 | : base(Ids.Branch, null) 17 | { 18 | FunctionStart = functionStart; 19 | Target = target; 20 | } 21 | 22 | public override void WriteArguments(BinaryWriter bw) 23 | { 24 | Target.AssertNotAmbiguous(); 25 | bw.WriteBE(Target.Value); 26 | } 27 | 28 | public override void CalculateAddress(KamekFile file) 29 | { 30 | // Do some reasonableness checks. 31 | // For now, we'll only work on functions ending in a blr 32 | var functionSize = file.QuerySymbolSize(FunctionStart); 33 | if (functionSize < 4) 34 | { 35 | throw new InvalidOperationException("Function too small!"); 36 | } 37 | var functionEnd = FunctionStart + (functionSize - 4); 38 | if (file.ReadUInt32(functionEnd) != 0x4E800020) 39 | { 40 | throw new InvalidOperationException("Function does not end in blr"); 41 | } 42 | 43 | // Just to be extra sure, are there any other returns in this function? 44 | for (var check = FunctionStart; check < functionEnd; check += 4) 45 | { 46 | var insn = file.ReadUInt32(check); 47 | if ((insn & 0xFC00FFFF) == 0x4C000020) 48 | { 49 | throw new InvalidOperationException("Function contains a return partway through"); 50 | } 51 | } 52 | 53 | Address = functionEnd; 54 | } 55 | 56 | public override string PackForRiivolution() 57 | { 58 | throw new NotImplementedException(); 59 | } 60 | 61 | public override string PackForDolphin() 62 | { 63 | throw new NotImplementedException(); 64 | } 65 | 66 | public override IEnumerable PackGeckoCodes() 67 | { 68 | throw new NotImplementedException(); 69 | } 70 | 71 | public override IEnumerable PackActionReplayCodes() 72 | { 73 | throw new NotImplementedException(); 74 | } 75 | 76 | public override void ApplyToCodeFile(CodeFiles.CodeFile file) 77 | { 78 | throw new NotImplementedException(); 79 | } 80 | 81 | public override bool Apply(KamekFile file) 82 | { 83 | if (Address.Value.Type == file.BaseAddress.Type 84 | && file.Contains(Address.Value) 85 | && Address.Value.Type == Target.Type) 86 | { 87 | file.WriteUInt32(Address.Value, GenerateInstruction()); 88 | return true; 89 | } 90 | 91 | return false; 92 | } 93 | 94 | 95 | private uint GenerateInstruction() 96 | { 97 | long delta = Target - Address.Value; 98 | uint insn = (Id == Ids.BranchLink) ? 0x48000001U : 0x48000000U; 99 | insn |= ((uint)delta & 0x3FFFFFC); 100 | return insn; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Kamek/Commands/RelocCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Kamek.Commands 9 | { 10 | class RelocCommand : Command 11 | { 12 | public readonly Word Target; 13 | 14 | public RelocCommand(Word source, Word target, Elf.Reloc reloc) 15 | : base((Ids)reloc, source) 16 | { 17 | Target = target; 18 | } 19 | 20 | public override void WriteArguments(BinaryWriter bw) 21 | { 22 | Target.AssertNotAmbiguous(); 23 | bw.WriteBE(Target.Value); 24 | } 25 | 26 | public override string PackForRiivolution() 27 | { 28 | throw new NotImplementedException(); 29 | } 30 | 31 | public override string PackForDolphin() 32 | { 33 | throw new NotImplementedException(); 34 | } 35 | 36 | public override IEnumerable PackGeckoCodes() 37 | { 38 | throw new NotImplementedException(); 39 | } 40 | 41 | public override IEnumerable PackActionReplayCodes() 42 | { 43 | throw new NotImplementedException(); 44 | } 45 | 46 | public override void ApplyToCodeFile(CodeFiles.CodeFile file) 47 | { 48 | Address.Value.AssertAbsolute(); 49 | Target.AssertAbsolute(); 50 | 51 | switch (Id) 52 | { 53 | case Ids.Rel24: 54 | long delta = Target - Address.Value; 55 | uint insn = file.ReadUInt32(Address.Value.Value) & 0xFC000003; 56 | insn |= ((uint)delta & 0x3FFFFFC); 57 | file.WriteUInt32(Address.Value.Value, insn); 58 | break; 59 | 60 | case Ids.Addr32: 61 | file.WriteUInt32(Address.Value.Value, Target.Value); 62 | break; 63 | 64 | case Ids.Addr16Lo: 65 | file.WriteUInt16(Address.Value.Value, (ushort)(Target.Value & 0xFFFF)); 66 | break; 67 | 68 | case Ids.Addr16Hi: 69 | file.WriteUInt16(Address.Value.Value, (ushort)(Target.Value >> 16)); 70 | break; 71 | 72 | case Ids.Addr16Ha: 73 | ushort v = (ushort)(Target.Value >> 16); 74 | if ((Target.Value & 0x8000) == 0x8000) 75 | v++; 76 | file.WriteUInt16(Address.Value.Value, v); 77 | break; 78 | 79 | default: 80 | throw new NotImplementedException("unrecognised relocation type"); 81 | } 82 | } 83 | 84 | public override bool Apply(KamekFile file) 85 | { 86 | if (Address.Value.Type != file.BaseAddress.Type) 87 | return false; 88 | 89 | switch (Id) 90 | { 91 | case Ids.Rel24: 92 | if ((Address.Value.IsAbsolute && Target.IsAbsolute) || (Address.Value.IsRelative && Target.IsRelative)) 93 | { 94 | long delta = Target - Address.Value; 95 | uint insn = file.ReadUInt32(Address.Value) & 0xFC000003; 96 | insn |= ((uint)delta & 0x3FFFFFC); 97 | file.WriteUInt32(Address.Value, insn); 98 | 99 | return true; 100 | } 101 | break; 102 | 103 | case Ids.Addr32: 104 | if (Target.IsAbsolute) 105 | { 106 | file.WriteUInt32(Address.Value, Target.Value); 107 | return true; 108 | } 109 | break; 110 | 111 | case Ids.Addr16Lo: 112 | if (Target.IsAbsolute) 113 | { 114 | file.WriteUInt16(Address.Value, (ushort)(Target.Value & 0xFFFF)); 115 | return true; 116 | } 117 | break; 118 | 119 | case Ids.Addr16Hi: 120 | if (Target.IsAbsolute) 121 | { 122 | file.WriteUInt16(Address.Value, (ushort)(Target.Value >> 16)); 123 | return true; 124 | } 125 | break; 126 | 127 | case Ids.Addr16Ha: 128 | if (Target.IsAbsolute) 129 | { 130 | ushort v = (ushort)(Target.Value >> 16); 131 | if ((Target.Value & 0x8000) == 0x8000) 132 | v++; 133 | file.WriteUInt16(Address.Value, v); 134 | return true; 135 | } 136 | break; 137 | 138 | default: 139 | throw new NotImplementedException("unrecognised relocation type"); 140 | } 141 | 142 | return false; 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Kamek/Commands/WriteCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Kamek.Commands 9 | { 10 | class WriteCommand : Command 11 | { 12 | public enum Type 13 | { 14 | Pointer = 1, 15 | Value32 = 2, 16 | Value16 = 3, 17 | Value8 = 4 18 | } 19 | 20 | private static Ids IdFromType(Type type, bool isConditional) 21 | { 22 | if (isConditional) 23 | { 24 | switch (type) 25 | { 26 | case Type.Pointer: return Ids.CondWritePointer; 27 | case Type.Value32: return Ids.CondWrite32; 28 | case Type.Value16: return Ids.CondWrite16; 29 | case Type.Value8: return Ids.CondWrite8; 30 | } 31 | } 32 | else 33 | { 34 | switch (type) 35 | { 36 | case Type.Pointer: return Ids.WritePointer; 37 | case Type.Value32: return Ids.Write32; 38 | case Type.Value16: return Ids.Write16; 39 | case Type.Value8: return Ids.Write8; 40 | } 41 | } 42 | 43 | throw new NotImplementedException(); 44 | } 45 | 46 | 47 | 48 | public readonly Type ValueType; 49 | public readonly Word Value; 50 | public readonly Word? Original; 51 | 52 | public WriteCommand(Word address, Word value, Type valueType, Word? original) 53 | : base(IdFromType(valueType, original.HasValue), address) 54 | { 55 | Value = value; 56 | ValueType = valueType; 57 | Original = original; 58 | } 59 | 60 | public override void WriteArguments(BinaryWriter bw) 61 | { 62 | if (ValueType == Type.Pointer) 63 | Value.AssertNotAmbiguous(); 64 | else 65 | Value.AssertValue(); 66 | 67 | bw.WriteBE(Value.Value); 68 | 69 | if (Original.HasValue) 70 | { 71 | Original.Value.AssertNotRelative(); 72 | bw.WriteBE(Original.Value.Value); 73 | } 74 | } 75 | 76 | public override string PackForRiivolution() 77 | { 78 | Address.Value.AssertAbsolute(); 79 | if (ValueType == Type.Pointer) 80 | Value.AssertAbsolute(); 81 | else 82 | Value.AssertValue(); 83 | 84 | if (Original.HasValue) 85 | { 86 | Original.Value.AssertNotRelative(); 87 | 88 | switch (ValueType) 89 | { 90 | case Type.Value8: return string.Format("", Address.Value.Value, Value.Value, Original.Value.Value); 91 | case Type.Value16: return string.Format("", Address.Value.Value, Value.Value, Original.Value.Value); 92 | case Type.Value32: 93 | case Type.Pointer: return string.Format("", Address.Value.Value, Value.Value, Original.Value.Value); 94 | } 95 | } 96 | else 97 | { 98 | switch (ValueType) 99 | { 100 | case Type.Value8: return string.Format("", Address.Value.Value, Value.Value); 101 | case Type.Value16: return string.Format("", Address.Value.Value, Value.Value); 102 | case Type.Value32: 103 | case Type.Pointer: return string.Format("", Address.Value.Value, Value.Value); 104 | } 105 | } 106 | 107 | return null; 108 | } 109 | 110 | public override string PackForDolphin() 111 | { 112 | Address.Value.AssertAbsolute(); 113 | if (ValueType == Type.Pointer) 114 | Value.AssertAbsolute(); 115 | else 116 | Value.AssertValue(); 117 | 118 | switch (ValueType) 119 | { 120 | case Type.Value8: return string.Format("0x{0:X8}:byte:0x000000{1:X2}", Address.Value.Value, Value.Value); 121 | case Type.Value16: return string.Format("0x{0:X8}:word:0x0000{1:X4}", Address.Value.Value, Value.Value); 122 | case Type.Value32: 123 | case Type.Pointer: return string.Format("0x{0:X8}:dword:0x{1:X8}", Address.Value.Value, Value.Value); 124 | } 125 | 126 | return null; 127 | } 128 | 129 | public override IEnumerable PackGeckoCodes() 130 | { 131 | Address.Value.AssertAbsolute(); 132 | if (ValueType == Type.Pointer) 133 | Value.AssertAbsolute(); 134 | else 135 | Value.AssertValue(); 136 | 137 | if (Address.Value.Value >= 0x90000000) 138 | throw new NotImplementedException("MEM2 writes not yet supported for gecko"); 139 | 140 | ulong code = ((ulong)(Address.Value.Value & 0x1FFFFFF) << 32) | Value.Value; 141 | switch (ValueType) 142 | { 143 | case Type.Value16: code |= 0x2000000UL << 32; break; 144 | case Type.Value32: 145 | case Type.Pointer: code |= 0x4000000UL << 32; break; 146 | } 147 | 148 | if (Original.HasValue) 149 | { 150 | if (ValueType == Type.Pointer) 151 | Original.Value.AssertAbsolute(); 152 | else 153 | Original.Value.AssertValue(); 154 | 155 | if (ValueType == Type.Value8) 156 | { 157 | // Gecko doesn't natively support conditional 8-bit writes, 158 | // so we have to implement it manually with a code embedding PPC... 159 | 160 | uint addrTop = (uint)(Address.Value.Value >> 16); 161 | uint addrBtm = (uint)(Address.Value.Value & 0xFFFF); 162 | uint orig = (uint)Original.Value.Value; 163 | uint value = (uint)Value.Value; 164 | 165 | // r0 and r3 empirically *seem* to be available, though there's zero documentation on this 166 | // r4 is definitely NOT available (codehandler dies if you mess with it) 167 | uint inst1 = 0x3C600000 | addrTop; // lis r3, X 168 | uint inst2 = 0x60630000 | addrBtm; // ori r3, r3, X 169 | uint inst3 = 0x88030000; // lbz r0, 0(r3) 170 | uint inst4 = 0x2C000000 | orig; // cmpwi r0, X 171 | uint inst5 = 0x4082000C; // bne @end 172 | uint inst6 = 0x38000000 | value; // li r0, X 173 | uint inst7 = 0x98030000; // stb r0, 0(r3) 174 | uint inst8 = 0x4E800020; // @end: blr 175 | 176 | return new ulong[5] { 177 | (0xC0000000UL << 32) | 4, // "4" for four lines of instruction data below 178 | ((ulong)inst1 << 32) | inst2, 179 | ((ulong)inst3 << 32) | inst4, 180 | ((ulong)inst5 << 32) | inst6, 181 | ((ulong)inst7 << 32) | inst8 182 | }; 183 | } 184 | else 185 | { 186 | // Sandwich the write between "if" and "endif" codes 187 | 188 | ulong if_start = ((ulong)(Address.Value.Value & 0x1FFFFFF) << 32) | Original.Value.Value; 189 | 190 | switch (ValueType) 191 | { 192 | case Type.Value16: if_start |= 0x28000000UL << 32; break; 193 | case Type.Value32: 194 | case Type.Pointer: if_start |= 0x20000000UL << 32; break; 195 | } 196 | 197 | ulong if_end = 0xE2000001UL << 32; 198 | 199 | return new ulong[3] { if_start, code, if_end }; 200 | } 201 | 202 | } 203 | else 204 | { 205 | return new ulong[1] { code }; 206 | } 207 | } 208 | 209 | public override IEnumerable PackActionReplayCodes() 210 | { 211 | Address.Value.AssertAbsolute(); 212 | if (ValueType == Type.Pointer) 213 | Value.AssertAbsolute(); 214 | else 215 | Value.AssertValue(); 216 | 217 | if (Address.Value.Value >= 0x90000000) 218 | throw new NotImplementedException("MEM2 writes not yet supported for action replay"); 219 | 220 | ulong code = ((ulong)(Address.Value.Value & 0x1FFFFFF) << 32) | Value.Value; 221 | switch (ValueType) 222 | { 223 | case Type.Value16: code |= 0x2000000UL << 32; break; 224 | case Type.Value32: 225 | case Type.Pointer: code |= 0x4000000UL << 32; break; 226 | } 227 | 228 | if (Original.HasValue) 229 | { 230 | if (ValueType == Type.Pointer) 231 | Original.Value.AssertAbsolute(); 232 | else 233 | Original.Value.AssertValue(); 234 | 235 | ulong if_start = ((ulong)(Address.Value.Value & 0x1FFFFFF) << 32) | Original.Value.Value; 236 | switch (ValueType) 237 | { 238 | case Type.Value8: if_start |= 0x08000000UL << 32; break; 239 | case Type.Value16: if_start |= 0x0A000000UL << 32; break; 240 | case Type.Value32: 241 | case Type.Pointer: if_start |= 0x0C000000UL << 32; break; 242 | } 243 | 244 | return new ulong[2] { if_start, code }; 245 | } 246 | else 247 | { 248 | return new ulong[1] { code }; 249 | } 250 | } 251 | 252 | public override void ApplyToCodeFile(CodeFiles.CodeFile file) 253 | { 254 | Address.Value.AssertAbsolute(); 255 | if (ValueType == Type.Pointer) 256 | Value.AssertAbsolute(); 257 | else 258 | Value.AssertValue(); 259 | 260 | if (Original.HasValue) 261 | { 262 | bool patchOK = false; 263 | switch (ValueType) 264 | { 265 | case Type.Value8: 266 | patchOK = (file.ReadByte(Address.Value.Value) == Original.Value.Value); 267 | break; 268 | case Type.Value16: 269 | patchOK = (file.ReadUInt16(Address.Value.Value) == Original.Value.Value); 270 | break; 271 | case Type.Value32: 272 | case Type.Pointer: 273 | patchOK = (file.ReadUInt32(Address.Value.Value) == Original.Value.Value); 274 | break; 275 | } 276 | if (!patchOK) 277 | return; 278 | } 279 | 280 | switch (ValueType) 281 | { 282 | case Type.Value8: file.WriteByte(Address.Value.Value, (byte)Value.Value); break; 283 | case Type.Value16: file.WriteUInt16(Address.Value.Value, (ushort)Value.Value); break; 284 | case Type.Value32: 285 | case Type.Pointer: file.WriteUInt32(Address.Value.Value, Value.Value); break; 286 | } 287 | } 288 | 289 | public override bool Apply(KamekFile file) 290 | { 291 | return false; 292 | } 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /Kamek/Elf.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Kamek 9 | { 10 | class Elf 11 | { 12 | class ElfHeader 13 | { 14 | public uint ei_mag; 15 | public byte ei_class, ei_data, ei_version, ei_osabi, ei_abiversion; 16 | public ushort e_type, e_machine; 17 | public uint e_version, e_entry, e_phoff, e_shoff, e_flags; 18 | public ushort e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx; 19 | 20 | public static ElfHeader Read(BinaryReader reader) 21 | { 22 | var h = new ElfHeader(); 23 | 24 | h.ei_mag = reader.ReadBigUInt32(); 25 | if (h.ei_mag != 0x7F454C46) // "\x7F" "ELF" 26 | throw new InvalidDataException("Incorrect ELF header"); 27 | 28 | h.ei_class = reader.ReadByte(); 29 | if (h.ei_class != 1) 30 | throw new InvalidDataException("Only 32-bit ELF files are supported"); 31 | 32 | h.ei_data = reader.ReadByte(); 33 | if (h.ei_data != 2) 34 | throw new InvalidDataException("Only big-endian ELF files are supported"); 35 | 36 | h.ei_version = reader.ReadByte(); 37 | if (h.ei_version != 1) 38 | throw new InvalidDataException("Only ELF version 1 is supported [a]"); 39 | 40 | h.ei_osabi = reader.ReadByte(); 41 | h.ei_abiversion = reader.ReadByte(); 42 | reader.BaseStream.Seek(7, SeekOrigin.Current); 43 | 44 | h.e_type = reader.ReadBigUInt16(); 45 | h.e_machine = reader.ReadBigUInt16(); 46 | h.e_version = reader.ReadBigUInt32(); 47 | if (h.e_version != 1) 48 | throw new InvalidDataException("Only ELF version 1 is supported [b]"); 49 | 50 | h.e_entry = reader.ReadBigUInt32(); 51 | h.e_phoff = reader.ReadBigUInt32(); 52 | h.e_shoff = reader.ReadBigUInt32(); 53 | h.e_flags = reader.ReadBigUInt32(); 54 | h.e_ehsize = reader.ReadBigUInt16(); 55 | h.e_phentsize = reader.ReadBigUInt16(); 56 | h.e_phnum = reader.ReadBigUInt16(); 57 | h.e_shentsize = reader.ReadBigUInt16(); 58 | h.e_shnum = reader.ReadBigUInt16(); 59 | h.e_shstrndx = reader.ReadBigUInt16(); 60 | 61 | return h; 62 | } 63 | } 64 | 65 | 66 | public class ElfSection 67 | { 68 | public enum Type : uint 69 | { 70 | SHT_NULL = 0, 71 | SHT_PROGBITS, 72 | SHT_SYMTAB, 73 | SHT_STRTAB, 74 | SHT_RELA, 75 | SHT_HASH, 76 | SHT_DYNAMIC, 77 | SHT_NOTE, 78 | SHT_NOBITS, 79 | SHT_REL, 80 | SHT_SHLIB, 81 | SHT_DYNSYM, 82 | SHT_LOPROC = 0x70000000, 83 | SHT_HIPROC = 0x7fffffff, 84 | SHT_LOUSER = 0x80000000, 85 | SHT_HIUSER = 0xffffffff 86 | } 87 | [Flags] 88 | public enum Flags : uint 89 | { 90 | SHF_WRITE = 1, 91 | SHF_ALLOC = 2, 92 | SHF_EXECINSTR = 4, 93 | SHF_MASKPROC = 0xf0000000 94 | } 95 | public uint sh_name; 96 | public Type sh_type; 97 | public Flags sh_flags; 98 | public uint sh_addr, sh_size; 99 | public uint sh_link, sh_info, sh_addralign, sh_entsize; 100 | public string name; 101 | public byte[] data; 102 | 103 | public static ElfSection Read(BinaryReader reader) 104 | { 105 | var s = new ElfSection(); 106 | 107 | s.sh_name = reader.ReadBigUInt32(); 108 | s.sh_type = (Type)reader.ReadBigUInt32(); 109 | s.sh_flags = (Flags)reader.ReadBigUInt32(); 110 | s.sh_addr = reader.ReadBigUInt32(); 111 | uint sh_offset = reader.ReadBigUInt32(); 112 | s.sh_size = reader.ReadBigUInt32(); 113 | s.sh_link = reader.ReadBigUInt32(); 114 | s.sh_info = reader.ReadBigUInt32(); 115 | s.sh_addralign = reader.ReadBigUInt32(); 116 | s.sh_entsize = reader.ReadBigUInt32(); 117 | 118 | if (s.sh_type != Type.SHT_NULL && s.sh_type != Type.SHT_NOBITS) 119 | { 120 | long savePos = reader.BaseStream.Position; 121 | reader.BaseStream.Position = sh_offset; 122 | s.data = reader.ReadBytes((int) s.sh_size); 123 | reader.BaseStream.Position = savePos; 124 | } 125 | 126 | return s; 127 | } 128 | } 129 | 130 | 131 | public enum SymBind 132 | { 133 | STB_LOCAL = 0, 134 | STB_GLOBAL = 1, 135 | STB_WEAK = 2, 136 | STB_LOPROC = 13, 137 | STB_HIPROC = 15 138 | } 139 | public enum SymType 140 | { 141 | STT_NOTYPE = 0, 142 | STT_OBJECT, 143 | STT_FUNC, 144 | STT_SECTION, 145 | STT_FILE, 146 | STT_LOPROC = 13, 147 | STT_HIPROC = 15 148 | } 149 | 150 | 151 | public enum Reloc 152 | { 153 | R_PPC_ADDR32 = 1, 154 | R_PPC_ADDR16_LO = 4, 155 | R_PPC_ADDR16_HI = 5, 156 | R_PPC_ADDR16_HA = 6, 157 | R_PPC_REL24 = 10 158 | } 159 | 160 | 161 | private ElfHeader _header; 162 | private List _sections = new List(); 163 | 164 | public IList Sections { get { return _sections; } } 165 | 166 | public Elf(Stream input) 167 | { 168 | var reader = new BinaryReader(input); 169 | 170 | _header = ElfHeader.Read(reader); 171 | 172 | if (_header.e_type != 1) 173 | throw new InvalidDataException("Only relocatable objects are supported"); 174 | if (_header.e_machine != 0x14) 175 | throw new InvalidDataException("Only PowerPC is supported"); 176 | 177 | 178 | input.Seek(_header.e_shoff, SeekOrigin.Begin); 179 | for (int i = 0; i < _header.e_shnum; i++) 180 | { 181 | _sections.Add(ElfSection.Read(reader)); 182 | } 183 | 184 | if (_header.e_shstrndx > 0 && _header.e_shstrndx < _sections.Count) 185 | { 186 | var table = _sections[_header.e_shstrndx].data; 187 | 188 | for (int i = 0; i < _sections.Count; i++) 189 | { 190 | _sections[i].name = Util.ExtractNullTerminatedString(table, (int)_sections[i].sh_name); 191 | } 192 | } 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /Kamek/Hooks/BranchHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Kamek.Hooks 9 | { 10 | class BranchHook : Hook 11 | { 12 | public BranchHook(bool isLink, Word[] args, AddressMapper mapper) 13 | { 14 | if (args.Length != 2) 15 | throw new InvalidDataException("wrong arg count for BranchCommand"); 16 | 17 | // expected args: 18 | // source : pointer to game code 19 | // dest : pointer to game code or to Kamek code 20 | var source = GetAbsoluteArg(args[0], mapper); 21 | var dest = GetAnyPointerArg(args[1], mapper); 22 | 23 | Commands.Add(new Commands.BranchCommand(source, dest, isLink)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Kamek/Hooks/Hook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Kamek.Hooks 9 | { 10 | abstract class Hook 11 | { 12 | public static Hook Create(Linker.HookData data, AddressMapper mapper) 13 | { 14 | switch (data.type) 15 | { 16 | case 1: 17 | return new WriteHook(false, data.args, mapper); 18 | case 2: 19 | return new WriteHook(true, data.args, mapper); 20 | case 3: 21 | return new BranchHook(false, data.args, mapper); 22 | case 4: 23 | return new BranchHook(true, data.args, mapper); 24 | case 5: 25 | return new PatchExitHook(data.args, mapper); 26 | default: 27 | throw new NotImplementedException("unknown command type"); 28 | } 29 | } 30 | 31 | 32 | public readonly List Commands = new List(); 33 | 34 | protected Word GetValueArg(Word word) 35 | { 36 | // _MUST_ be a value 37 | if (word.Type != WordType.Value) 38 | throw new InvalidDataException(string.Format("hook {0} requested a value argument, but got {1}", this, word)); 39 | 40 | return word; 41 | } 42 | 43 | protected Word GetAbsoluteArg(Word word, AddressMapper mapper) 44 | { 45 | if (word.Type != WordType.AbsoluteAddr) 46 | { 47 | if (word.Type == WordType.Value) 48 | return new Word(WordType.AbsoluteAddr, mapper.Remap(word.Value)); 49 | else 50 | throw new InvalidDataException(string.Format("hook {0} requested an absolute address argument, but got {1}", this, word)); 51 | } 52 | 53 | return word; 54 | } 55 | 56 | protected Word GetAnyPointerArg(Word word, AddressMapper mapper) 57 | { 58 | switch (word.Type) 59 | { 60 | case WordType.Value: 61 | return new Word(WordType.AbsoluteAddr, mapper.Remap(word.Value)); 62 | case WordType.AbsoluteAddr: 63 | case WordType.RelativeAddr: 64 | return word; 65 | default: 66 | throw new NotImplementedException(); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Kamek/Hooks/PatchExitHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Kamek.Hooks 9 | { 10 | class PatchExitHook : Hook 11 | { 12 | public PatchExitHook(Word[] args, AddressMapper mapper) 13 | { 14 | if (args.Length != 2) 15 | throw new InvalidDataException("PatchExitCommand requires two arguments"); 16 | 17 | var function = args[0]; 18 | var dest = GetAnyPointerArg(args[1], mapper); 19 | 20 | if (!args[1].IsValue || args[1].Value != 0) 21 | { 22 | Commands.Add(new Commands.PatchExitCommand(function, dest)); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Kamek/Hooks/WriteHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Kamek.Commands; 8 | 9 | namespace Kamek.Hooks 10 | { 11 | class WriteHook : Hook 12 | { 13 | public WriteHook(bool isConditional, Word[] args, AddressMapper mapper) 14 | { 15 | if (args.Length != (isConditional ? 4 : 3)) 16 | throw new InvalidDataException("wrong arg count for WriteCommand"); 17 | 18 | // expected args: 19 | // address : pointer to game code 20 | // value : value, OR pointer to game code or to Kamek code 21 | // original : value, OR pointer to game code or to Kamek code 22 | var type = (WriteCommand.Type)GetValueArg(args[0]).Value; 23 | Word address, value; 24 | Word? original = null; 25 | 26 | address = GetAbsoluteArg(args[1], mapper); 27 | if (type == WriteCommand.Type.Pointer) 28 | { 29 | value = GetAnyPointerArg(args[2], mapper); 30 | if (isConditional) 31 | original = GetAnyPointerArg(args[3], mapper); 32 | } 33 | else 34 | { 35 | value = GetValueArg(args[2]); 36 | if (isConditional) 37 | original = GetValueArg(args[3]); 38 | } 39 | 40 | Commands.Add(new WriteCommand(address, value, type, original)); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Kamek/Kamek.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net6.0 5 | false 6 | true 7 | 8 | -------------------------------------------------------------------------------- /Kamek/Kamek.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kamek", "Kamek.csproj", "{57BCB38A-0135-4AC6-AD0B-3191A2804DC4}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {57BCB38A-0135-4AC6-AD0B-3191A2804DC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {57BCB38A-0135-4AC6-AD0B-3191A2804DC4}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {57BCB38A-0135-4AC6-AD0B-3191A2804DC4}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {57BCB38A-0135-4AC6-AD0B-3191A2804DC4}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /Kamek/KamekFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Kamek 9 | { 10 | class KamekFile 11 | { 12 | public static byte[] PackFrom(Linker linker) 13 | { 14 | var kf = new KamekFile(); 15 | kf.LoadFromLinker(linker); 16 | return kf.Pack(); 17 | } 18 | 19 | 20 | 21 | private Word _baseAddress; 22 | private byte[] _codeBlob; 23 | private long _bssSize; 24 | private long _ctorStart; 25 | private long _ctorEnd; 26 | 27 | public Word BaseAddress { get { return _baseAddress; } } 28 | public byte[] CodeBlob { get { return _codeBlob; } } 29 | 30 | #region Result Binary Manipulation 31 | public ushort ReadUInt16(Word addr) 32 | { 33 | return Util.ExtractUInt16(_codeBlob, addr - _baseAddress); 34 | } 35 | public uint ReadUInt32(Word addr) 36 | { 37 | return Util.ExtractUInt32(_codeBlob, addr - _baseAddress); 38 | } 39 | public void WriteUInt16(Word addr, ushort value) 40 | { 41 | Util.InjectUInt16(_codeBlob, addr - _baseAddress, value); 42 | } 43 | public void WriteUInt32(Word addr, uint value) 44 | { 45 | Util.InjectUInt32(_codeBlob, addr - _baseAddress, value); 46 | } 47 | 48 | public bool Contains(Word addr) 49 | { 50 | return (addr >= _baseAddress && addr < (_baseAddress + _codeBlob.Length)); 51 | } 52 | 53 | public uint QuerySymbolSize(Word addr) 54 | { 55 | return _symbolSizes[addr]; 56 | } 57 | #endregion 58 | 59 | private Dictionary _commands; 60 | private List _hooks; 61 | private Dictionary _symbolSizes; 62 | private AddressMapper _mapper; 63 | 64 | public void LoadFromLinker(Linker linker) 65 | { 66 | if (_codeBlob != null) 67 | throw new InvalidOperationException("this KamekFile already has stuff in it"); 68 | 69 | _mapper = linker.Mapper; 70 | 71 | // Extract _just_ the code/data sections 72 | _codeBlob = new byte[linker.OutputEnd - linker.OutputStart]; 73 | Array.Copy(linker.Memory, linker.OutputStart - linker.BaseAddress, _codeBlob, 0, _codeBlob.Length); 74 | 75 | _baseAddress = linker.BaseAddress; 76 | _bssSize = linker.BssSize; 77 | _ctorStart = linker.CtorStart - linker.OutputStart; 78 | _ctorEnd = linker.CtorEnd - linker.OutputStart; 79 | 80 | _hooks = new List(); 81 | _commands = new Dictionary(); 82 | 83 | _symbolSizes = new Dictionary(); 84 | foreach (var pair in linker.SymbolSizes) 85 | _symbolSizes.Add(pair.Key, pair.Value); 86 | 87 | AddRelocsAsCommands(linker.Fixups); 88 | 89 | foreach (var cmd in linker.Hooks) 90 | ApplyHook(cmd); 91 | ApplyStaticCommands(); 92 | } 93 | 94 | 95 | private void AddRelocsAsCommands(IReadOnlyList relocs) 96 | { 97 | foreach (var rel in relocs) 98 | { 99 | if (_commands.ContainsKey(rel.source)) 100 | throw new InvalidOperationException(string.Format("duplicate commands for address {0}", rel.source)); 101 | Commands.Command cmd = new Commands.RelocCommand(rel.source, rel.dest, rel.type); 102 | cmd.CalculateAddress(this); 103 | cmd.AssertAddressNonNull(); 104 | _commands[rel.source] = cmd; 105 | } 106 | } 107 | 108 | 109 | private void ApplyHook(Linker.HookData hookData) 110 | { 111 | var hook = Hooks.Hook.Create(hookData, _mapper); 112 | foreach (var cmd in hook.Commands) 113 | { 114 | cmd.CalculateAddress(this); 115 | cmd.AssertAddressNonNull(); 116 | if (_commands.ContainsKey(cmd.Address.Value)) 117 | throw new InvalidOperationException(string.Format("duplicate commands for address {0}", cmd.Address.Value)); 118 | _commands[cmd.Address.Value] = cmd; 119 | } 120 | _hooks.Add(hook); 121 | } 122 | 123 | 124 | private void ApplyStaticCommands() 125 | { 126 | // leave _commands containing just the ones we couldn't apply here 127 | var original = _commands; 128 | _commands = new Dictionary(); 129 | 130 | foreach (var cmd in original.Values) 131 | { 132 | if (!cmd.Apply(this)) { 133 | cmd.AssertAddressNonNull(); 134 | _commands[cmd.Address.Value] = cmd; 135 | } 136 | } 137 | } 138 | 139 | 140 | 141 | public byte[] Pack() 142 | { 143 | using (var ms = new MemoryStream()) 144 | { 145 | using (var bw = new BinaryWriter(ms)) 146 | { 147 | bw.WriteBE((uint)0x4B616D65); // 'Kamek\0\0\2' 148 | bw.WriteBE((uint)0x6B000002); 149 | bw.WriteBE((uint)_bssSize); 150 | bw.WriteBE((uint)_codeBlob.Length); 151 | bw.WriteBE((uint)_ctorStart); 152 | bw.WriteBE((uint)_ctorEnd); 153 | bw.WriteBE((uint)0); 154 | bw.WriteBE((uint)0); 155 | 156 | bw.Write(_codeBlob); 157 | 158 | foreach (var pair in _commands) 159 | { 160 | pair.Value.AssertAddressNonNull(); 161 | uint cmdID = (uint)pair.Value.Id << 24; 162 | if (pair.Value.Address.Value.IsRelative) 163 | { 164 | if (pair.Value.Address.Value.Value > 0xFFFFFF) 165 | throw new InvalidOperationException("Address too high for packed command"); 166 | 167 | cmdID |= pair.Value.Address.Value.Value; 168 | bw.WriteBE(cmdID); 169 | } 170 | else 171 | { 172 | cmdID |= 0xFFFFFE; 173 | bw.WriteBE(cmdID); 174 | bw.WriteBE(pair.Value.Address.Value.Value); 175 | } 176 | pair.Value.WriteArguments(bw); 177 | } 178 | } 179 | 180 | return ms.ToArray(); 181 | } 182 | } 183 | 184 | public string PackRiivolution(string template, string valuefilePath) 185 | { 186 | if (_baseAddress.Type == WordType.RelativeAddr) 187 | throw new InvalidOperationException("cannot pack a dynamically linked binary as a Riivolution patch"); 188 | 189 | var elements = new List(); 190 | 191 | if (_codeBlob.Length > 0) 192 | { 193 | // add the big patch 194 | if (valuefilePath != null) 195 | elements.Add(string.Format("", _baseAddress.Value, valuefilePath)); 196 | 197 | else 198 | { 199 | var sb = new StringBuilder(_codeBlob.Length * 2); 200 | for (int i = 0; i < _codeBlob.Length; i++) 201 | sb.AppendFormat("{0:X2}", _codeBlob[i]); 202 | 203 | elements.Add(string.Format("", _baseAddress.Value, sb.ToString())); 204 | } 205 | } 206 | 207 | // add individual patches 208 | foreach (var pair in _commands) 209 | elements.Add(pair.Value.PackForRiivolution()); 210 | 211 | return InjectLinesIntoTemplate(template, elements.ToArray(), "Riivolution XML"); 212 | } 213 | 214 | public string PackDolphin(string template) 215 | { 216 | if (_baseAddress.Type == WordType.RelativeAddr) 217 | throw new InvalidOperationException("cannot pack a dynamically linked binary as a Dolphin patch"); 218 | 219 | var elements = new List(); 220 | 221 | // add the big patch 222 | int i = 0; 223 | while (i < _codeBlob.Length) 224 | { 225 | var sb = new StringBuilder(27); 226 | sb.AppendFormat("0x{0:X8}:", _baseAddress.Value + i); 227 | 228 | int lineLength; 229 | switch (_codeBlob.Length - i) 230 | { 231 | case 1: 232 | lineLength = 1; 233 | sb.Append("byte:0x000000"); 234 | break; 235 | case 2: 236 | case 3: 237 | lineLength = 2; 238 | sb.Append("word:0x0000"); 239 | break; 240 | default: 241 | lineLength = 4; 242 | sb.Append("dword:0x"); 243 | break; 244 | } 245 | 246 | for (int j = 0; j < lineLength; j++, i++) 247 | sb.AppendFormat("{0:X2}", _codeBlob[i]); 248 | 249 | elements.Add(sb.ToString()); 250 | } 251 | 252 | // add individual patches 253 | foreach (var pair in _commands) 254 | elements.Add(pair.Value.PackForDolphin()); 255 | 256 | return InjectLinesIntoTemplate(template, elements.ToArray(), "Dolphin INI"); 257 | } 258 | 259 | public string PackGeckoCodes() 260 | { 261 | if (_baseAddress.Type == WordType.RelativeAddr) 262 | throw new InvalidOperationException("cannot pack a dynamically linked binary as a Gecko code"); 263 | 264 | var codes = new List(); 265 | 266 | if (_codeBlob.Length > 0) 267 | { 268 | // add the big patch 269 | long paddingSize = 0; 270 | if ((_codeBlob.Length % 8) != 0) 271 | paddingSize = 8 - (_codeBlob.Length % 8); 272 | 273 | ulong header = 0x06000000UL << 32; 274 | header |= (ulong)(_baseAddress.Value & 0x1FFFFFF) << 32; 275 | header |= (ulong)(_codeBlob.Length + paddingSize) & 0xFFFFFFFF; 276 | codes.Add(header); 277 | 278 | for (int i = 0; i < _codeBlob.Length; i += 8) 279 | { 280 | ulong bits = 0; 281 | if (i < _codeBlob.Length) bits |= (ulong)_codeBlob[i] << 56; 282 | if ((i + 1) < _codeBlob.Length) bits |= (ulong)_codeBlob[i + 1] << 48; 283 | if ((i + 2) < _codeBlob.Length) bits |= (ulong)_codeBlob[i + 2] << 40; 284 | if ((i + 3) < _codeBlob.Length) bits |= (ulong)_codeBlob[i + 3] << 32; 285 | if ((i + 4) < _codeBlob.Length) bits |= (ulong)_codeBlob[i + 4] << 24; 286 | if ((i + 5) < _codeBlob.Length) bits |= (ulong)_codeBlob[i + 5] << 16; 287 | if ((i + 6) < _codeBlob.Length) bits |= (ulong)_codeBlob[i + 6] << 8; 288 | if ((i + 7) < _codeBlob.Length) bits |= (ulong)_codeBlob[i + 7]; 289 | codes.Add(bits); 290 | } 291 | } 292 | 293 | // add individual patches 294 | foreach (var pair in _commands) 295 | codes.AddRange(pair.Value.PackGeckoCodes()); 296 | 297 | // convert everything 298 | var elements = new string[codes.Count]; 299 | for (int i = 0; i < codes.Count; i++) 300 | elements[i] = string.Format("{0:X8} {1:X8}", codes[i] >> 32, codes[i] & 0xFFFFFFFF); 301 | 302 | return string.Join("\n", elements); 303 | } 304 | 305 | public string PackActionReplayCodes() 306 | { 307 | if (_baseAddress.Type == WordType.RelativeAddr) 308 | throw new InvalidOperationException("cannot pack a dynamically linked binary as an Action Replay code"); 309 | 310 | var codes = new List(); 311 | 312 | // add the big patch 313 | for (int i = 0; i < _codeBlob.Length; i += 4) 314 | { 315 | ulong bits = 0x04000000UL << 32; 316 | bits |= (ulong)((_baseAddress.Value + i) & 0x1FFFFFF) << 32; 317 | if (i < _codeBlob.Length) bits |= (ulong)_codeBlob[i] << 24; 318 | if ((i + 1) < _codeBlob.Length) bits |= (ulong)_codeBlob[i + 1] << 16; 319 | if ((i + 2) < _codeBlob.Length) bits |= (ulong)_codeBlob[i + 2] << 8; 320 | if ((i + 3) < _codeBlob.Length) bits |= (ulong)_codeBlob[i + 3]; 321 | codes.Add(bits); 322 | } 323 | 324 | // add individual patches 325 | foreach (var pair in _commands) 326 | codes.AddRange(pair.Value.PackActionReplayCodes()); 327 | 328 | // convert everything 329 | var elements = new string[codes.Count]; 330 | for (int i = 0; i < codes.Count; i++) 331 | elements[i] = string.Format("{0:X8} {1:X8}", codes[i] >> 32, codes[i] & 0xFFFFFFFF); 332 | 333 | return string.Join("\n", elements); 334 | } 335 | 336 | public void InjectIntoDol(CodeFiles.Dol dol) 337 | { 338 | if (_baseAddress.Type == WordType.RelativeAddr) 339 | throw new InvalidOperationException("cannot pack a dynamically linked binary into a DOL"); 340 | 341 | if (_codeBlob.Length > 0) 342 | { 343 | // find an empty text section 344 | int victimSection = -1; 345 | for (int i = dol.Sections.Length - 1; i >= 0; i--) 346 | { 347 | if (dol.Sections[i].Data.Length == 0) 348 | { 349 | victimSection = i; 350 | break; 351 | } 352 | } 353 | 354 | if (victimSection == -1) 355 | throw new InvalidOperationException("cannot find an empty text section in the DOL"); 356 | 357 | // throw the code blob into it 358 | dol.Sections[victimSection].LoadAddress = _baseAddress.Value; 359 | dol.Sections[victimSection].Data = _codeBlob; 360 | } 361 | 362 | // apply all patches 363 | foreach (var pair in _commands) 364 | pair.Value.ApplyToCodeFile(dol); 365 | } 366 | 367 | public void InjectIntoAlf(CodeFiles.Alf alf) 368 | { 369 | if (_baseAddress.Type == WordType.RelativeAddr) 370 | throw new InvalidOperationException("cannot pack a dynamically linked binary into an ALF"); 371 | 372 | if (_codeBlob.Length > 0) 373 | { 374 | CodeFiles.Alf.Section codeSection = new CodeFiles.Alf.Section(); 375 | codeSection.LoadAddress = _baseAddress.Value; 376 | codeSection.Size = (uint)_codeBlob.Length; 377 | codeSection.Data = _codeBlob; 378 | codeSection.Symbols = new List(); 379 | alf.Sections.Add(codeSection); 380 | } 381 | 382 | if (_bssSize > 0) 383 | { 384 | CodeFiles.Alf.Section bssSection = new CodeFiles.Alf.Section(); 385 | bssSection.LoadAddress = (uint)(_baseAddress.Value + _codeBlob.Length); 386 | bssSection.Size = (uint)_bssSize; 387 | bssSection.Data = new byte[0]; 388 | bssSection.Symbols = new List(); 389 | alf.Sections.Add(bssSection); 390 | } 391 | 392 | // apply all patches 393 | foreach (var pair in _commands) 394 | pair.Value.ApplyToCodeFile(alf); 395 | } 396 | 397 | private static string InjectLinesIntoTemplate(string template, string[] lines, string formatName) 398 | { 399 | if (template == null) 400 | { 401 | return string.Join("\n", lines); 402 | } 403 | else 404 | { 405 | int placeholderIndex = template.IndexOf("$KF$"); 406 | if (placeholderIndex == -1) 407 | throw new InvalidDataException(string.Format("\"$KF$\" placeholder not found in {0} template", formatName)); 408 | if (template.IndexOf("$KF$", placeholderIndex + 1) != -1) 409 | throw new InvalidDataException(string.Format("multiple \"$KF$\" placeholders found in {0} template", formatName)); 410 | 411 | // If the line containing $KF$ has only whitespace before it, 412 | // we can use that as indentation for all of our new lines. 413 | // Otherwise, be conservative and don't do that. 414 | 415 | int placeholderLineStartIndex = template.LastIndexOf('\n', placeholderIndex) + 1; 416 | string placeholderLineStart = template.Substring( 417 | placeholderLineStartIndex, 418 | placeholderIndex - placeholderLineStartIndex); 419 | 420 | var joinString = "\n"; 421 | if (placeholderLineStart.All(char.IsWhiteSpace)) 422 | { 423 | joinString = "\n" + placeholderLineStart; 424 | } 425 | 426 | return template.Replace("$KF$", string.Join(joinString, lines)); 427 | } 428 | } 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /Kamek/Linker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace Kamek 7 | { 8 | class Linker 9 | { 10 | private bool _linked = false; 11 | private List _modules = new List(); 12 | public readonly AddressMapper Mapper; 13 | 14 | public Linker(AddressMapper mapper) 15 | { 16 | Mapper = mapper; 17 | } 18 | 19 | public void AddModule(Elf elf) 20 | { 21 | if (_linked) 22 | throw new InvalidOperationException("This linker has already been linked"); 23 | if (_modules.Contains(elf)) 24 | throw new InvalidOperationException("This module is already part of this linker"); 25 | 26 | _modules.Add(elf); 27 | } 28 | 29 | public void LinkStatic(uint baseAddress, Dictionary externalSymbols) 30 | { 31 | _baseAddress = new Word(WordType.AbsoluteAddr, Mapper.Remap(baseAddress)); 32 | DoLink(externalSymbols); 33 | } 34 | public void LinkDynamic(Dictionary externalSymbols) 35 | { 36 | _baseAddress = new Word(WordType.RelativeAddr, 0); 37 | DoLink(externalSymbols); 38 | } 39 | 40 | private void DoLink(Dictionary externalSymbols) 41 | { 42 | if (_linked) 43 | throw new InvalidOperationException("This linker has already been linked"); 44 | _linked = true; 45 | 46 | _externalSymbols = new Dictionary(); 47 | foreach (var pair in externalSymbols) 48 | _externalSymbols.Add(pair.Key, Mapper.Remap(pair.Value)); 49 | 50 | CollectSections(); 51 | BuildSymbolTables(); 52 | ProcessRelocations(); 53 | ProcessHooks(); 54 | } 55 | 56 | private Word _baseAddress; 57 | private Word _initStart, _initEnd; 58 | private Word _textStart, _textEnd; 59 | private Word _ctorStart, _ctorEnd; 60 | private Word _dtorStart, _dtorEnd; 61 | private Word _rodataStart, _rodataEnd; 62 | private Word _dataStart, _dataEnd; 63 | private Word _outputStart, _outputEnd; 64 | private Word _bssStart, _bssEnd; 65 | private Word _kamekStart, _kamekEnd; 66 | private byte[] _memory = null; 67 | 68 | public Word BaseAddress { get { return _baseAddress; } } 69 | public Word CtorStart { get { return _ctorStart; } } 70 | public Word CtorEnd { get { return _ctorEnd; } } 71 | public Word OutputStart { get { return _outputStart; } } 72 | public Word OutputEnd { get { return _outputEnd; } } 73 | public byte[] Memory { get { return _memory; } } 74 | public long BssSize { get { return _bssEnd - _bssStart; } } 75 | 76 | 77 | #region Collecting Sections 78 | private List _binaryBlobs = new List(); 79 | private Dictionary _sectionBases = new Dictionary(); 80 | 81 | private Word _location; 82 | 83 | private void ImportSections(string prefix) 84 | { 85 | foreach (var elf in _modules) 86 | { 87 | foreach (var s in (from s in elf.Sections 88 | where s.name.StartsWith(prefix) 89 | select s)) 90 | { 91 | if (s.data != null) 92 | _binaryBlobs.Add(s.data); 93 | else 94 | _binaryBlobs.Add(new byte[s.sh_size]); 95 | 96 | _sectionBases[s] = _location; 97 | _location += s.sh_size; 98 | 99 | // Align to 4 bytes 100 | if ((_location.Value % 4) != 0) 101 | { 102 | long alignment = 4 - (_location.Value % 4); 103 | _binaryBlobs.Add(new byte[alignment]); 104 | _location += alignment; 105 | } 106 | } 107 | } 108 | } 109 | 110 | private void CollectSections() 111 | { 112 | _location = _baseAddress; 113 | _outputStart = _location; 114 | 115 | _initStart = _location; 116 | ImportSections(".init"); 117 | _initEnd = _location; 118 | 119 | ImportSections(".fini"); 120 | 121 | _textStart = _location; 122 | ImportSections(".text"); 123 | _textEnd = _location; 124 | 125 | _ctorStart = _location; 126 | ImportSections(".ctors"); 127 | _ctorEnd = _location; 128 | 129 | _dtorStart = _location; 130 | ImportSections(".dtors"); 131 | _dtorEnd = _location; 132 | 133 | _rodataStart = _location; 134 | ImportSections(".rodata"); 135 | _rodataEnd = _location; 136 | 137 | _dataStart = _location; 138 | ImportSections(".data"); 139 | _dataEnd = _location; 140 | 141 | _outputEnd = _location; 142 | 143 | // TODO: maybe should align to 0x20 here? 144 | _bssStart = _location; 145 | ImportSections(".bss"); 146 | _bssEnd = _location; 147 | 148 | _kamekStart = _location; 149 | ImportSections(".kamek"); 150 | _kamekEnd = _location; 151 | 152 | // Create one big blob from this 153 | _memory = new byte[_location - _baseAddress]; 154 | int position = 0; 155 | foreach (var blob in _binaryBlobs) 156 | { 157 | Array.Copy(blob, 0, _memory, position, blob.Length); 158 | position += blob.Length; 159 | } 160 | } 161 | #endregion 162 | 163 | 164 | #region Result Binary Manipulation 165 | private ushort ReadUInt16(Word addr) 166 | { 167 | return Util.ExtractUInt16(_memory, addr - _baseAddress); 168 | } 169 | private uint ReadUInt32(Word addr) 170 | { 171 | return Util.ExtractUInt32(_memory, addr - _baseAddress); 172 | } 173 | private void WriteUInt16(Word addr, ushort value) 174 | { 175 | Util.InjectUInt16(_memory, addr - _baseAddress, value); 176 | } 177 | private void WriteUInt32(Word addr, uint value) 178 | { 179 | Util.InjectUInt32(_memory, addr - _baseAddress, value); 180 | } 181 | #endregion 182 | 183 | 184 | #region Symbol Tables 185 | private struct Symbol 186 | { 187 | public Word address; 188 | public uint size; 189 | public bool isWeak; 190 | } 191 | private struct SymbolName 192 | { 193 | public string name; 194 | public ushort shndx; 195 | } 196 | private Dictionary _globalSymbols = null; 197 | private Dictionary> _localSymbols = null; 198 | private Dictionary _symbolTableContents = null; 199 | private Dictionary _externalSymbols = null; 200 | private Dictionary _symbolSizes = null; 201 | public IReadOnlyDictionary SymbolSizes { get { return _symbolSizes; } } 202 | 203 | private void BuildSymbolTables() 204 | { 205 | _globalSymbols = new Dictionary(); 206 | _localSymbols = new Dictionary>(); 207 | _symbolTableContents = new Dictionary(); 208 | _symbolSizes = new Dictionary(); 209 | 210 | _globalSymbols["__ctor_loc"] = new Symbol { address = _ctorStart }; 211 | _globalSymbols["__ctor_end"] = new Symbol { address = _ctorEnd }; 212 | 213 | _globalSymbols["_f_init"] = new Symbol { address = _initStart }; 214 | _globalSymbols["_e_init"] = new Symbol { address = _initEnd }; 215 | 216 | _globalSymbols["_f_text"] = new Symbol { address = _textStart }; 217 | _globalSymbols["_e_text"] = new Symbol { address = _textEnd }; 218 | 219 | _globalSymbols["_f_ctors"] = new Symbol { address = _ctorStart }; 220 | _globalSymbols["_e_ctors"] = new Symbol { address = _ctorEnd }; 221 | 222 | _globalSymbols["_f_dtors"] = new Symbol { address = _dtorStart }; 223 | _globalSymbols["_e_dtors"] = new Symbol { address = _dtorEnd }; 224 | 225 | _globalSymbols["_f_rodata"] = new Symbol { address = _rodataStart }; 226 | _globalSymbols["_e_rodata"] = new Symbol { address = _rodataEnd }; 227 | 228 | _globalSymbols["_f_data"] = new Symbol { address = _dataStart }; 229 | _globalSymbols["_e_data"] = new Symbol { address = _dataEnd }; 230 | 231 | foreach (Elf elf in _modules) 232 | { 233 | var locals = new Dictionary(); 234 | _localSymbols[elf] = locals; 235 | 236 | foreach (var s in (from s in elf.Sections 237 | where s.sh_type == Elf.ElfSection.Type.SHT_SYMTAB 238 | select s)) 239 | { 240 | // we must have a string table 241 | uint strTabIdx = s.sh_link; 242 | if (strTabIdx <= 0 || strTabIdx >= elf.Sections.Count) 243 | throw new InvalidDataException("Symbol table is not linked to a string table"); 244 | 245 | var strtab = elf.Sections[(int)strTabIdx]; 246 | 247 | _symbolTableContents[s] = ParseSymbolTable(elf, s, strtab, locals); 248 | } 249 | } 250 | } 251 | 252 | private SymbolName[] ParseSymbolTable(Elf elf, Elf.ElfSection symtab, Elf.ElfSection strtab, Dictionary locals) 253 | { 254 | if (symtab.sh_entsize != 16) 255 | throw new InvalidDataException("Invalid symbol table format (sh_entsize != 16)"); 256 | if (strtab.sh_type != Elf.ElfSection.Type.SHT_STRTAB) 257 | throw new InvalidDataException("String table does not have type SHT_STRTAB"); 258 | 259 | var symbolNames = new List(); 260 | var reader = new BinaryReader(new MemoryStream(symtab.data)); 261 | int count = symtab.data.Length / 16; 262 | 263 | // always ignore the first symbol 264 | symbolNames.Add(new SymbolName()); 265 | reader.BaseStream.Seek(16, SeekOrigin.Begin); 266 | 267 | for (int i = 1; i < count; i++) 268 | { 269 | // Read info from the ELF 270 | uint st_name = reader.ReadBigUInt32(); 271 | uint st_value = reader.ReadBigUInt32(); 272 | uint st_size = reader.ReadBigUInt32(); 273 | byte st_info = reader.ReadByte(); 274 | byte st_other = reader.ReadByte(); 275 | ushort st_shndx = reader.ReadBigUInt16(); 276 | 277 | Elf.SymBind bind = (Elf.SymBind)(st_info >> 4); 278 | Elf.SymType type = (Elf.SymType)(st_info & 0xF); 279 | 280 | string name = Util.ExtractNullTerminatedString(strtab.data, (int)st_name); 281 | 282 | symbolNames.Add(new SymbolName { name = name, shndx = st_shndx }); 283 | if (name.Length == 0 || st_shndx == 0) 284 | continue; 285 | 286 | Word addr; 287 | if (st_shndx == 0xFFF1) 288 | { 289 | // Absolute symbol 290 | addr = new Word(WordType.AbsoluteAddr, st_value); 291 | } 292 | else if (st_shndx < 0xFF00) 293 | { 294 | // Part of a section 295 | var section = elf.Sections[st_shndx]; 296 | if (!_sectionBases.ContainsKey(section)) 297 | continue; // skips past symbols we don't care about, like DWARF junk 298 | addr = _sectionBases[section] + st_value; 299 | } 300 | else 301 | throw new NotImplementedException("unknown section index found in symbol table"); 302 | 303 | 304 | switch (bind) 305 | { 306 | case Elf.SymBind.STB_LOCAL: 307 | if (locals.ContainsKey(name)) 308 | throw new InvalidDataException("redefinition of local symbol " + name); 309 | locals[name] = new Symbol { address = addr, size = st_size }; 310 | _symbolSizes[addr] = st_size; 311 | break; 312 | 313 | case Elf.SymBind.STB_GLOBAL: 314 | if (_globalSymbols.ContainsKey(name) && !_globalSymbols[name].isWeak) 315 | throw new InvalidDataException("redefinition of global symbol " + name); 316 | _globalSymbols[name] = new Symbol { address = addr, size = st_size }; 317 | _symbolSizes[addr] = st_size; 318 | break; 319 | 320 | case Elf.SymBind.STB_WEAK: 321 | if (!_globalSymbols.ContainsKey(name)) 322 | { 323 | _globalSymbols[name] = new Symbol { address = addr, size = st_size, isWeak = true }; 324 | _symbolSizes[addr] = st_size; 325 | } 326 | break; 327 | } 328 | } 329 | 330 | return symbolNames.ToArray(); 331 | } 332 | 333 | 334 | Symbol ResolveSymbol(Elf elf, string name) 335 | { 336 | var locals = _localSymbols[elf]; 337 | if (locals.ContainsKey(name)) 338 | return locals[name]; 339 | if (_globalSymbols.ContainsKey(name)) 340 | return _globalSymbols[name]; 341 | if (_externalSymbols.ContainsKey(name)) 342 | return new Symbol { address = new Word(WordType.AbsoluteAddr, _externalSymbols[name]) }; 343 | if (name.StartsWith("__kAutoMap_")) 344 | { 345 | var addr = name.Substring(11); 346 | if (addr.StartsWith("0x") || addr.StartsWith("0X")) 347 | addr = addr.Substring(2); 348 | var parsedAddr = uint.Parse(addr, System.Globalization.NumberStyles.AllowHexSpecifier); 349 | var mappedAddr = Mapper.Remap(parsedAddr); 350 | return new Symbol { address = new Word(WordType.AbsoluteAddr, mappedAddr) }; 351 | } 352 | 353 | throw new InvalidDataException("undefined symbol " + name); 354 | } 355 | 356 | public void WriteSymbolMap(string path) 357 | { 358 | using StreamWriter file = new StreamWriter(path, false); 359 | file.WriteLine("Kamek Binary Map"); 360 | file.WriteLine(" Offset Size Name"); 361 | 362 | foreach (var s in _globalSymbols.OrderBy(x => x.Value.address.Value)) 363 | { 364 | String name = s.Key; 365 | Symbol sym = s.Value; 366 | file.WriteLine(String.Format(" {0:X8} {1:X6} {2}", sym.address.Value, sym.size, name)); 367 | } 368 | } 369 | #endregion 370 | 371 | 372 | #region Relocations 373 | public struct Fixup 374 | { 375 | public Elf.Reloc type; 376 | public Word source, dest; 377 | } 378 | private List _fixups = new List(); 379 | public IReadOnlyList Fixups { get { return _fixups; } } 380 | 381 | private void ProcessRelocations() 382 | { 383 | foreach (Elf elf in _modules) 384 | { 385 | foreach (var s in (from s in elf.Sections 386 | where s.sh_type == Elf.ElfSection.Type.SHT_REL 387 | select s)) 388 | { 389 | throw new InvalidDataException("OH SHIT"); 390 | } 391 | 392 | foreach (var s in (from s in elf.Sections 393 | where s.sh_type == Elf.ElfSection.Type.SHT_RELA 394 | select s)) 395 | { 396 | // Get the two affected sections 397 | if (s.sh_info <= 0 || s.sh_info >= elf.Sections.Count) 398 | throw new InvalidDataException("Rela table is not linked to a section"); 399 | if (s.sh_link <= 0 || s.sh_link >= elf.Sections.Count) 400 | throw new InvalidDataException("Rela table is not linked to a symbol table"); 401 | 402 | var affected = elf.Sections[(int)s.sh_info]; 403 | var symtab = elf.Sections[(int)s.sh_link]; 404 | 405 | ProcessRelaSection(elf, s, affected, symtab); 406 | } 407 | } 408 | } 409 | 410 | 411 | private void ProcessRelaSection(Elf elf, Elf.ElfSection relocs, Elf.ElfSection section, Elf.ElfSection symtab) 412 | { 413 | if (relocs.sh_entsize != 12) 414 | throw new InvalidDataException("Invalid relocs format (sh_entsize != 12)"); 415 | if (symtab.sh_type != Elf.ElfSection.Type.SHT_SYMTAB) 416 | throw new InvalidDataException("Symbol table does not have type SHT_SYMTAB"); 417 | 418 | var reader = new BinaryReader(new MemoryStream(relocs.data)); 419 | int count = relocs.data.Length / 12; 420 | 421 | for (int i = 0; i < count; i++) 422 | { 423 | uint r_offset = reader.ReadBigUInt32(); 424 | uint r_info = reader.ReadBigUInt32(); 425 | int r_addend = reader.ReadBigInt32(); 426 | 427 | Elf.Reloc reloc = (Elf.Reloc)(r_info & 0xFF); 428 | int symIndex = (int)(r_info >> 8); 429 | 430 | if (symIndex == 0) 431 | throw new InvalidDataException("linking to undefined symbol"); 432 | if (!_sectionBases.ContainsKey(section)) 433 | continue; // we don't care about this 434 | 435 | SymbolName symbol = _symbolTableContents[symtab][symIndex]; 436 | string symName = symbol.name; 437 | //Console.WriteLine("{0,-30} {1}", symName, reloc); 438 | 439 | Word source = _sectionBases[section] + r_offset; 440 | Word dest = (String.IsNullOrEmpty(symName) ? _sectionBases[elf.Sections[symbol.shndx]] : ResolveSymbol(elf, symName).address) + r_addend; 441 | 442 | //Console.WriteLine("Linking from 0x{0:X8} to 0x{1:X8}", source.Value, dest.Value); 443 | 444 | if (!KamekUseReloc(reloc, source, dest)) 445 | _fixups.Add(new Fixup { type = reloc, source = source, dest = dest }); 446 | } 447 | } 448 | #endregion 449 | 450 | 451 | #region Kamek Hooks 452 | private Dictionary _kamekRelocations = new Dictionary(); 453 | 454 | private bool KamekUseReloc(Elf.Reloc type, Word source, Word dest) 455 | { 456 | if (source < _kamekStart || source >= _kamekEnd) 457 | return false; 458 | if (type != Elf.Reloc.R_PPC_ADDR32) 459 | throw new InvalidOperationException("Unsupported relocation type in the Kamek hook data section"); 460 | 461 | _kamekRelocations[source] = dest; 462 | return true; 463 | } 464 | 465 | public struct HookData 466 | { 467 | public uint type; 468 | public Word[] args; 469 | } 470 | 471 | private List _hooks = new List(); 472 | public IList Hooks { get { return _hooks; } } 473 | 474 | 475 | private void ProcessHooks() 476 | { 477 | foreach (var elf in _modules) 478 | { 479 | foreach (var pair in _localSymbols[elf]) 480 | { 481 | if (pair.Key.StartsWith("_kHook")) 482 | { 483 | var cmdAddr = pair.Value.address; 484 | 485 | var argCount = ReadUInt32(cmdAddr); 486 | var type = ReadUInt32(cmdAddr + 4); 487 | var args = new Word[argCount]; 488 | 489 | for (int i = 0; i < argCount; i++) 490 | { 491 | var argAddr = cmdAddr + (8 + (i * 4)); 492 | if (_kamekRelocations.ContainsKey(argAddr)) 493 | args[i] = _kamekRelocations[argAddr]; 494 | else 495 | args[i] = new Word(WordType.Value, ReadUInt32(argAddr)); 496 | } 497 | 498 | _hooks.Add(new HookData { type = type, args = args }); 499 | } 500 | } 501 | } 502 | } 503 | #endregion 504 | } 505 | } 506 | -------------------------------------------------------------------------------- /Kamek/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using System.Threading.Tasks; 8 | 9 | namespace Kamek 10 | { 11 | class Program 12 | { 13 | static bool Loud = true; 14 | 15 | static void Main(string[] args) 16 | { 17 | // Check for the -q/-quiet option first, so we can know if 18 | // we should print the banner line 19 | foreach (var arg in args) 20 | { 21 | if (arg == "-quiet" || arg == "-q") 22 | { 23 | Loud = false; 24 | continue; 25 | } 26 | } 27 | 28 | if (Loud) { 29 | Console.WriteLine("Kamek 2.0 by Ninji/Ash Wolf - https://github.com/Treeki/Kamek"); 30 | Console.WriteLine(); 31 | } 32 | 33 | // Parse the command line arguments and do cool things! 34 | var modules = new List(); 35 | uint? baseAddress = null; 36 | string outputKamekPath = null, inputRiivPath = null, outputRiivPath = null, inputDolphinPath = null, outputDolphinPath = null, outputGeckoPath = null, outputARPath = null, outputCodePath = null; 37 | string inputDolPath = null, outputDolPath = null; 38 | string inputAlfPath = null, outputAlfPath = null; 39 | string outputMapPath = null; 40 | var externals = new Dictionary(); 41 | VersionInfo versions = null; 42 | var selectedVersions = new List(); 43 | string valuefilePath = null; 44 | 45 | foreach (var arg in args) 46 | { 47 | if (arg.StartsWith("-")) 48 | { 49 | if (arg == "-h" || arg == "-help" || arg == "--help") 50 | { 51 | ShowHelp(); 52 | return; 53 | } 54 | if (arg == "-dynamic") 55 | baseAddress = null; 56 | else if (arg.StartsWith("-static=0x")) 57 | baseAddress = uint.Parse(arg.Substring(10), System.Globalization.NumberStyles.HexNumber); 58 | else if (arg.StartsWith("-output-kamek=")) 59 | outputKamekPath = arg.Substring(14); 60 | else if (arg.StartsWith("-input-riiv=")) 61 | inputRiivPath = arg.Substring(12); 62 | else if (arg.StartsWith("-output-riiv=")) 63 | outputRiivPath = arg.Substring(13); 64 | else if (arg.StartsWith("-input-dolphin=")) 65 | inputDolphinPath = arg.Substring(15); 66 | else if (arg.StartsWith("-output-dolphin=")) 67 | outputDolphinPath = arg.Substring(16); 68 | else if (arg.StartsWith("-output-gecko=")) 69 | outputGeckoPath = arg.Substring(14); 70 | else if (arg.StartsWith("-output-ar=")) 71 | outputARPath = arg.Substring(11); 72 | else if (arg.StartsWith("-output-code=")) 73 | outputCodePath = arg.Substring(13); 74 | else if (arg.StartsWith("-input-dol=")) 75 | inputDolPath = arg.Substring(11); 76 | else if (arg.StartsWith("-output-dol=")) 77 | outputDolPath = arg.Substring(12); 78 | else if (arg.StartsWith("-input-alf=")) 79 | inputAlfPath = arg.Substring(11); 80 | else if (arg.StartsWith("-output-alf=")) 81 | outputAlfPath = arg.Substring(12); 82 | else if (arg.StartsWith("-output-map=")) 83 | outputMapPath = arg.Substring(12); 84 | else if (arg.StartsWith("-externals=")) 85 | ReadExternals(externals, arg.Substring(11)); 86 | else if (arg.StartsWith("-versions=")) 87 | versions = new VersionInfo(arg.Substring(10)); 88 | else if (arg.StartsWith("-select-version=")) 89 | selectedVersions.Add(arg.Substring(16)); 90 | else if (arg.StartsWith("-valuefile=")) 91 | valuefilePath = arg.Substring(11); 92 | #pragma warning disable 642 93 | else if (arg == "-quiet" || arg == "-q") 94 | ; // already handled separately, earlier 95 | #pragma warning restore 642 96 | else 97 | Console.WriteLine("warning: unrecognised argument: {0}", arg); 98 | } 99 | else 100 | { 101 | if (Loud) 102 | Console.WriteLine("adding {0} as object..", arg); 103 | using (var stream = new FileStream(arg, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 104 | { 105 | modules.Add(new Elf(stream)); 106 | } 107 | } 108 | } 109 | 110 | 111 | // We need a default VersionList for the loop later 112 | if (versions == null) 113 | versions = new VersionInfo(); 114 | 115 | 116 | // Can we build a thing? 117 | if (modules.Count == 0) 118 | { 119 | Console.WriteLine("no input files specified"); 120 | return; 121 | } 122 | if (outputKamekPath == null && outputRiivPath == null && outputDolphinPath == null && outputGeckoPath == null && outputARPath == null && outputCodePath == null && outputDolPath == null && outputAlfPath == null) 123 | { 124 | Console.WriteLine("no output path(s) specified"); 125 | return; 126 | } 127 | if (outputDolPath != null && inputDolPath == null) 128 | { 129 | Console.WriteLine("input dol path not specified"); 130 | return; 131 | } 132 | if (outputAlfPath != null && inputAlfPath == null) 133 | { 134 | Console.WriteLine("input alf path not specified"); 135 | return; 136 | } 137 | 138 | 139 | // Do safety checks 140 | if (versions.Mappers.Count > 1 && selectedVersions.Count != 1) 141 | { 142 | bool ambiguousOutputPath = false; 143 | ambiguousOutputPath |= (outputKamekPath != null && !outputKamekPath.Contains("$KV$")); 144 | ambiguousOutputPath |= (outputRiivPath != null && !outputRiivPath.Contains("$KV$")); 145 | ambiguousOutputPath |= (outputDolphinPath != null && !outputDolphinPath.Contains("$KV$")); 146 | ambiguousOutputPath |= (outputGeckoPath != null && !outputGeckoPath.Contains("$KV$")); 147 | ambiguousOutputPath |= (outputARPath != null && !outputARPath.Contains("$KV$")); 148 | ambiguousOutputPath |= (outputCodePath != null && !outputCodePath.Contains("$KV$")); 149 | ambiguousOutputPath |= (outputDolPath != null && !outputDolPath.Contains("$KV$")); 150 | ambiguousOutputPath |= (outputAlfPath != null && !outputAlfPath.Contains("$KV$")); 151 | if (ambiguousOutputPath) 152 | { 153 | Console.WriteLine("ERROR: this configuration builds for multiple game versions, and some of the outputs will be overwritten"); 154 | Console.WriteLine("add the $KV$ placeholder to your output paths, or use -select-version=.. to only build one version"); 155 | return; 156 | } 157 | } 158 | 159 | if (inputRiivPath != null && outputRiivPath == null) 160 | Console.WriteLine("warning: -input-riiv can only be used with -output-riiv"); 161 | 162 | if (inputDolphinPath != null && outputDolphinPath == null) 163 | Console.WriteLine("warning: -input-dolphin can only be used with -output-dolphin"); 164 | 165 | if (valuefilePath != null && outputRiivPath == null) 166 | Console.WriteLine("warning: -valuefile can only be used with -output-riiv"); 167 | 168 | 169 | foreach (var version in versions.Mappers) 170 | { 171 | if (selectedVersions.Count > 0 && !selectedVersions.Contains(version.Key)) 172 | { 173 | if (Loud) 174 | Console.WriteLine("(skipping version {0} as it's not selected)", version.Key); 175 | continue; 176 | } 177 | if (Loud) 178 | Console.WriteLine("linking version {0}...", version.Key); 179 | 180 | var linker = new Linker(version.Value); 181 | foreach (var module in modules) 182 | linker.AddModule(module); 183 | 184 | if (baseAddress.HasValue) 185 | linker.LinkStatic(baseAddress.Value, externals); 186 | else 187 | linker.LinkDynamic(externals); 188 | 189 | var kf = new KamekFile(); 190 | kf.LoadFromLinker(linker); 191 | if (outputKamekPath != null) 192 | File.WriteAllBytes(outputKamekPath.Replace("$KV$", version.Key), kf.Pack()); 193 | if (outputRiivPath != null) 194 | { 195 | string inputRiivData = null; 196 | if (inputRiivPath != null) 197 | inputRiivData = File.ReadAllText(inputRiivPath.Replace("$KV$", version.Key)); 198 | File.WriteAllText(outputRiivPath.Replace("$KV$", version.Key), kf.PackRiivolution(inputRiivData, valuefilePath)); 199 | } 200 | if (outputDolphinPath != null) 201 | { 202 | string inputDolphinData = null; 203 | if (inputDolphinPath != null) 204 | inputDolphinData = File.ReadAllText(inputDolphinPath.Replace("$KV$", version.Key)); 205 | File.WriteAllText(outputDolphinPath.Replace("$KV$", version.Key), kf.PackDolphin(inputDolphinData)); 206 | } 207 | if (outputGeckoPath != null) 208 | File.WriteAllText(outputGeckoPath.Replace("$KV$", version.Key), kf.PackGeckoCodes()); 209 | if (outputARPath != null) 210 | File.WriteAllText(outputARPath.Replace("$KV$", version.Key), kf.PackActionReplayCodes()); 211 | if (outputCodePath != null) 212 | File.WriteAllBytes(outputCodePath.Replace("$KV$", version.Key), kf.CodeBlob); 213 | 214 | if (outputDolPath != null) 215 | { 216 | var dol = new CodeFiles.Dol(new FileStream(inputDolPath.Replace("$KV$", version.Key), FileMode.Open, FileAccess.ReadWrite, FileShare.None)); 217 | kf.InjectIntoDol(dol); 218 | 219 | var outpath = outputDolPath.Replace("$KV$", version.Key); 220 | using (var outStream = new FileStream(outpath, FileMode.Create)) 221 | { 222 | dol.Write(outStream); 223 | } 224 | } 225 | else if (outputAlfPath != null) 226 | { 227 | var alf = new CodeFiles.Alf(new FileStream(inputAlfPath.Replace("$KV$", version.Key), FileMode.Open, FileAccess.ReadWrite, FileShare.None)); 228 | kf.InjectIntoAlf(alf); 229 | 230 | var outpath = outputAlfPath.Replace("$KV$", version.Key); 231 | using (var outStream = new FileStream(outpath, FileMode.Create)) 232 | { 233 | alf.Write(outStream); 234 | } 235 | } 236 | 237 | if (outputMapPath != null) 238 | { 239 | linker.WriteSymbolMap(outputMapPath.Replace("$KV$", version.Key)); 240 | } 241 | } 242 | } 243 | 244 | private static void ReadExternals(Dictionary dict, string path) 245 | { 246 | var commentRegex = new Regex(@"^\s*#"); 247 | var emptyLineRegex = new Regex(@"^\s*$"); 248 | var assignmentRegex = new Regex(@"^\s*([a-zA-Z0-9_<>@,-\\$]+)\s*=\s*0x([a-fA-F0-9]+)\s*(#.*)?$"); 249 | 250 | foreach (var line in File.ReadAllLines(path)) 251 | { 252 | if (emptyLineRegex.IsMatch(line)) 253 | continue; 254 | if (commentRegex.IsMatch(line)) 255 | continue; 256 | 257 | var match = assignmentRegex.Match(line); 258 | if (match.Success) 259 | { 260 | dict[match.Groups[1].Value] = uint.Parse(match.Groups[2].Value, System.Globalization.NumberStyles.HexNumber); 261 | } 262 | else 263 | { 264 | Console.WriteLine("unrecognised line in externals file: {0}", line); 265 | } 266 | } 267 | } 268 | 269 | private static void ShowHelp() 270 | { 271 | Console.WriteLine("Syntax:"); 272 | Console.WriteLine(" Kamek file1.o [file2.o...] [options]"); 273 | Console.WriteLine(); 274 | Console.WriteLine("Options:"); 275 | Console.WriteLine(" General:"); 276 | Console.WriteLine(" -quiet / -q"); 277 | Console.WriteLine(" don't print anything to stdout unless there are warnings or errors"); 278 | Console.WriteLine(); 279 | Console.WriteLine(" Build Mode (select one; defaults to -dynamic):"); 280 | Console.WriteLine(" -dynamic"); 281 | Console.WriteLine(" generate a dynamically linked Kamek binary for use with the loader"); 282 | Console.WriteLine(" -static=0x80001900"); 283 | Console.WriteLine(" generate a blob of code which must be loaded at the specified Wii RAM address"); 284 | Console.WriteLine(); 285 | Console.WriteLine(" Game Configuration:"); 286 | Console.WriteLine(" -externals=file.txt"); 287 | Console.WriteLine(" specify the addresses of external symbols that exist in the target game"); 288 | Console.WriteLine(" -versions=file.txt"); 289 | Console.WriteLine(" specify the different executable versions that Kamek can link binaries for"); 290 | Console.WriteLine(" -select-version=key"); 291 | Console.WriteLine(" build only one version from the versions file, and ignore the rest"); 292 | Console.WriteLine(" (can be specified multiple times)"); 293 | Console.WriteLine(); 294 | Console.WriteLine(" Outputs (at least one is required; $KV$ will be replaced with the version name):"); 295 | Console.WriteLine(" -output-kamek=file.$KV$.bin"); 296 | Console.WriteLine(" write a Kamek binary for use with the loader (-dynamic only)"); 297 | Console.WriteLine(" -output-riiv=file.$KV$.xml"); 298 | Console.WriteLine(" write a Riivolution XML fragment or file (-static only)"); 299 | Console.WriteLine(" -output-dolphin=file.$KV$.ini"); 300 | Console.WriteLine(" write a Dolphin INI fragment or file (-static only)"); 301 | Console.WriteLine(" -output-gecko=file.$KV$.xml"); 302 | Console.WriteLine(" write a list of Gecko codes (-static only)"); 303 | Console.WriteLine(" -output-ar=file.$KV$.xml"); 304 | Console.WriteLine(" write a list of Action Replay codes (-static only)"); 305 | Console.WriteLine(" -input-dol=file.$KV$.dol -output-dol=file2.$KV$.dol"); 306 | Console.WriteLine(" apply these patches and generate a modified DOL (-static only)"); 307 | Console.WriteLine(" -input-alf=file.$KV$.alf -output-alf=file2.$KV$.alf"); 308 | Console.WriteLine(" apply these patches and generate a modified ALF (-static only)"); 309 | Console.WriteLine(" -output-code=file.$KV$.bin"); 310 | Console.WriteLine(" write the combined code+data segment to file.bin (for manual injection or debugging)"); 311 | Console.WriteLine(" -output-map=file.$KV$.map"); 312 | Console.WriteLine(" write a list of symbols and their relative offsets (for debugging)"); 313 | Console.WriteLine(); 314 | Console.WriteLine(" Output Configuration:"); 315 | Console.WriteLine(" -input-riiv=file.$KV$.xml"); 316 | Console.WriteLine(" if -output-riiv is used, use this file as a template, where"); 317 | Console.WriteLine(" the magic string \"$KF$\" will be replaced by the new XML tags."); 318 | Console.WriteLine(" otherwise, the new XML tags will be emitted by themselves"); 319 | Console.WriteLine(" -input-dolphin=file.$KV$.ini"); 320 | Console.WriteLine(" if -output-dolphin is used, use this file as a template, where"); 321 | Console.WriteLine(" the magic string \"$KF$\" will be replaced by the new INI lines."); 322 | Console.WriteLine(" otherwise, the new INI lines will be emitted by themselves"); 323 | Console.WriteLine(" -valuefile=loader.bin"); 324 | Console.WriteLine(" if -output-riiv is used, emit a \"valuefile\" attribute containing this path string, instead of \"value\""); 325 | } 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /Kamek/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Kamek")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Kamek")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("d6d8a1f3-6863-482a-a3fa-5639623fc253")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Kamek/Util.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Kamek 9 | { 10 | static class Util 11 | { 12 | public static ushort ReadBigUInt16(this BinaryReader br) 13 | { 14 | byte a = br.ReadByte(); 15 | byte b = br.ReadByte(); 16 | return (ushort)((a << 8) | b); 17 | } 18 | 19 | public static ushort ReadLittleUInt16(this BinaryReader br) 20 | { 21 | byte a = br.ReadByte(); 22 | byte b = br.ReadByte(); 23 | return (ushort)((b << 8) | a); 24 | } 25 | 26 | public static uint ReadBigUInt32(this BinaryReader br) 27 | { 28 | ushort a = br.ReadBigUInt16(); 29 | ushort b = br.ReadBigUInt16(); 30 | return (uint)((a << 16) | b); 31 | } 32 | 33 | public static uint ReadLittleUInt32(this BinaryReader br) 34 | { 35 | ushort a = br.ReadLittleUInt16(); 36 | ushort b = br.ReadLittleUInt16(); 37 | return (uint)((b << 16) | a); 38 | } 39 | 40 | public static int ReadBigInt32(this BinaryReader br) 41 | { 42 | ushort a = br.ReadBigUInt16(); 43 | ushort b = br.ReadBigUInt16(); 44 | return (int)((a << 16) | b); 45 | } 46 | 47 | public static int ReadLittleInt32(this BinaryReader br) 48 | { 49 | ushort a = br.ReadLittleUInt16(); 50 | ushort b = br.ReadLittleUInt16(); 51 | return (int)((b << 16) | a); 52 | } 53 | 54 | public static void WriteBE(this BinaryWriter bw, ushort value) 55 | { 56 | bw.Write((byte)(value >> 8)); 57 | bw.Write((byte)(value & 0xFF)); 58 | } 59 | 60 | public static void WriteLE(this BinaryWriter bw, ushort value) 61 | { 62 | bw.Write((byte)(value & 0xFF)); 63 | bw.Write((byte)(value >> 8)); 64 | } 65 | 66 | public static void WriteBE(this BinaryWriter bw, uint value) 67 | { 68 | bw.WriteBE((ushort)(value >> 16)); 69 | bw.WriteBE((ushort)(value & 0xFFFF)); 70 | } 71 | 72 | public static void WriteLE(this BinaryWriter bw, uint value) 73 | { 74 | bw.WriteLE((ushort)(value & 0xFFFF)); 75 | bw.WriteLE((ushort)(value >> 16)); 76 | } 77 | 78 | 79 | public static ushort ExtractUInt16(byte[] array, long offset) 80 | { 81 | return (ushort)((array[offset] << 8) | array[offset + 1]); 82 | } 83 | public static uint ExtractUInt32(byte[] array, long offset) 84 | { 85 | return (uint)((array[offset] << 24) | (array[offset + 1] << 16) | 86 | (array[offset + 2] << 8) | array[offset + 3]); 87 | } 88 | public static void InjectUInt16(byte[] array, long offset, ushort value) 89 | { 90 | array[offset] = (byte)((value >> 8) & 0xFF); 91 | array[offset + 1] = (byte)(value & 0xFF); 92 | } 93 | public static void InjectUInt32(byte[] array, long offset, uint value) 94 | { 95 | array[offset] = (byte)((value >> 24) & 0xFF); 96 | array[offset + 1] = (byte)((value >> 16) & 0xFF); 97 | array[offset + 2] = (byte)((value >> 8) & 0xFF); 98 | array[offset + 3] = (byte)(value & 0xFF); 99 | } 100 | 101 | 102 | public static string ExtractNullTerminatedString(byte[] table, int offset) 103 | { 104 | if (offset >= 0 && offset < table.Length) 105 | { 106 | // find where it ends 107 | for (int i = offset; i < table.Length; i++) 108 | { 109 | if (table[i] == 0) 110 | { 111 | return Encoding.ASCII.GetString(table, offset, i - offset); 112 | } 113 | } 114 | } 115 | 116 | return null; 117 | } 118 | 119 | 120 | public static void DumpToConsole(byte[] array) 121 | { 122 | int lines = array.Length / 16; 123 | 124 | for (int offset = 0; offset < array.Length; offset += 0x10) 125 | { 126 | Console.Write("{0:X8} | ", offset); 127 | 128 | for (int pos = offset; pos < (offset + 0x10) && pos < array.Length; pos++) 129 | { 130 | Console.Write("{0:X2} ", array[pos]); 131 | } 132 | 133 | Console.Write("| "); 134 | 135 | for (int pos = offset; pos < (offset + 0x10) && pos < array.Length; pos++) 136 | { 137 | if (array[pos] >= ' ' && array[pos] <= 0x7F) 138 | Console.Write("{0}", (char)array[pos]); 139 | else 140 | Console.Write("."); 141 | } 142 | 143 | Console.WriteLine(); 144 | } 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Kamek/VersionInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text.RegularExpressions; 5 | 6 | public class VersionInfo 7 | { 8 | public VersionInfo() 9 | { 10 | _mappers["default"] = new AddressMapper(); 11 | } 12 | 13 | public VersionInfo(string path) 14 | { 15 | var commentRegex = new Regex(@"^\s*#"); 16 | var emptyLineRegex = new Regex(@"^\s*$"); 17 | var sectionRegex = new Regex(@"^\s*\[([a-zA-Z0-9_.]+)\]$"); 18 | var extendRegex = new Regex(@"^\s*extend ([a-zA-Z0-9_.]+)\s*(#.*)?$"); 19 | var mappingRegex = new Regex(@"^\s*([a-fA-F0-9]{8})-((?:[a-fA-F0-9]{8})|\*)\s*:\s*([-+])0x([a-fA-F0-9]+)\s*(#.*)?$"); 20 | String currentVersionName = null; 21 | AddressMapper currentVersion = null; 22 | 23 | foreach (var line in File.ReadAllLines(path)) 24 | { 25 | if (emptyLineRegex.IsMatch(line)) 26 | continue; 27 | if (commentRegex.IsMatch(line)) 28 | continue; 29 | 30 | var match = sectionRegex.Match(line); 31 | if (match.Success) 32 | { 33 | // New version 34 | currentVersionName = match.Groups[1].Value; 35 | if (_mappers.ContainsKey(currentVersionName)) 36 | throw new InvalidDataException(string.Format("versions file contains duplicate version name {0}", currentVersionName)); 37 | 38 | currentVersion = new AddressMapper(); 39 | _mappers[currentVersionName] = currentVersion; 40 | continue; 41 | } 42 | 43 | if (currentVersion != null) 44 | { 45 | // Try to associate something with the current version 46 | match = extendRegex.Match(line); 47 | if (match.Success) 48 | { 49 | var baseName = match.Groups[1].Value; 50 | if (!_mappers.ContainsKey(baseName)) 51 | throw new InvalidDataException(string.Format("version {0} extends unknown version {1}", currentVersionName, baseName)); 52 | if (currentVersion.Base != null) 53 | throw new InvalidDataException(string.Format("version {0} already extends a version", currentVersionName)); 54 | 55 | currentVersion.Base = _mappers[baseName]; 56 | continue; 57 | } 58 | 59 | match = mappingRegex.Match(line); 60 | if (match.Success) 61 | { 62 | uint startAddress, endAddress; 63 | int delta; 64 | 65 | startAddress = uint.Parse(match.Groups[1].Value, System.Globalization.NumberStyles.HexNumber); 66 | if (match.Groups[2].Value == "*") 67 | endAddress = 0xFFFFFFFF; 68 | else 69 | endAddress = uint.Parse(match.Groups[2].Value, System.Globalization.NumberStyles.HexNumber); 70 | 71 | delta = int.Parse(match.Groups[4].Value, System.Globalization.NumberStyles.HexNumber); 72 | if (match.Groups[3].Value == "-") 73 | delta = -delta; 74 | 75 | currentVersion.AddMapping(startAddress, endAddress, delta); 76 | continue; 77 | } 78 | } 79 | 80 | Console.WriteLine("unrecognised line in versions file: {0}", line); 81 | } 82 | } 83 | 84 | private Dictionary _mappers = new Dictionary(); 85 | public IReadOnlyDictionary Mappers { get { return _mappers; } } 86 | } 87 | -------------------------------------------------------------------------------- /Kamek/Word.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Kamek 9 | { 10 | public enum WordType 11 | { 12 | Value = 1, 13 | AbsoluteAddr = 2, 14 | RelativeAddr = 3 15 | } 16 | 17 | public struct Word 18 | { 19 | public readonly WordType Type; 20 | public readonly uint Value; 21 | 22 | public Word(WordType type, uint addr) 23 | { 24 | this.Type = type; 25 | this.Value = addr; 26 | } 27 | 28 | #region Word Arithmetic Operators 29 | public static Word operator +(Word a, long addend) 30 | { 31 | return new Word(a.Type, (uint)(a.Value + addend)); 32 | } 33 | public static long operator -(Word a, Word b) 34 | { 35 | if (a.Type != b.Type) 36 | throw new InvalidOperationException("cannot perform arithmetic on different kinds of words"); 37 | return a.Value - b.Value; 38 | } 39 | #endregion 40 | 41 | #region Word Comparison Operators 42 | public static bool operator <(Word a, Word b) 43 | { 44 | if (a.Type != b.Type) 45 | throw new InvalidOperationException("cannot compare different kinds of words"); 46 | return a.Value < b.Value; 47 | } 48 | public static bool operator >(Word a, Word b) 49 | { 50 | if (a.Type != b.Type) 51 | throw new InvalidOperationException("cannot compare different kinds of words"); 52 | return a.Value > b.Value; 53 | } 54 | public static bool operator <=(Word a, Word b) 55 | { 56 | if (a.Type != b.Type) 57 | throw new InvalidOperationException("cannot compare different kinds of words"); 58 | return a.Value <= b.Value; 59 | } 60 | public static bool operator >=(Word a, Word b) 61 | { 62 | if (a.Type != b.Type) 63 | throw new InvalidOperationException("cannot compare different kinds of words"); 64 | return a.Value >= b.Value; 65 | } 66 | #endregion 67 | 68 | #region Type Checking 69 | public bool IsAbsolute { get { return (Type == WordType.AbsoluteAddr); } } 70 | public bool IsRelative { get { return (Type == WordType.RelativeAddr); } } 71 | public bool IsValue { get { return (Type == WordType.Value); } } 72 | 73 | public void AssertAbsolute() 74 | { 75 | if (!IsAbsolute) 76 | throw new InvalidOperationException(string.Format("word {0} must be an absolute address in this context", this)); 77 | } 78 | public void AssertNotRelative() 79 | { 80 | if (IsRelative) 81 | throw new InvalidOperationException(string.Format("word {0} cannot be a relative address in this context", this)); 82 | } 83 | public void AssertValue() 84 | { 85 | if (!IsValue) 86 | throw new InvalidOperationException(string.Format("word {0} must be a value in this context", this)); 87 | } 88 | public void AssertNotAmbiguous() 89 | { 90 | // Verifies that this Address can be disambiguated between Absolute 91 | // and Relative from _just_ the top bit 92 | if (IsAbsolute && (Value & 0x80000000) == 0) 93 | throw new InvalidOperationException("address is ambiguous: absolute, top bit not set"); 94 | if (IsRelative && (Value & 0x80000000) != 0) 95 | throw new InvalidOperationException("address is ambiguous: relative, top bit set"); 96 | } 97 | #endregion 98 | 99 | public override string ToString() 100 | { 101 | switch (Type) 102 | { 103 | case WordType.AbsoluteAddr: 104 | return string.Format("", Value); 105 | case WordType.RelativeAddr: 106 | return string.Format("", Value); 107 | case WordType.Value: 108 | return string.Format("", Value); 109 | } 110 | 111 | throw new NotImplementedException(); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kamek 2 | a nice code injection engine for Wii games - (c) Treeki 2010-2021 3 | 4 | ### Introduction 5 | 6 | Kamek is an engine for injecting custom code into Wii games - it's designed for 7 | *New Super Mario Bros. Wii*, but it should work with many other games as well. 8 | 9 | (This is a fully rebuilt version of the toolchain I developed from 2010 to 2013 10 | for the [*Newer Super Mario Bros. Wii* project][newer]. It cannot build Newer 11 | in its current form, but this may be possible later.) 12 | 13 | ### TODO 14 | 15 | - Port some of the Newer hacks to this 16 | 17 | - More testing 18 | 19 | ### Requirements 20 | 21 | To build and run the Kamek linker: 22 | 23 | - .NET Framework 6.0 (installable on both Windows and non-Windows OSes) 24 | 25 | To compile code: 26 | 27 | - NXP ['CodeWarrior Special Edition'][cw] for MPC55xx/MPC56xx v2.10 28 | - If this direct link doesn't work, the original page is 29 | ['available on the Internet Archive'][cwIA]. 30 | - After installing it, you need `license.dat` from the installation root, 31 | and the DLL and EXE files inside `PowerPC_EABI_Tools/Command_Line_Tools`. 32 | - Useful documentation is in `Help/Power_Arch_Build_Tools_Reference.chm` 33 | (or `Help/PDF/Power Architecture Build Tools Reference.pdf`) 34 | 35 | Support for other compilers such as GCC and Clang is not planned. 36 | (C++ code generated by these compilers uses a different ABI to 37 | CodeWarrior, so features like classes and virtual inheritance only work 38 | properly when CW is used.) 39 | 40 | 41 | 42 | [cw]: http://cache.nxp.com/lgfiles/devsuites/PowerPC/CW55xx_v2_10_SE.exe?WT_TYPE=IDE%20-%20Debug,%20Compile%20and%20Build%20Tools&WT_VENDOR=FREESCALE&WT_FILE_FORMAT=exe&WT_ASSET=Downloads&fileExt=.exe 43 | [cwIA]: http://web.archive.org/web/20160602205749/http://www.nxp.com/products/software-and-tools/software-development-tools/codewarrior-development-tools/downloads/special-edition-software:CW_SPECIALEDITIONS 44 | [newer]: https://github.com/Treeki/NewerSMBW 45 | 46 | -------------------------------------------------------------------------------- /examples/1-nsmbw-osreport.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // NOTE: bss _must_ be init'ed to zero! 4 | // todo: modify the Riiv patcher to take this into account! 5 | // ALSO, this doesn't work on Gecko as the codehandler resets counter=1 6 | // every frame 7 | int counter = 1; 8 | 9 | bool bootTest() 10 | { 11 | if (counter >= (60 * 10)) 12 | return true; 13 | 14 | ++counter; 15 | if ((counter % 60) == 0) 16 | OSReport("Delaying for absolutely no good reason... [%d]\n", counter); 17 | return false; 18 | } 19 | 20 | //8015BC60 is a test for BRANCHES! 21 | //kmWritePointer(0x8015BC60, &bootTest); 22 | kmWritePointer(0x80328478, &bootTest); 23 | 24 | // Hijack the end of dAcPy_c's constructor 25 | 26 | kmBranchDefCpp(0x801447D4, NULL, void *, void *obj) 27 | { 28 | OSReport("dAcPy_c constructed at %p!\n", obj); 29 | return obj; 30 | } 31 | -------------------------------------------------------------------------------- /examples/2-assembly.S: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Line Lift patch from Newer 4 | kmCallDef 0x808399D4 5 | stfs f0, 0x324(r28) 6 | kamek_const_float f0, 512.0, r4 7 | stfs f0, 0x318(r28) 8 | stfs f0, 0x31C(r28) 9 | blr 10 | 11 | kmBranchDef 0x80839B00 12 | stwu r1, -0x10(r1) 13 | lwz r6, 4(r3) 14 | extrwi. r6, r6, 4, 24 15 | bne myLineLiftActivator 16 | kamek_b 0x80839B04 17 | myLineLiftActivator: 18 | mflr r0 19 | stw r0, 0x14(r1) 20 | stw r31, 0xC(r1) 21 | li r31, 0 22 | stw r30, 0x8(r1) 23 | lwz r30, 0x518(r3) 24 | b llaStartLoop 25 | llaLoop: 26 | lwz r3, 4(r30) 27 | lbz r0, 0x38C(r3) 28 | cmplwi r0, 1 29 | bne llaNotPlayer 30 | addi r31, r31, 1 31 | b llaNext 32 | llaNotPlayer: 33 | cmplwi r0, 2 34 | bne llaNext 35 | lwz r12, 0x60(r3) 36 | lwz r12, 0x6C(r12) 37 | mtctr r12 38 | bctrl 39 | lbz r0, 0(r3) 40 | extsb r0, r0 41 | cmplwi r0, 4 42 | bge llaNext 43 | addi r31, r31, 1 44 | llaNext: 45 | lwz r30, 0xC(r30) 46 | llaStartLoop: 47 | cmpwi r30, 0 48 | bne llaLoop 49 | 50 | cmpwi r31, 0 51 | beq llaReturnZero 52 | li r3, 1 53 | b llaReturn 54 | llaReturnZero: 55 | li r3, 0 56 | llaReturn: 57 | lwz r31, 0xC(r1) 58 | lwz r30, 8(r1) 59 | lwz r0, 0x14(r1) 60 | mtlr r0 61 | addi r1, r1, 0x10 62 | blr 63 | 64 | -------------------------------------------------------------------------------- /examples/build-1.bat: -------------------------------------------------------------------------------- 1 | ..\cw\mwcceppc -I- -i ../k_stdlib -Cpp_exceptions off -enum int -Os -use_lmw_stmw on -fp hard -rostr -sdata 0 -sdata2 0 -c -o 1-nsmbw-osreport.o 1-nsmbw-osreport.cpp 2 | ..\Kamek\bin\Debug\net6.0\Kamek 1-nsmbw-osreport.o -static=0x80341E68 -externals=externals-nsmbw-eu-v1.txt -output-riiv=1-loader.xml -output-gecko=1-loader.txt -output-code=1-loader.bin 3 | ..\Kamek\bin\Debug\net6.0\Kamek 1-nsmbw-osreport.o -dynamic -externals=externals-nsmbw-eu-v1.txt -versions=versions-nsmbw.txt -output-kamek=1-dynamic.$KV$.bin 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/build-1.sh: -------------------------------------------------------------------------------- 1 | CW_PATH=../cw 2 | CPPFILES="1-nsmbw-osreport" 3 | ASMFILES="" 4 | 5 | AS=$CW_PATH/mwasmeppc 6 | ASMFLAGS="-I- -i ../k_stdlib" 7 | 8 | CC=$CW_PATH/mwcceppc 9 | CFLAGS="-I- -i ../k_stdlib -Cpp_exceptions off -enum int -Os -use_lmw_stmw on -fp hard -rostr -sdata 0 -sdata2 0" 10 | 11 | OBJECTS="" 12 | 13 | for i in $CPPFILES 14 | do 15 | echo Compiling $i.cpp... 16 | OBJECTS="$OBJECTS $i.o" 17 | $CC $CFLAGS -c -o $i.o $i.cpp 18 | done 19 | for i in $ASMFILES 20 | do 21 | echo Assembling $i.S... 22 | OBJECTS="$OBJECTS $i.o" 23 | $AS $ASMFLAGS -c -o $i.o $i.S 24 | done 25 | 26 | echo Linking... 27 | 28 | ../Kamek/bin/Debug/net6.0/Kamek $OBJECTS -static=0x80341E68 -externals=externals-nsmbw-eu-v1.txt -output-riiv=1-loader.xml -output-gecko=1-loader.txt -output-code=1-loader.bin 29 | ../Kamek/bin/Debug/net6.0/Kamek $OBJECTS -dynamic -externals=externals-nsmbw-eu-v1.txt -versions=versions-nsmbw.txt -output-kamek=1-dynamic.\$KV\$.bin 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/build-2.bat: -------------------------------------------------------------------------------- 1 | ..\cw\mwasmeppc -I- -i ../k_stdlib -c -o 2-assembly.o 2-assembly.S 2 | ..\Kamek\bin\Debug\net6.0\Kamek 2-assembly.o -static=0x80341E68 -externals=externals-nsmbw-eu-v1.txt -output-riiv=2-loader.xml -output-gecko=2-loader.txt -output-code=2-loader.bin 3 | ..\Kamek\bin\Debug\net6.0\Kamek 2-assembly.o -dynamic -externals=externals-nsmbw-eu-v1.txt -versions=versions-nsmbw.txt -output-kamek=2-dynamic.$KV$.bin 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/externals-nsmbw-eu-v1.txt: -------------------------------------------------------------------------------- 1 | memcpy=0x80004364 2 | memset=0x800046B4 3 | OSReport=0x8015F870 4 | OSFatal=0x801AF710 5 | OSGetTime=0x801B60C0 6 | OSGetTick=0x801B60E0 7 | OSTicksToCalendarTime=0x801B61C0 8 | PSMTXIdentity=0x801C0610 9 | PSMTXCopy=0x801C0640 10 | PSMTXConcat=0x801C0680 11 | PSMTXConcatArray_maybe=0x801C0750 12 | PSMTXInverse=0x801C08E0 13 | PSMTXInvXpose=0x801C09E0 14 | PSMTXRotRad=0x801C0AB0 15 | PSMTXRotTrig=0x801C0B30 16 | PSMTXRotAxisRad=0x801C0C90 17 | PSMTXTrans=0x801C0D10 18 | PSMTXTransApply=0x801C0D50 19 | PSMTXScale=0x801C0DA0 20 | PSMTXScaleApply=0x801C0DD0 21 | PSMTXQuat=0x801C0E30 22 | C_MTXLookAt=0x801C0EE0 23 | C_MTXLightFrustum=0x801C1060 24 | C_MTXLightPerspective=0x801C1110 25 | C_MTXLightOrtho=0x801C1210 26 | PSMTXMultVec=0x801C12A0 27 | C_MTXFrustum=0x801C1300 28 | C_MTXPerspective=0x801C13A0 29 | C_MTXOrtho=0x801C1490 30 | PSVECAdd=0x801C1530 31 | PSVECSubtract=0x801C1560 32 | PSVECScale=0x801C1590 33 | PSVECNormalize=0x801C15B0 34 | PSVECMag=0x801C1600 35 | PSVECDotProduct=0x801C1650 36 | PSVECCrossProduct=0x801C1670 37 | C_VECHalfAngle=0x801C16B0 38 | PSVECSquareDistance=0x801C1790 39 | C_QUATMtx=0x801C17C0 40 | C_QUATSlerp=0x801C1990 41 | strlen=0x802DC98C 42 | __va_arg=0x802DC9A8 43 | #__register_global_object=0x802DCA70 # not Kamek compatible (yet) 44 | #__destroy_global_chain=0x802DCA88 45 | construct_new_array=0x802DCAD0 # actual name?? 46 | __partial_array_destructor=0x802DCBD4 47 | __construct_array=0x802DCC90 48 | __destroy_arr=0x802DCD88 49 | __ptmf_test=0x802DCE80 50 | __ptmf_cmpr=0x802DCEB0 51 | __ptmf_scall=0x802DCEEC 52 | __ptmf_scall4=0x802DCF14 53 | __cvt_fp2unsigned=0x802DCF3C 54 | _savefpr_14=0x802DCF98 55 | _savefpr_15=0x802DCF9C 56 | _savefpr_16=0x802DCFA0 57 | _savefpr_17=0x802DCFA4 58 | _savefpr_18=0x802DCFA8 59 | _savefpr_19=0x802DCFAC 60 | _savefpr_20=0x802DCFB0 61 | _savefpr_21=0x802DCFB4 62 | _savefpr_22=0x802DCFB8 63 | _savefpr_23=0x802DCFBC 64 | _savefpr_24=0x802DCFC0 65 | _savefpr_25=0x802DCFC4 66 | _savefpr_26=0x802DCFC8 67 | _savefpr_27=0x802DCFCC 68 | _savefpr_28=0x802DCFD0 69 | _savefpr_29=0x802DCFD4 70 | _savefpr_30=0x802DCFD8 71 | _savefpr_31=0x802DCFDC 72 | _restfpr_14=0x802DCFE4 73 | _restfpr_15=0x802DCFE8 74 | _restfpr_16=0x802DCFEC 75 | _restfpr_17=0x802DCFF0 76 | _restfpr_18=0x802DCFF4 77 | _restfpr_19=0x802DCFF8 78 | _restfpr_20=0x802DCFFC 79 | _restfpr_21=0x802DD000 80 | _restfpr_22=0x802DD004 81 | _restfpr_23=0x802DD008 82 | _restfpr_24=0x802DD00C 83 | _restfpr_25=0x802DD010 84 | _restfpr_26=0x802DD014 85 | _restfpr_27=0x802DD018 86 | _restfpr_28=0x802DD01C 87 | _restfpr_29=0x802DD020 88 | _restfpr_30=0x802DD024 89 | _restfpr_31=0x802DD028 90 | _savegpr_14=0x802DD030 91 | _savegpr_15=0x802DD034 92 | _savegpr_16=0x802DD038 93 | _savegpr_17=0x802DD03C 94 | _savegpr_18=0x802DD040 95 | _savegpr_19=0x802DD044 96 | _savegpr_20=0x802DD048 97 | _savegpr_21=0x802DD04C 98 | _savegpr_22=0x802DD050 99 | _savegpr_23=0x802DD054 100 | _savegpr_24=0x802DD058 101 | _savegpr_25=0x802DD05C 102 | _savegpr_26=0x802DD060 103 | _savegpr_27=0x802DD064 104 | _savegpr_28=0x802DD068 105 | _savegpr_29=0x802DD06C 106 | _savegpr_30=0x802DD070 107 | _savegpr_31=0x802DD074 108 | _restgpr_14=0x802DD07C 109 | _restgpr_15=0x802DD080 110 | _restgpr_16=0x802DD084 111 | _restgpr_17=0x802DD088 112 | _restgpr_18=0x802DD08C 113 | _restgpr_19=0x802DD090 114 | _restgpr_20=0x802DD094 115 | _restgpr_21=0x802DD098 116 | _restgpr_22=0x802DD09C 117 | _restgpr_23=0x802DD0A0 118 | _restgpr_24=0x802DD0A4 119 | _restgpr_25=0x802DD0A8 120 | _restgpr_26=0x802DD0AC 121 | _restgpr_27=0x802DD0B0 122 | _restgpr_28=0x802DD0B4 123 | _restgpr_29=0x802DD0B8 124 | _restgpr_30=0x802DD0BC 125 | _restgpr_31=0x802DD0C0 126 | __div2u=0x802DD0C8 127 | __div2i=0x802DD1B4 128 | __mod2u=0x802DD2EC 129 | __mod2i=0x802DD3D0 130 | __shl2i=0x802DD4DC 131 | __shr2i=0x802DD500 132 | abs=0x802DE804 133 | labs=0x802DE814 134 | memmove=0x802DF264 135 | memchr=0x802DF330 136 | memcmp=0x802DF388 137 | vsnprintf=0x802E18CC 138 | vsprintf=0x802E1954 139 | snprintf=0x802E19D8 140 | sprintf=0x802E1ACC 141 | strcpy=0x802E1C28 142 | strncpy=0x802E1CE8 143 | strcat=0x802E1D2C 144 | strncat=0x802E1D58 145 | strcmp=0x802E1DA4 146 | strncmp=0x802E1EC0 147 | strchr=0x802E1F00 148 | strrchr=0x802E1F30 149 | strstr=0x802E1F78 150 | atoi=0x802E23F8 151 | wmemcpy=0x802E24BC 152 | wmemchr=0x802E24C4 153 | vswprintf=0x802E4680 154 | wcslen=0x802E470C 155 | wcscpy=0x802E4728 156 | wcsncpy=0x802E4744 157 | wcscmp=0x802E4788 158 | wcschr=0x802E47BC 159 | atan=0x802E7F04 160 | ceil=0x802E8134 161 | copysign=0x802E8280 162 | cos=0x802E82AC 163 | floor=0x802E8374 164 | frexp=0x802E84C4 165 | ldexp=0x802E854C 166 | modf=0x802E86B8 167 | sin=0x802E87B4 168 | tan=0x802E8880 169 | acos=0x802E88F8 170 | asin=0x802E88FC 171 | atan2=0x802E8900 172 | fmod=0x802E8904 173 | log10=0x802E8908 174 | pow=0x802E890C 175 | sqrt=0x802E8B58 176 | stricmp=0x802E8B5C 177 | NAN=0x8042E928 -------------------------------------------------------------------------------- /examples/versions-nsmbw.txt: -------------------------------------------------------------------------------- 1 | # NSMBW address maps 2 | # Originally by Ninji and CLF78, rewritten by RoadrunnerWMC in 2021 3 | # ------------------ 4 | # Conventions: 5 | # - For this file format in general, address ranges are inclusive on 6 | # both ends. 7 | # - Some existing tools consider unmapped ranges to be implicitly +0x0, 8 | # but mine are a bit stricter and consider unmapped ranges to be 9 | # non-portable, so +0x0 ranges are provided explicitly in this map. 10 | # - When a function symbol should be preserved across versions, but the 11 | # actual code at the start of the function is completely different, I 12 | # map the first byte of the first instruction and nothing else. If the 13 | # corresponding instruction does show up later, I map the remaining 14 | # three bytes to it. So if you want to map instructions rather than 15 | # symbols, you'll be slightly more accurate if you add 1 to the 16 | # address before mapping it, and then subtract 1 after. This only 17 | # happens in a handful of places, though. 18 | # - When there's things corresponding to multiple languages (English, 19 | # Spanish, French, etc) in one version, and only one language (e.g. 20 | # Korean) in the other version, I map English to that version. 21 | 22 | 23 | [P1] 24 | # Base version: International NSMBW v1 (SMNP01 rev 1) 25 | 26 | [E1] 27 | # North American NSMBW v1 (SMNE01 rev 1) 28 | # .text section 29 | extend P1 30 | 80000000-800b4600: +0x0 31 | 800b4630-800b463f: -0x20 32 | 800b4650-800b466b: -0x1c 33 | 800b4670-800b46bb: -0x20 34 | 800b46cc-800b46fb: -0x30 35 | 800b4724-800b475f: -0x58 36 | 800b4760-800c8daf: -0x50 37 | 800c8e50-800e4c5f: -0xf0 38 | 800e4c60-800e4c63: -0xe8 39 | 800e4c64-800e4c6b: -0xf4 40 | 800e4c6c-800e4d6f: -0xf0 41 | 800e4d94-800e4d9b: -0x114 42 | 800e4da0-800e4ebf: -0x114 43 | 800e4ec0-8010f203: -0x110 44 | 8010f234-8010f237: -0x134 45 | 8010f238-8010f243: -0x144 46 | 8010f244-802bb6bf: -0x140 47 | 802bb6d0-802bb74f: -0x150 48 | 802bb860-802bbbff: -0x260 49 | 802bbc90-802edccf: -0x2f0 50 | 802edce0-80317737: -0x300 51 | 80317750-80322ffb: -0x318 52 | 80323000-8032301b: -0x31c 53 | 80323020-8032307b: -0x320 54 | 80323080-803230cf: -0x324 55 | 803230e8-803230ff: -0x33c 56 | 8032310c-8032e77f: -0x348 57 | 8032e780-8035197f: -0x340 58 | 80351980-80429563: -0x300 59 | 80429564-80429d7f: -0x2f8 60 | 80429d80-806dffff: -0x2e0 61 | 806e0000-8098a43b: +0x0 62 | 8098a43c-8098a473: +0x24 63 | 8098a478-*: +0x20 64 | 65 | [J1] 66 | # Japanese NSMBW v1 (SMNJ01 rev 1) 67 | extend P1 68 | 80000000-800b4600: +0x0 69 | 800b4630-800b4630: -0x20 70 | 800b4660-800b466b: -0x4c 71 | 800b4670-800b46ab: -0x50 72 | 800b4734-800b475f: -0xd8 73 | 800b4760-800c8daf: -0xd0 74 | 800c8e50-800e4c5f: -0x170 75 | 800e4c60-800e4c63: -0x168 76 | 800e4c64-800e4c6b: -0x174 77 | 800e4c6c-800e4d6f: -0x170 78 | 800e4d94-800e4d9b: -0x194 79 | 800e4da0-800e4ebf: -0x194 80 | 800e4ec0-8010f1df: -0x190 81 | 8010f1ec-8010f203: -0x19c 82 | 8010f234-8010f237: -0x1bc 83 | 8010f238-8010f23b: -0x1c8 84 | 8010f23c-8010f23f: -0x1d0 85 | 8010f240-8010f243: -0x1cc 86 | 8010f244-8010f257: -0x1c8 87 | 8010f32c-8010f32f: -0x29c 88 | 8010f330-8010f333: -0x298 89 | 8010f338-8010f33f: -0x29c 90 | 8010f340-8010f347: -0x298 91 | 8010f378-8010f387: -0x2c8 92 | 8010f3ec-8010f417: -0x32c 93 | 8010f41c-802bb6bf: -0x330 94 | 802bb6d0-802bb74f: -0x340 95 | 802bb860-802bbbff: -0x450 96 | 802bbc90-80317737: -0x4e0 97 | 80317750-80322ff7: -0x4f8 98 | 803230a0-803230c3: -0x5a0 99 | 80323118-8035197f: -0x5e0 100 | 80351980-80427e5f: -0x580 101 | 80427e88-8042954b: -0x5a8 102 | 80429560-80429563: -0x5bc 103 | 80429570-80429d7f: -0x5c8 104 | 80429d80-806dffff: -0x5c0 105 | 806e0000-80779abf: +0x0 106 | 80779b78-80779b7f: -0xb8 107 | 80779b84-80779b93: -0xb8 108 | 80779bbc-80779bcb: -0xe0 109 | 80779c1c-8078891f: -0x130 110 | 807889d8-807889df: -0x1e8 111 | 807889e4-807889f3: -0x1e8 112 | 80788a1c-80788a2b: -0x210 113 | 80788a7c-80789eef: -0x260 114 | 80789f00-808d3b87: -0x270 115 | 808d3bd4-808d3bd7: -0x2b4 116 | 808d3bd8-808d3bdb: -0x2bc 117 | 808d3bdc-808d3be3: -0x2b8 118 | 808d3be4-808d3be7: -0x2ac 119 | 808d3be8-808d3beb: -0x2b4 120 | 808d3bec-808d3bef: -0x2bc 121 | 808d3bf0-808d3bf3: -0x2c4 122 | 808d3bf4-808d3c17: -0x2b8 123 | 808d3c20-80940cb3: -0x2c0 124 | 80940ea0-80940ed3: -0x4ac 125 | 80940ed8-80940f07: -0x4b0 126 | 80940f58-80943187: -0x4e8 127 | 8094318c-809431ab: -0x4ec 128 | 809431b0-809431cf: -0x4f0 129 | 809431d4-809431f3: -0x4f4 130 | 809431f8-809432b7: -0x4f8 131 | 809432c0-80944e93: -0x500 132 | 80944e98-80944eb3: -0x504 133 | 80944ec0-80944edb: -0x510 134 | 80944ee0-80944eeb: -0x514 135 | 809450ac-809450c3: -0x6d4 136 | 809450c8-809450f3: -0x6d8 137 | 80945144-80945153: -0x714 138 | 80945158-8098a43b: -0x718 139 | 8098a43c-8098a473: -0x6f4 140 | 8098a478-809907f7: -0x6f8 141 | 80990800-*: -0x700 142 | 143 | [P2] 144 | # International NSMBW v2 (SMNP01 rev 2) 145 | extend P1 146 | 80000000-800cf29f: +0x0 147 | 800cf2a0-800cf2a3: +0x4 148 | 800cf2a4-800cf303: +0x8 149 | 800cf304-800cf307: +0xc 150 | 800cf308-800cf30f: +0x18 151 | 800cf320-800cf327: -0x8 152 | 800cf328-800cf6e7: +0x0 153 | 800cf6e8-800cf6eb: +0x4 154 | 800cf6ec-800cf73b: +0x8 155 | 800cf73c-800cf73f: +0xc 156 | 800cf740-800cf7e7: +0x10 157 | 800cf7e8-800cf7eb: +0x14 158 | 800cf7ec-800cf7f3: +0x20 159 | 800cf804-800cf80b: +0x0 160 | 800cf80c-800cf907: +0x8 161 | 800cf910-800e0d2b: +0x0 162 | 800e0d30-800e0d3f: -0x4 163 | 800e0d44-800e0d4b: -0x8 164 | 800e0d4c-800e0ddb: +0x10 165 | 800e0de4-800e0df7: +0x8 166 | 800e0dfc-800e0e0b: +0x4 167 | 800e0e10-807683f6: +0x0 168 | 807683f7-8076842b: +0xf 169 | 8076842c-80768462: +0x1e 170 | 80768463-8076849b: +0x2d 171 | 8076849c-8076849f: +0x3c 172 | 807684a0-807aaa30: +0x40 173 | 807aaa6c-809907ff: +0x10 174 | 80990800-*: +0x20 175 | 176 | [E2] 177 | # North American NSMBW v2 (SMNE01 rev 2) 178 | extend E1 179 | 80000000-800cf1af: +0x0 180 | 800cf1b0-800cf1b3: +0x4 181 | 800cf1b4-800cf213: +0x8 182 | 800cf214-800cf217: +0xc 183 | 800cf218-800cf21f: +0x18 184 | 800cf230-800cf237: -0x8 185 | 800cf238-800cf5f7: +0x0 186 | 800cf5f8-800cf5fb: +0x4 187 | 800cf5fc-800cf64b: +0x8 188 | 800cf64c-800cf64f: +0xc 189 | 800cf650-800cf6f7: +0x10 190 | 800cf6f8-800cf6fb: +0x14 191 | 800cf6fc-800cf703: +0x20 192 | 800cf714-800cf71b: +0x0 193 | 800cf71c-800cf817: +0x8 194 | 800cf820-800e0c3b: +0x0 195 | 800e0c40-800e0c4f: -0x4 196 | 800e0c54-800e0c5b: -0x8 197 | 800e0c5c-800e0ceb: +0x10 198 | 800e0cf4-800e0d07: +0x8 199 | 800e0d0c-800e0d1b: +0x4 200 | 800e0d20-807683f6: +0x0 201 | 807683f7-8076842b: +0xf 202 | 8076842c-80768462: +0x1e 203 | 80768463-8076849b: +0x2d 204 | 8076849c-8076849f: +0x3c 205 | 807684a0-807aaa30: +0x40 206 | 807aaa6c-8099081f: +0x10 207 | 80990820-*: +0x20 208 | 209 | [J2] 210 | # Japanese NSMBW v2 (SMNJ01 rev 2) 211 | extend J1 212 | 80000000-800cf12f: +0x0 213 | 800cf130-800cf133: +0x4 214 | 800cf134-800cf193: +0x8 215 | 800cf194-800cf197: +0xc 216 | 800cf198-800cf19f: +0x18 217 | 800cf1b0-800cf1b7: -0x8 218 | 800cf1b8-800cf577: +0x0 219 | 800cf578-800cf57b: +0x4 220 | 800cf57c-800cf5cb: +0x8 221 | 800cf5cc-800cf5cf: +0xc 222 | 800cf5d0-800cf677: +0x10 223 | 800cf678-800cf67b: +0x14 224 | 800cf67c-800cf683: +0x20 225 | 800cf694-800cf69b: +0x0 226 | 800cf69c-800cf797: +0x8 227 | 800cf7a0-800e0bbb: +0x0 228 | 800e0bc0-800e0bcf: -0x4 229 | 800e0bd4-800e0bdb: -0x8 230 | 800e0bdc-800e0c6b: +0x10 231 | 800e0c74-800e0c87: +0x8 232 | 800e0c8c-800e0c9b: +0x4 233 | 800e0ca0-807683f6: +0x0 234 | 807683f7-8076842b: +0xf 235 | 8076842c-80768462: +0x1e 236 | 80768463-8076849b: +0x2d 237 | 8076849c-8076849f: +0x3c 238 | 807684a0-807aa7c0: +0x40 239 | 807aa7fc-809900ff: +0x10 240 | 80990100-*: +0x20 241 | 242 | [K] 243 | # South Korean NSMBW (SMNK01) 244 | extend P2 245 | 80000000-800122c0: +0x0 246 | 800122c1-800122eb: +0x14 247 | 800122f0-800122f7: +0x14 248 | 80012300-8001230b: +0x28 249 | 8001230c-8001230f: +0x2c 250 | 80012314-8001231f: +0x3c 251 | 80012320-80012337: +0x40 252 | 80012338-8001233f: +0x78 253 | 80012364-80012397: +0x54 254 | 80012398-8001239f: +0x58 255 | 800123a4-800123bb: +0x54 256 | 800123c0-800123eb: +0x50 257 | 800123ec-8001244f: +0x64 258 | 80012450-8004dea7: +0x70 259 | 8004deac-8004deb7: +0x6c 260 | 8004dec0-8004dedf: +0x68 261 | 8004dee0-8004df37: +0x70 262 | 8004df3c-8004df47: +0x6c 263 | 8004df50-8004df6f: +0x68 264 | 8004df70-80050e5f: +0x70 265 | 80050e60-80052f03: +0xd0 266 | 80052f24-8005c453: +0xe0 267 | 8005c454-8005c457: +0xe8 268 | 8005c458-8005c48b: +0xf8 269 | 8005c48c-8005c497: +0xfc 270 | 8005c498-8005c6c7: +0x108 271 | 8005c6d0-8005f813: +0x100 272 | 8005f850-8005f877: +0xc8 273 | 8005f880-8009624f: +0x140 274 | 80096250-80096253: +0x150 275 | 80096254-8009625b: +0x13c 276 | 8009625c-8009626f: +0x148 277 | 80096278-8009628f: +0x17c 278 | 80096290-800b45ff: +0x180 279 | 800b4600-800b4600: +0x190 280 | 800b4630-800b4630: +0x170 281 | 800b4670-800b46ab: +0x140 282 | 800b4734-800b475f: +0xb8 283 | 800b4760-800c8daf: +0xc0 284 | 800c8e50-800e4c5f: +0x20 285 | 800e4c60-800e4c63: +0x28 286 | 800e4c64-800e4c6b: +0x1c 287 | 800e4c6c-800e4d6f: +0x20 288 | 800e4d94-800e4d9b: -0x4 289 | 800e4da0-800e4ebf: -0x4 290 | 800e4ec0-800f794f: +0x0 291 | 800f7950-800f79bf: +0xc 292 | 800f79c0-800fd5eb: +0x10 293 | 800fd5ec-800fd60b: +0x44 294 | 800fd610-801011bf: +0x40 295 | 801011c0-8010127f: +0xb0 296 | 80101280-80106f37: +0x100 297 | 80106f38-80106f6f: +0x108 298 | 80106f70-80106fbb: +0x110 299 | 80106fbc-80106fbf: +0x114 300 | 80106fc0-80106fc3: +0x12c 301 | 80106fc4-80106fc7: +0x11c 302 | 80106fc8-80106fe7: +0x128 303 | 80106ff0-80107047: +0x120 304 | 80107048-80107053: +0x124 305 | 80107074-80107077: +0x114 306 | 80107078-8010708f: +0x118 307 | 80107090-80107090: +0x120 308 | 80107091-8010709f: +0x144 309 | 801070a4-801070ab: +0x174 310 | 801070b0-801070b0: +0x170 311 | 801070b1-801070b3: +0x188 312 | 801070b4-801070b7: +0x194 313 | 801070b8-801070bb: +0x188 314 | 801070bc-801070bf: +0x194 315 | 801070c0-801070c3: +0x18c 316 | 801070c8-801070cf: +0x1c0 317 | 801070d0-8010710b: +0x1f0 318 | 8010710c-80107113: +0x1fc 319 | 80107114-801071f3: +0x210 320 | 801071f4-8010727f: +0x21c 321 | 80107280-80107317: +0x350 322 | 80107318-8010733b: +0x35c 323 | 8010733c-801073bf: +0x368 324 | 801073c0-801074ef: +0x4e0 325 | 801074f0-801074f7: +0x4e4 326 | 801074f8-801074fb: +0x4d8 327 | 801074fc-80107503: +0x4e0 328 | 80107504-8010751b: +0x4e4 329 | 80107520-801076c7: +0x4e0 330 | 801076c8-80107707: +0x4e4 331 | 80107708-80107787: +0x4f0 332 | 80107788-801077b3: +0x4fc 333 | 801077c0-801079f3: +0x4f0 334 | 801079f4-80107b5b: +0x520 335 | 80107b60-80107c27: +0x520 336 | 80107c28-80107c5f: +0x524 337 | 80107c60-80107c63: +0x530 338 | 80107c64-80107c67: +0x60c 339 | 80107c68-80107c6b: +0x62c 340 | 80107c6c-80107c6f: +0x620 341 | 80107c70-80107cef: +0x528 342 | 80107cf0-80107d27: +0x530 343 | 80107d28-80107d3b: +0x534 344 | 80107d3c-80107d53: +0x538 345 | 80107d54-80107d57: +0x430 346 | 80107d58-80107d5b: +0x43c 347 | 80107d5c-80107d5f: +0x534 348 | 80107d60-80107d77: +0x538 349 | 80107d80-80107e83: +0x530 350 | 80107e84-80107fb3: +0x538 351 | 80107fb4-80107fcf: +0x544 352 | 80107fd0-8010f1b3: +0x550 353 | 8010f1b4-8010f1b7: +0x570 354 | 8010f1b8-8010f1bb: +0x550 355 | 8010f1bc-8010f1cf: +0x56c 356 | 8010f1d0-8010f1df: +0x570 357 | 8010f1ec-8010f203: +0x564 358 | 8010f234-8010f237: +0x544 359 | 8010f238-8010f23b: +0x538 360 | 8010f23c-8010f23f: +0x530 361 | 8010f240-8010f243: +0x534 362 | 8010f244-8010f257: +0x538 363 | 8010f32c-8010f32f: +0x464 364 | 8010f330-8010f333: +0x468 365 | 8010f338-8010f33f: +0x464 366 | 8010f340-8010f347: +0x468 367 | 8010f378-8010f387: +0x438 368 | 8010f3ec-8010f417: +0x3d4 369 | 8010f41c-80150e77: +0x3d0 370 | 80150e80-80150e83: +0x3c8 371 | 80150e90-80150ebb: +0x3c0 372 | 80150ec4-80150ec7: +0x3b8 373 | 80150ed4-8015140f: +0x3b0 374 | 80151410-8015145f: +0x3c4 375 | 80151460-80152413: +0x3d0 376 | 80152414-8015241f: +0x3dc 377 | 80152424-80158837: +0x3e0 378 | 8015883c-801589cf: +0x3dc 379 | 801589d0-801590c3: +0x3e0 380 | 801590c4-801590eb: +0x3ec 381 | 801590ec-8015912f: +0x3f0 382 | 80159130-8015918f: +0x3f4 383 | 80159190-8015acdf: +0x400 384 | 8015ace0-8015ace3: +0x410 385 | 8015ace4-8015ace7: +0x3fc 386 | 8015ace8-8015acef: +0x400 387 | 8015acf0-8015acf3: +0x40c 388 | 8015acf4-8015acfb: +0x400 389 | 8015acfc-8015ad2b: +0x404 390 | 8015ad2c-8015ad2f: +0x3b8 391 | 8015ad30-802bb6bf: +0x400 392 | 802bb6d0-802bb74f: +0x3f0 393 | 802bb860-802bbbff: +0x2e0 394 | 802bbc90-802e467c: +0x250 395 | 802e4680-802edcd7: +0x348 396 | 802edce0-802f4b1f: +0x340 397 | 802f4b20-802f4b7f: +0x348 398 | 802f4b80-802f4bdf: +0x350 399 | 802f4be0-802f5d43: +0x358 400 | 802f5d44-802fd29f: +0xc538 401 | 802fd2a0-80317737: +0xc540 402 | 80317750-8032080f: +0xc528 403 | 80320810-8032164f: +0xc570 404 | 80321650-80321f07: +0xc578 405 | 80321f08-80321f17: +0xc57c 406 | 80321f18-80321f33: +0xc588 407 | 80321f38-80321f6f: +0xc584 408 | 80321f70-80321f97: +0xc588 409 | 80321f98-80321fe7: +0xc58c 410 | 80321fe8-80322003: +0xc594 411 | 80322004-8032204f: +0xc5b4 412 | 80322050-8032206b: +0xc600 413 | 8032206c-803220bf: +0xc61c 414 | 803220c0-803220df: +0xc670 415 | 803220e0-80322147: +0xc690 416 | 80322148-803221a7: +0xc6f4 417 | 803221a8-803221c3: +0xc754 418 | 803221c4-80322233: +0xc76c 419 | 80322234-803222a3: +0xc7d8 420 | 803222a4-803222c3: +0xc848 421 | 803222c4-80322337: +0xc86c 422 | 80322338-80322353: +0xc8e0 423 | 80322354-803223c7: +0xc8fc 424 | 803223c8-803223e7: +0xc968 425 | 803223e8-80322447: +0xc988 426 | 80322448-803224d7: +0xcab8 427 | 803224d8-803224e3: +0xcad0 428 | 803224f0-80322ff7: +0xcac8 429 | 803230a0-803230c3: +0xca20 430 | 80323118-803279cf: +0xc9e0 431 | 803279d0-8032e777: +0xc9e8 432 | 8032e780-8035197f: +0xc9e0 433 | 80351980-80427e5f: +0xca00 434 | 80427e88-8042823f: +0xc9d8 435 | 80428244-80428253: +0xc9d4 436 | 80428258-80429427: +0xc9d0 437 | 80429428-80429497: +0xc9d8 438 | 80429498-804294df: +0xc9e0 439 | 804294e0-804294e3: +0xc9e4 440 | 804294e8-8042954b: +0xc9e0 441 | 80429560-80429563: +0xc9cc 442 | 80429570-8042d537: +0xc9c0 443 | 8042d538-8042d547: +0xc9c8 444 | 8042d548-8042d54b: +0xc9cc 445 | 8042d550-8042f427: +0xc9c8 446 | 8042f430-806d363f: +0xc9c0 447 | 806e0000-807683f2: +0x0 448 | 80768406-80768436: -0x13 449 | 8076844a-8076847c: -0x26 450 | 80768490-807684c4: -0x39 451 | 807684d8-807684df: -0x4c 452 | 807684e0-8076b27f: -0x40 453 | 8076b280-8076ca93: -0x38 454 | 8076ca94-8076d45f: -0x28 455 | 8076d4a0-8076dd7c: -0x40 456 | 8076dd80-8076dd83: -0x28 457 | 8076dd88-8076dd8b: -0x2c 458 | 8076dd8c-8076dd8f: -0x28 459 | 8076dd90-8076dd93: -0x30 460 | 8076dd94-8076dd97: -0x28 461 | 8076dd98-8076dd9b: -0x30 462 | 8076dd9c-8076ddcf: -0x2c 463 | 8076ddd0-8076ddd3: -0x28 464 | 8076ddd4-8076df1f: -0x24 465 | 8076df20-80779afc: -0x20 466 | 80779bb8-80779bd3: -0xd8 467 | 80779bfc-80779c0b: -0x100 468 | 80779c5c-8078895f: -0x150 469 | 80788a18-80788a33: -0x208 470 | 80788a5c-80788a6b: -0x230 471 | 80788abc-8079d57f: -0x280 472 | 8079d580-8079d8f3: +0x4090 473 | 8079d8f4-8079d9bb: +0x4094 474 | 8079d9bc-8079d9eb: +0x40b0 475 | 8079d9ec-8079e1db: +0x40c0 476 | 8079e1dc-8079e2f3: +0x40d0 477 | 8079e2f8-8079e2fb: +0x40cc 478 | 8079e318-8079e387: +0x40dc 479 | 8079e3a8-8079e3cb: +0x40bc 480 | 8079e3d0-8079e3d3: +0x40b8 481 | 8079e3f0-8079e45f: +0x40c8 482 | 8079e480-8079e497: +0x40a8 483 | 8079e4a0-8079eb23: +0x40a0 484 | 8079eb24-8079eb63: +0x40b0 485 | 8079eb64-8079ebdb: +0x40c4 486 | 8079ebe0-807a124f: +0x40c0 487 | 807a1250-807a1253: +0x40dc 488 | 807a1254-807a1257: +0x40c0 489 | 807a1258-807a125b: +0x40d8 490 | 807a125c-807a125f: +0x40b4 491 | 807a1260-807a1273: +0x40b8 492 | 807a1274-807a43af: +0x40c0 493 | 807a43b0-807a43b3: +0x40c4 494 | 807a43b8-807a43bb: +0x40b8 495 | 807a43bc-807a44bf: +0x40bc 496 | 807a44c0-807a7b63: +0x40c0 497 | 807a7b64-807a7b67: +0x40c4 498 | 807a7b6c-807a7b6f: +0x40b8 499 | 807a7b70-807a7c1f: +0x40bc 500 | 807a7c20-80817390: +0x40c0 501 | 80817391-80817393: +0x40c8 502 | 80817394-80817397: +0x40e0 503 | 80817398-8081739b: +0x40cc 504 | 8081739c-8081739f: +0x40d4 505 | 808173a0-808173a7: +0x40c8 506 | 808173a8-808173b3: +0x40d0 507 | 808173b4-808173b7: +0x40a8 508 | 808173b8-808173eb: +0x40cc 509 | 808173ec-808173ef: +0x40e0 510 | 808173f0-808173f7: +0x40e8 511 | 80817400-808174db: +0x40e0 512 | 808174dc-8081752f: +0x40ec 513 | 80817530-80826ddf: +0x40f0 514 | 80826e90-80827003: +0x4210 515 | 80827004-80827007: +0x4214 516 | 80827008-8082700f: +0x4218 517 | 80827014-80827023: +0x4228 518 | 80827028-80827043: +0x4224 519 | 80827048-80827227: +0x4220 520 | 80827228-8082729f: +0x4228 521 | 808272a0-8086e98f: +0x4230 522 | 8086e990-8086e9db: +0x4244 523 | 8086e9e0-8086eabb: +0x4240 524 | 8086eabc-8086eabf: +0x4244 525 | 8086eac0-8086eac3: +0x423c 526 | 8086eac4-8086eb7b: +0x4240 527 | 8086eb7c-8086eb7f: +0x4244 528 | 8086eb80-8086eb83: +0x4260 529 | 8086eb84-8086eb87: +0x4268 530 | 8086eb88-8086eb8b: +0x4260 531 | 8086eb8c-8086ebbf: +0x4264 532 | 8086ebc0-808d3b97: +0x4270 533 | 808d3be4-808d3be7: +0x422c 534 | 808d3be8-808d3beb: +0x4224 535 | 808d3bec-808d3bef: +0x4228 536 | 808d3bf8-808d3bfb: +0x422c 537 | 808d3bfc-808d3bff: +0x4224 538 | 808d3c00-808d3c03: +0x421c 539 | 808d3c04-808d3c27: +0x4228 540 | 808d3c30-808ef2df: +0x4220 541 | 808ef310-808fb94f: +0x41f0 542 | 808fb950-808fb95b: +0x41fc 543 | 808fb994-808fbcef: +0x41f8 544 | 808fbcf8-808fbd0f: +0x420c 545 | 808fbd10-808fbfdf: +0x4210 546 | 808fbfe0-80902a87: +0x4250 547 | 80902aa0-80902acf: +0x423c 548 | 80902ad0-8090947f: +0x4240 549 | 80909510-8091f15f: +0x41b0 550 | 8091f160-8091f167: +0x41b4 551 | 8091f168-8091f17f: +0x41b8 552 | 8091f180-8091f257: +0x41c0 553 | 8091f258-8091f27b: +0x41d4 554 | 8091f280-809337eb: +0x41d0 555 | 809337ec-8093520f: +0x41d8 556 | 80935210-809382cf: +0x42f0 557 | 809382e0-80940c77: +0x4300 558 | 80940c7c-80940c9b: +0x42fc 559 | 80940ca0-80940cbf: +0x42f8 560 | 80940cc4-80940ccf: +0x42f4 561 | 80940eb0-80940ee3: +0x4114 562 | 80940ee8-80940f17: +0x4110 563 | 80940f68-80944ea3: +0x40d8 564 | 80944ea8-80944ec7: +0x40d4 565 | 80944ed0-80944efb: +0x40cc 566 | 809450bc-809450d3: +0x3f0c 567 | 809450d8-80945103: +0x3f08 568 | 80945154-809494a7: +0x3ed0 569 | 809494a8-8094978f: +0x3edc 570 | 80949790-8094a387: +0x3ee0 571 | 8094a388-8094a3bb: +0x5090 572 | 8094a3bc-8094a443: +0x5094 573 | 8094a444-8094a46f: +0x5098 574 | 8094a470-8094a49b: +0x509c 575 | 8094a4a0-8094a5f3: +0x5098 576 | 8094a5f8-8094a617: +0x50a4 577 | 8094a618-8094c0d7: +0x50a8 578 | 8094c0d8-8094c123: +0x50b0 579 | 8094c124-8094c14b: +0x50b4 580 | 8094c150-8094c177: +0x50b0 581 | 8094c178-8094c1a3: +0x50b4 582 | 8094c1a8-8094cabb: +0x50b0 583 | 8094cabc-8094d5cb: +0x50b8 584 | 8094d5cc-8098a37b: +0x50c0 585 | 8098a398-8098a3b3: +0x50a4 586 | 8098a488-8099080f: +0x4fd0 587 | 80990820-80994317: +0x4fc0 588 | 80994318-80a0a57f: +0x54e0 589 | 80a0a580-80a0a5a7: +0x54e4 590 | 80a0a5b4-80a0a5ef: +0x54e8 591 | 80a0a5f0-80a0a5ff: +0x54ec 592 | 80a0a600-80a0a60f: +0x54f0 593 | 80a0a610-80a0a633: +0x54f8 594 | 80a0a63c-80a0a63f: +0x54fc 595 | 80a0a640-80a0a643: +0x5504 596 | 80a0a644-80a0a647: +0x54fc 597 | 80a0a648-80a0a64b: +0x54f4 598 | 80a0a64c-80a0a67f: +0x54fc 599 | 80a0a680-80a0b1c3: +0x5500 600 | 80a0b1c4-80a0b1c7: +0x5578 601 | 80a0b1c8-80a0b1cb: +0x5500 602 | 80a0b1cc-80a0b50f: +0x5574 603 | 80a0b510-80a38ee0: +0x5580 604 | 80a38ee1-80a38ee3: +0x55a4 605 | 80a38ee4-80a38eef: +0x55ac 606 | 80a38ef0-80b1143f: +0x55b0 607 | 80b11440-80b304fb: +0x55c0 608 | 80b304fc-80b30513: +0x55cc 609 | 80b3051c-80b3051f: +0x55c8 610 | 80b30520-80b30523: +0x55dc 611 | 80b30524-80b30527: +0x55d0 612 | 80b30528-80b3052b: +0x55d8 613 | 80b3052c-80b3052f: +0x55cc 614 | 80b30530-80b30533: +0x55bc 615 | 80b30534-80b30537: +0x55d8 616 | 80b30538-80b3053f: +0x55cc 617 | 80b30540-80b30543: +0x55d4 618 | 80b30544-80b30547: +0x55cc 619 | 80b30548-80b3054b: +0x55d0 620 | 80b3054c-80b3054f: +0x55e8 621 | 80b30550-80b3055f: +0x55cc 622 | 80b30560-80b30563: +0x55dc 623 | 80b30564-80b3056b: +0x55c8 624 | 80b3056c-80b3056f: +0x55cc 625 | 80b30570-80b89aaf: +0x55d0 626 | 80b89ac0-*: +0x55c0 627 | 628 | [W] 629 | # Taiwanese NSMBW (SMNW01) 630 | extend K 631 | 80000000-800b47a0: +0x0 632 | 800b47a4-800b47ab: +0x4 633 | 800b47b0-800e712f: +0x0 634 | 800e7130-800e7133: +0xc 635 | 800e7134-800e7137: -0x4 636 | 800e7138-800e713b: +0x0 637 | 800e713c-800e714b: +0x4 638 | 800e7150-80158c17: +0x0 639 | 80158c18-80158dab: +0x4 640 | 80158db0-80300647: +0x0 641 | 80302278-803097cf: -0x1c30 642 | 803097e0-803343af: -0x1c40 643 | 803343b8-8033b15f: -0x1c48 644 | 8033b160-8035e37f: -0x1c40 645 | 8035e380-806dffff: -0x1c00 646 | 806e0000-807a1a33: +0x0 647 | 807a1a50-807a1a9b: -0x1c 648 | 807a1aac-807a1adf: -0x2c 649 | 807a1ae0-807a229b: -0x20 650 | 807a22ac-807a2bc3: -0x30 651 | 807a2bd4-807a2c13: -0x40 652 | 807a2c28-807a2c9f: -0x54 653 | 807a2ca0-807a846f: -0x50 654 | 807a8470-807a8473: -0x48 655 | 807a8474-807a8477: -0x54 656 | 807a8478-807a857b: -0x4c 657 | 807a8580-807abc23: -0x50 658 | 807abc24-807abc27: -0x48 659 | 807abc28-807abc2b: -0x54 660 | 807abc2c-807abcdb: -0x4c 661 | 807abce0-8094d377: -0x50 662 | 8094d384-8094d66b: -0x5c 663 | 8094d670-8094f44b: -0x60 664 | 8094f450-8094f4d7: -0x64 665 | 8094f4dc-8094f507: -0x68 666 | 8094f50c-8094f54f: -0x6c 667 | 8094f550-8094f68b: -0x68 668 | 8094f69c-8095117f: -0x78 669 | 80951188-809511d3: -0x80 670 | 809511d8-809511ff: -0x84 671 | 80951200-80951227: -0x80 672 | 8095122c-80951277: -0x84 673 | 80951278-80951b6b: -0x80 674 | 80951b74-80952683: -0x88 675 | 8095268c-809957df: -0x90 676 | 809957e0-*: -0x80 677 | -------------------------------------------------------------------------------- /k_stdlib/base/c_stdlib.h: -------------------------------------------------------------------------------- 1 | #ifndef __KAMEK_BASE_C_STDLIB_H 2 | #define __KAMEK_BASE_C_STDLIB_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* math.h */ 9 | double acos(double); 10 | double asin(double); 11 | double atan(double); 12 | double atan2(double, double); 13 | double ceil(double); 14 | double copysign(double, double); 15 | double cos(double); 16 | double cosh(double); // not in nsmbw 17 | double exp(double); // not in nsmbw 18 | inline double fabs(double x) { return __fabs(x); } 19 | double floor(double); 20 | double fmod(double, double); 21 | double frexp(double, int *); 22 | double ldexp(double, int); 23 | double log(double); // not in nsmbw? 24 | double log10(double); 25 | double modf(double, double *); 26 | double pow(double, double); 27 | double sin(double); 28 | double sinh(double); // not in nsmbw 29 | double sqrt(double); 30 | double tan(double); 31 | double tanh(double); // not in nsmbw 32 | 33 | extern f32 NAN; 34 | 35 | /* stdarg.h */ 36 | typedef struct va_list_struct { 37 | char gpr, fpr, reserved[2], *input_arg_area, *reg_save_area; 38 | } va_list[1]; 39 | extern void *__va_arg(void *, int); 40 | #define va_start(ap, last) (__builtin_va_info(&ap)) 41 | #define va_arg(ap, type) (*((type *)__va_arg(ap, _var_arg_typeof(type)))) 42 | #define va_end(ap) ((void)0) 43 | 44 | /* stdio.h */ 45 | int snprintf(char *, size_t, const char *, ...); 46 | int sprintf(char *, const char *, ...); 47 | int vsnprintf(char *, size_t, const char *, va_list); 48 | int vsprintf(char *, const char *, va_list); 49 | 50 | /* stdlib.h */ 51 | int abs(int); 52 | long labs(long); 53 | int atoi(const char *str); 54 | 55 | /* string.h */ 56 | void *memchr(const void *, int, size_t); 57 | int memcmp(const void *, const void *, size_t); 58 | void *memcpy(void *, const void *, size_t); 59 | void *memmove(void *, const void *, size_t); 60 | void *memset(void *, int, size_t); 61 | char *strcpy(char *, const char *); 62 | char *strncpy(char *, const char *, size_t); 63 | char *strcat(char *, const char *); 64 | char *strncat(char *, const char *, size_t); 65 | size_t strlen(const char *); 66 | int strcmp(const char *, const char *); 67 | int strncmp(const char *, const char *, size_t); 68 | int stricmp(const char *, const char *); // not standard but nsmbw's got it anyway! 69 | char *strchr(const char *, int); 70 | char *strrchr(const char *, int); 71 | char *strstr(const char *, const char *); 72 | 73 | /* wchar.h */ 74 | int vswprintf(wchar_t *, size_t, const wchar_t *, va_list); 75 | wchar_t *wcschr(const wchar_t *, wchar_t); 76 | int wcscmp(const wchar_t *, const wchar_t *); 77 | wchar_t *wcscpy(wchar_t *, const wchar_t *); 78 | wchar_t *wcsncpy(wchar_t *, const wchar_t *, size_t); 79 | size_t wcslen(const wchar_t *); 80 | wchar_t *wmemchr(const wchar_t, wchar_t, size_t); 81 | wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t); 82 | 83 | #ifdef __cplusplus 84 | } 85 | #endif 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /k_stdlib/base/hooks.h: -------------------------------------------------------------------------------- 1 | #ifndef __KAMEK_BASE_HOOKS_H 2 | #define __KAMEK_BASE_HOOKS_H 3 | 4 | // allow Kamek hooks to be defined from C++ source files 5 | #pragma section ".kamek" 6 | 7 | // hook type IDs _must_ match what's in the Kamek source! 8 | #define kctWrite 1 9 | #define kctConditionalWrite 2 10 | #define kctInjectBranch 3 11 | #define kctInjectCall 4 12 | #define kctPatchExit 5 13 | 14 | 15 | #define kmIdentifier(key, counter) \ 16 | _k##key##counter 17 | #define kmHookInt(counter) \ 18 | __declspec (section ".kamek") static const u32 kmIdentifier(Hook, counter) 19 | 20 | // general hook definition macros 21 | // TODO: debugging data (file, line, ...) for diagnostic use by Kamek maybe? :3 22 | #define kmHook0(type) \ 23 | kmHookInt(__COUNTER__)[2] = { 0, (type) } 24 | #define kmHook1(type, arg0) \ 25 | kmHookInt(__COUNTER__)[3] = { 1, (type), (u32)(arg0) } 26 | #define kmHook2(type, arg0, arg1) \ 27 | kmHookInt(__COUNTER__)[4] = { 2, (type), (u32)(arg0), (u32)(arg1) } 28 | #define kmHook3(type, arg0, arg1, arg2) \ 29 | kmHookInt(__COUNTER__)[5] = { 3, (type), (u32)(arg0), (u32)(arg1), (u32)(arg2) } 30 | #define kmHook4(type, arg0, arg1, arg2, arg3) \ 31 | kmHookInt(__COUNTER__)[6] = { 4, (type), (u32)(arg0), (u32)(arg1), (u32)(arg2), (u32)(arg3) } 32 | 33 | // kmCondWrite 34 | // Write value to address, conditionally 35 | #define kmCondWritePointer(addr, original, value) kmHook4(kctConditionalWrite, 1, (addr), (value), (original)) 36 | #define kmCondWrite32(addr, original, value) kmHook4(kctConditionalWrite, 2, (addr), (value), (original)) 37 | #define kmCondWrite16(addr, original, value) kmHook4(kctConditionalWrite, 3, (addr), (value), (original)) 38 | #define kmCondWrite8(addr, original, value) kmHook4(kctConditionalWrite, 4, (addr), (value), (original)) 39 | 40 | // kmWrite 41 | // Write value to address 42 | #define kmWritePointer(addr, ptr) kmHook3(kctWrite, 1, (addr), (ptr)) 43 | #define kmWrite32(addr, value) kmHook3(kctWrite, 2, (addr), (value)) 44 | #define kmWrite16(addr, value) kmHook3(kctWrite, 3, (addr), (value)) 45 | #define kmWrite8(addr, value) kmHook3(kctWrite, 4, (addr), (value)) 46 | 47 | // kmPatchExitPoint 48 | // Force the end of a Kamek function to always jump to a specific address 49 | // (if the address is 0, the end remains as-is (i.e. blr)) 50 | #define kmPatchExitPoint(funcStart, dest) kmHook2(kctPatchExit, (funcStart), (dest)) 51 | 52 | // kmBranch, kmCall 53 | // Set up a branch from a specific instruction to a specific address 54 | #define kmBranch(addr, ptr) kmHook2(kctInjectBranch, (addr), (ptr)) 55 | #define kmCall(addr, ptr) kmHook2(kctInjectCall, (addr), (ptr)) 56 | 57 | // kmBranchDefCpp, kmBranchDefAsm 58 | // Set up a branch (b) from a specific instruction to a function defined 59 | // directly underneath. If exitPoint is not NULL, the function will 60 | // branch to exitPoint when done; otherwise, it executes blr as normal 61 | #define kmBranchDefInt(counter, addr, exitPoint, returnType, ...) \ 62 | static returnType kmIdentifier(UserFunc, counter) (__VA_ARGS__); \ 63 | kmBranch(addr, kmIdentifier(UserFunc, counter)); \ 64 | kmPatchExitPoint(kmIdentifier(UserFunc, counter), exitPoint); \ 65 | static returnType kmIdentifier(UserFunc, counter) (__VA_ARGS__) 66 | 67 | #define kmBranchDefCpp(addr, exitPoint, returnType, ...) \ 68 | kmBranchDefInt(__COUNTER__, addr, exitPoint, returnType, __VA_ARGS__) 69 | #define kmBranchDefAsm(addr, exitPoint) \ 70 | kmBranchDefInt(__COUNTER__, addr, exitPoint, asm void, ) 71 | 72 | // kmCallDefCpp, kmCallDefAsm 73 | // Set up a branch with link (bl) from a specific instruction to a function 74 | // defined directly underneath. 75 | #define kmCallDefInt(counter, addr, returnType, ...) \ 76 | static returnType kmIdentifier(UserFunc, counter) (__VA_ARGS__); \ 77 | kmCall(addr, kmIdentifier(UserFunc, counter)); \ 78 | static returnType kmIdentifier(UserFunc, counter) (__VA_ARGS__) 79 | 80 | #define kmCallDefCpp(addr, returnType, ...) \ 81 | kmCallDefInt(__COUNTER__, addr, returnType, __VA_ARGS__) 82 | #define kmCallDefAsm(addr) \ 83 | kmCallDefInt(__COUNTER__, addr, asm void, ) 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /k_stdlib/base/rvl_sdk.h: -------------------------------------------------------------------------------- 1 | #ifndef __KAMEK_BASE_RVL_SDK_H 2 | #define __KAMEK_BASE_RVL_SDK_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* OS Module */ 9 | void OSReport(const char *format, ...); 10 | u64 OSGetTime(); 11 | u32 OSGetTick(); 12 | void OSFatal(u32 *fg, u32 *bg, const char *message); 13 | 14 | typedef struct { 15 | int sec, min, hour, mday, mon, year, wday, yday, msec, usec; 16 | } OSCalendarTime; 17 | void OSTicksToCalendarTime(u64 time, OSCalendarTime *result); 18 | 19 | /* MTX Module */ 20 | typedef struct { f32 x, y; } Vec2; 21 | typedef struct { f32 x, y, z; } Vec; 22 | typedef struct { s16 x, y, z; } S16Vec; 23 | typedef f32 Mtx[3][4]; 24 | typedef f32 Mtx44[4][4]; 25 | typedef struct { f32 x, y, z, w; } Quaternion; 26 | 27 | void PSMTXIdentity(Mtx matrix); 28 | void PSMTXCopy(const Mtx source, Mtx dest); 29 | void PSMTXConcat(const Mtx sourceA, const Mtx sourceB, Mtx dest); 30 | void PSMTXConcatArray(const Mtx sourceA, const Mtx *sourcesB, Mtx *destsB, int count); 31 | u32 PSMTXInverse(const Mtx source, Mtx dest); 32 | u32 PSMTXInvXpose(const Mtx source, Mtx dest); 33 | void PSMTXRotRad(Mtx matrix, u8 axis, f32 radians); 34 | void PSMTXRotTrig(Mtx matrix, u8 axis, f32 sin, f32 cos); 35 | void PSMTXRotAxisRad(Mtx matrix, Vec *axis, f32 radians); 36 | void PSMTXTrans(Mtx matrix, f32 x, f32 y, f32 z); 37 | void PSMTXTransApply(const Mtx source, Mtx dest, f32 x, f32 y, f32 z); 38 | void PSMTXScale(Mtx matrix, f32 x, f32 y, f32 z); 39 | void PSMTXScaleApply(const Mtx source, Mtx dest, f32 x, f32 y, f32 z); 40 | void PSMTXQuat(Mtx dest, const Quaternion *quat); 41 | void C_MTXLookAt(Mtx dest, const Vec *cameraPos, const Vec *cameraUp, const Vec *target); 42 | void C_MTXLightFrustum(Mtx dest, f32 top, f32 bottom, f32 left, f32 right, f32 near, f32 scaleS, f32 scaleT, f32 transS, f32 transT); 43 | void C_MTXLightPerspective(Mtx dest, f32 fovy, f32 aspect, f32 scaleS, f32 scaleT, f32 transS, f32 transT); 44 | void C_MTXLightOrtho(Mtx dest, f32 top, f32 bottom, f32 left, f32 right, f32 scaleS, f32 scaleT, f32 transS, f32 transT); 45 | void PSMTXMultVec(const Mtx matrix, const Vec *source, Vec *dest); 46 | void C_MTXFrustum(Mtx44 dest, f32 top, f32 bottom, f32 left, f32 right, f32 near, f32 far); 47 | void C_MTXPerspective(Mtx44 dest, f32 fovy, f32 aspect, f32 near, f32 far); 48 | void C_MTXOrtho(Mtx44 dest, f32 top, f32 bottom, f32 left, f32 right, f32 near, f32 far); 49 | void PSVECAdd(const Vec *sourceA, const Vec *sourceB, Vec *dest); 50 | void PSVECSubtract(const Vec *sourceA, const Vec *sourceB, Vec *dest); 51 | void PSVECScale(const Vec *source, Vec *dest, f32 scale); 52 | void PSVECNormalize(const Vec *source, Vec *dest); 53 | f32 PSVECMag(const Vec *vec); 54 | f32 PSVECDotProduct(const Vec *sourceA, const Vec *sourceB); 55 | void PSVECCrossProduct(const Vec *sourceA, const Vec *sourceB, Vec *dest); 56 | void C_VECHalfAngle(const Vec *sourceA, const Vec *sourceB, Vec *dest); 57 | f32 PSVECSquareDistance(const Vec *sourceA, const Vec *sourceB); 58 | void C_QUATMtx(Quaternion *dest, const Mtx source); 59 | void C_QUATSlerp(const Quaternion *sourceA, const Quaternion *sourceB, Quaternion *dest, f32 value); 60 | 61 | #define MTXIdentity PSMTXIdentity 62 | #define MTXCopy PSMTXCopy 63 | #define MTXConcat PSMTXConcat 64 | #define MTXConcatArray PSMTXConcatArray 65 | #define MTXInverse PSMTXInverse 66 | #define MTXInvXpose PSMTXInvXpose 67 | #define MTXRotRad PSMTXRotRad 68 | #define MTXRotTrig PSMTXRotTrig 69 | #define MTXRotAxisRad PSMTXRotAxisRad 70 | #define MTXTrans PSMTXTrans 71 | #define MTXTransApply PSMTXTransApply 72 | #define MTXScale PSMTXScale 73 | #define MTXScaleApply PSMTXScaleApply 74 | #define MTXQuat PSMTXQuat 75 | #define MTXLookAt C_MTXLookAt 76 | #define MTXLightFrustum C_MTXLightFrustum 77 | #define MTXLightPerspective C_MTXLightPerspective 78 | #define MTXLightOrtho C_MTXLightOrtho 79 | #define MTXMultVec PSMTXMultVec 80 | #define MTXFrustum C_MTXFrustum 81 | #define MTXPerspective C_MTXPerspective 82 | #define MTXOrtho C_MTXOrtho 83 | #define VECAdd PSVECAdd 84 | #define VECSubtract PSVECSubtract 85 | #define VECScale PSVECScale 86 | #define VECNormalize PSVECNormalize 87 | #define VECMag PSVECMag 88 | #define VECDotProduct PSVECDotProduct 89 | #define VECCrossProduct PSVECCrossProduct 90 | #define VECHalfAngle C_VECHalfAngle 91 | #define VECSquareDistance PSVECSquareDistance 92 | #define QUATMtx C_QUATMtx 93 | #define QUATSlerp C_QUATSlerp 94 | 95 | // TODO: GX, CX, IOS ... and then of course NW4R 96 | 97 | #ifdef __cplusplus 98 | } 99 | #endif 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /k_stdlib/egg.h: -------------------------------------------------------------------------------- 1 | #ifndef __KAMEK_EGG_H 2 | #define __KAMEK_EGG_H 3 | 4 | #include "kamek.h" 5 | #include "nw4r.h" 6 | 7 | namespace EGG { 8 | class Heap; 9 | 10 | class Disposer { 11 | public: 12 | Disposer(); 13 | virtual ~Disposer(); 14 | 15 | private: 16 | Heap *heap; 17 | }; 18 | 19 | class Allocator { 20 | private: 21 | u8 _unk00[0x10]; 22 | 23 | public: 24 | Allocator(Heap *heap, long alignment); 25 | virtual ~Allocator(); 26 | virtual void *alloc(unsigned long size); 27 | virtual void free(void *buffer); 28 | 29 | private: 30 | Heap *heap; 31 | long alignment; 32 | }; 33 | 34 | class Heap : public Disposer { 35 | public: 36 | Heap(/*MEMiHeapHead*/ void *rvlHeap); 37 | virtual ~Heap(); 38 | virtual int getHeapKind() const = 0; 39 | virtual void initAllocator(Allocator *allocator, long alignment) = 0; 40 | virtual void *alloc(unsigned long size, long alignment) = 0; 41 | virtual void free(void *buffer) = 0; 42 | virtual void destroy() = 0; 43 | virtual unsigned long resizeForMBlock(void *buffer, u32 newSize) = 0; 44 | virtual u32 getTotalFreeSize() = 0; 45 | virtual u32 getAllocatableSize(long alignment) = 0; 46 | virtual u32 adjust() = 0; 47 | 48 | static void *alloc(unsigned long size, int align, Heap *heap); 49 | static Heap *findHeap(/*MEMiHeapHead*/ void *rvlHeap); 50 | Heap *findParentHeap(); 51 | static Heap *findContainHeap(void *buffer); 52 | static void free(void *buffer, Heap *heap); 53 | void dispose(); 54 | void dump(); 55 | static void dumpAll(); 56 | Heap *becomeCurrentHeap(); 57 | Heap *becomeCurrentHeapWithoutLocking(); 58 | 59 | private: 60 | nw4r::ut::Link parentLink; 61 | void *rvlHeap; 62 | void *parentHeapMBlock; 63 | Heap *parentHeap; 64 | u16 dameFlag; 65 | nw4r::ut::Link globalLink; 66 | nw4r::ut::List childList; 67 | const char *name; 68 | }; 69 | 70 | class MsgRes { 71 | private: 72 | const u8 *bmg, *INF1, *DAT1, *STR1, *MID1, *FLW1, *FLI1; 73 | public: 74 | MsgRes(const u8 *bmgFile, u32 unusedParam); 75 | virtual ~MsgRes(); 76 | 77 | static void parseFormatCode(wchar_t initialTag, const wchar_t *string, u8 *outArgsSize, u32 *outCmd, const wchar_t **args); 78 | 79 | const wchar_t *findStringForMessageID(int category, int message) const; 80 | 81 | private: 82 | void setBMG(const u8 *ptr); 83 | void setINF(const u8 *ptr); 84 | void setDAT(const u8 *ptr); 85 | void setSTR(const u8 *ptr); 86 | void setMID(const u8 *ptr); 87 | void setFLW(const u8 *ptr); 88 | void setFLI(const u8 *ptr); 89 | int identifySectionByMagic(u32 magic) const; 90 | 91 | protected: 92 | struct INFEntry { 93 | u32 stringOffset; 94 | }; 95 | const INFEntry *findINFForMessageID(int category, int message) const; 96 | u32 getEntryFromMID(int index) const; 97 | }; 98 | } 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /k_stdlib/kamek.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Kamek Standard Library 3 | * Wii game patching engine 4 | * (c) Treeki 2010-2018 5 | */ 6 | 7 | #ifndef __KAMEK_H 8 | #define __KAMEK_H 9 | 10 | #ifndef __MWERKS__ 11 | #error "Kamek requires the CodeWarrior compiler!" 12 | #endif 13 | 14 | #define NULL 0 15 | 16 | typedef unsigned char u8; 17 | typedef unsigned short u16; 18 | typedef unsigned int u32; 19 | typedef unsigned long long u64; 20 | typedef signed char s8; 21 | typedef signed short s16; 22 | typedef signed int s32; 23 | typedef signed long long s64; 24 | typedef float f32; 25 | typedef double f64; 26 | typedef volatile s64 vs64; 27 | typedef volatile s32 vs32; 28 | typedef volatile s16 vs16; 29 | typedef volatile s8 vs8; 30 | typedef volatile u64 vu64; 31 | typedef volatile u32 vu32; 32 | typedef volatile u16 vu16; 33 | typedef volatile u8 vu8; 34 | typedef volatile f64 vf64; 35 | typedef volatile f32 vf32; 36 | 37 | typedef unsigned long size_t; 38 | 39 | #include "base/hooks.h" 40 | #include "base/c_stdlib.h" 41 | #include "base/rvl_sdk.h" 42 | 43 | #endif 44 | 45 | -------------------------------------------------------------------------------- /k_stdlib/kamek_asm.S: -------------------------------------------------------------------------------- 1 | kctWrite .equ 1 2 | kctConditionalWrite .equ 2 3 | kctInjectBranch .equ 3 4 | kctInjectCall .equ 4 5 | kctPatchExit .equ 5 6 | 7 | // general hook definition macros 8 | kmHook0: .macro type 9 | .section .kamek 10 | _kHook\@: .long 0, type 11 | .previous 12 | .section .discard 13 | li r0, (_kHook\@)@l // reference to prevent CodeWarrior from deleting the symbol 14 | .previous 15 | .endm 16 | kmHook1: .macro type, arg0 17 | .section .kamek 18 | _kHook\@: .long 1, type, arg0 19 | .previous 20 | .section .discard 21 | li r0, (_kHook\@)@l // reference to prevent CodeWarrior from deleting the symbol 22 | .previous 23 | .endm 24 | kmHook2: .macro type, arg0, arg1 25 | .section .kamek 26 | _kHook\@: .long 2, type, arg0, arg1 27 | .previous 28 | .section .discard 29 | li r0, (_kHook\@)@l // reference to prevent CodeWarrior from deleting the symbol 30 | .previous 31 | .endm 32 | kmHook3: .macro type, arg0, arg1, arg2 33 | .section .kamek 34 | _kHook\@: .long 3, type, arg0, arg1, arg2 35 | .previous 36 | .section .discard 37 | li r0, (_kHook\@)@l // reference to prevent CodeWarrior from deleting the symbol 38 | .previous 39 | .endm 40 | kmHook4: .macro type, arg0, arg1, arg2, arg3 41 | .section .kamek 42 | _kHook\@: .long 4, type, arg0, arg1, arg2, arg3 43 | .previous 44 | .section .discard 45 | li r0, (_kHook\@)@l // reference to prevent CodeWarrior from deleting the symbol 46 | .previous 47 | .endm 48 | 49 | // kmCondWrite 50 | // Write value to address, conditionally 51 | kmCondWritePointer: .macro addr, original, value 52 | kmHook4 kctConditionalWrite, 1, addr, value, original 53 | .endm 54 | kmCondWrite32: .macro addr, original, value 55 | kmHook4 kctConditionalWrite, 2, addr, value, original 56 | .endm 57 | kmCondWrite16: .macro addr, original, value 58 | kmHook4 kctConditionalWrite, 3, addr, value, original 59 | .endm 60 | kmCondWrite8: .macro addr, original, value 61 | kmHook4 kctConditionalWrite, 4, addr, value, original 62 | .endm 63 | 64 | // kmWrite 65 | // Write value to address 66 | kmWritePointer: .macro addr, ptr 67 | kmHook3 kctWrite, 1, addr, ptr 68 | .endm 69 | kmWrite32: .macro addr, value 70 | kmHook3 kctWrite, 2, addr, value 71 | .endm 72 | kmWrite16: .macro addr, value 73 | kmHook3 kctWrite, 3, addr, value 74 | .endm 75 | kmWrite8: .macro addr, value 76 | kmHook3 kctWrite, 4, addr, value 77 | .endm 78 | 79 | // kmBranch, kmCall 80 | // Set up a branch from a specific instruction to a specific address 81 | kmBranch: .macro addr, ptr 82 | kmHook2 kctInjectBranch, addr, ptr 83 | .endm 84 | kmCall: .macro addr, ptr 85 | kmHook2 kctInjectCall, addr, ptr 86 | .endm 87 | 88 | // kmBranchDef 89 | // Set up a branch (b) from a specific instruction to a function defined 90 | // directly underneath. 91 | kmBranchDef: .macro addr 92 | kmBranch addr, __kUserFuncBranch\@ 93 | __kUserFuncBranch\@: 94 | .endm 95 | 96 | // kmCallDef 97 | // Set up a call (bl) from a specific instruction to a function defined 98 | // directly underneath. 99 | kmCallDef: .macro addr 100 | kmCall addr, __kUserFuncCall\@ 101 | __kUserFuncCall\@: 102 | .endm 103 | 104 | // kamek_b, kamek_bl 105 | // Branch to or call a direct code address from the original game executable. 106 | // This allows Kamek's address mapping functionality to be used without 107 | // having to add single-use externals. 108 | kamek_b: .macro addr 109 | .extern __kAutoMap_&&addr 110 | b __kAutoMap_&&addr 111 | .endm 112 | kamek_bl: .macro addr 113 | .extern __kAutoMap_&&addr 114 | bl __kAutoMap_&&addr 115 | .endm 116 | 117 | // kamek_ref_address 118 | // Load a direct data address from the original game executable. 119 | // This allows Kamek's address mapping functionality to be used without 120 | // having to add single-use externals. 121 | // Example: 122 | // Set r3 to this address (mapped): 123 | // kamek_ref_address r3, 0x8098DC28 124 | kamek_ref_address: .macro output_reg, addr 125 | .extern __kAutoMap_&&addr 126 | lis output_reg, __kAutoMap_&&addr@ha 127 | addi output_reg, output_reg, __kAutoMap_&&addr@l 128 | .endm 129 | 130 | // kamek_ref 131 | // Reference something at a direct address from the original executable. 132 | // This requires you to supply a temporary register which you know is 133 | // safe to overwrite. 134 | // When loading an integer, you can supply the same output_reg and temp_reg. 135 | // 136 | // Examples: 137 | // Load the 32-bit value at this address into r3: 138 | // kamek_ref lwz, r3, 0x8042A4A8, r3 139 | // Store r7 as a byte at this address, using r10 as a temporary: 140 | // kamek_ref stb, r7, 0x80B07DD0, r10 141 | // Load or store the 32-bit float at this address into f1, using r10 as a temporary: 142 | // kamek_ref f1, 0x80AD3400, r10 143 | kamek_ref: .macro insn, output_reg, addr, temp_reg 144 | .extern __kAutoMap_&&addr 145 | lis temp_reg, __kAutoMap_&&addr@ha 146 | insn output_reg, __kAutoMap_&&addr@l(temp_reg) 147 | .endm 148 | 149 | // kamek_const_float 150 | // Load a constant float. 151 | // This requires a temporary non-float register. 152 | kamek_const_float: .macro output_reg, float_value, temp_reg 153 | .data 154 | __kConstFloat\@: .float float_value 155 | .previous 156 | lis temp_reg, (__kConstFloat\@)@ha 157 | lfs output_reg, (__kConstFloat\@)@l(temp_reg) 158 | .endm 159 | 160 | 161 | // Default to .text so that Kamek macros work 162 | .text 163 | 164 | -------------------------------------------------------------------------------- /k_stdlib/nw4r.h: -------------------------------------------------------------------------------- 1 | #ifndef __KAMEK_NW4R_H 2 | #define __KAMEK_NW4R_H 3 | 4 | #include "kamek.h" 5 | 6 | namespace nw4r { 7 | namespace ut { 8 | struct Color { 9 | union { 10 | struct { u8 r,g,b,a; }; 11 | u32 rgba; 12 | }; 13 | }; 14 | 15 | 16 | struct Link { 17 | void *prev, *next; 18 | }; 19 | struct List { 20 | Link *head, *tail; 21 | u16 count, offset; 22 | }; 23 | 24 | void List_Init(List *list, u16 offset); 25 | void List_Append(List *list, void *newObj); 26 | void List_Insert(List *list, void *insertBefore, void *newObj); 27 | void List_Remove(List *list, void *obj); 28 | void *List_GetNext(List *list, void *obj); 29 | void *List_GetPrev(List *list, void *obj); 30 | 31 | class LinkListNode { 32 | public: 33 | LinkListNode *next; 34 | LinkListNode *prev; 35 | }; 36 | 37 | class LinkList { 38 | public: 39 | int count; 40 | LinkListNode initialNode; 41 | }; 42 | 43 | // TODO: Character/text/font utility classes 44 | template 45 | class TagProcessorBase { }; 46 | } 47 | 48 | 49 | namespace db { 50 | // prints message to NW4R console 51 | void Exception_Printf_(const char *, ...); 52 | } 53 | 54 | 55 | namespace math { 56 | namespace detail { 57 | f32 FExp(f32); 58 | f32 FLog(f32); 59 | } 60 | 61 | inline f32 FExp(f32 value) { return detail::FExp(value); } 62 | inline f32 FLog(f32 value) { return (value > 0.0f) ? detail::FLog(value) : NAN; } 63 | f32 FrSqrt(f32); 64 | 65 | // TODO: inline Idx (rather than FIdx) versions of Sin, Cos, SinCos 66 | f32 SinFIdx(f32 fidx); 67 | f32 CosFIdx(f32 fidx); 68 | void SinCosFIdx(f32 *pSin, f32 *pCos, f32 fidx); 69 | f32 Atan2FIdx(f32 y, f32 x); 70 | u16 Atan2Idx(f32 y, f32 x); 71 | 72 | 73 | struct VEC2 : public Vec2 { }; 74 | struct VEC3 : public Vec { 75 | void Report(bool addNewLine, const char *prefixText = 0); 76 | }; 77 | 78 | struct MTX33 { f32 data[3][3]; }; 79 | struct MTX34 { f32 data[3][4]; }; 80 | struct MTX44 { f32 data[4][4]; }; 81 | 82 | void MTX33Identity(MTX33 *mtx); 83 | void MTX34ToMTX33(MTX33 *dest, const MTX34 *source); 84 | void MTX34Zero(MTX34 *mtx); 85 | void MTX34Add(MTX34 *dest, const MTX34 *sourceA, const MTX34 *sourceB); 86 | void MTX34Scale(MTX34 *dest, const MTX34 *sourceMatrix, const VEC3 *sourceVector); 87 | void MTX34Trans(MTX34 *dest, const MTX34 *sourceMatrix, const VEC3 *sourceVector); 88 | void MTX34RotAxisFIdx(MTX34 *dest, const VEC3 *vector, float fidx); 89 | void MTX34RotXYZFIdx(MTX34 *dest, float x, float y, float z); 90 | void VEC3TransformNormal(VEC3 *dest, const MTX34 *sourceMatrix, const VEC3 *sourceVector); 91 | void MTX44Identity(MTX44 *mtx); 92 | void MTX44Copy(MTX44 *dest, const MTX44 *source); 93 | 94 | struct PLANE { 95 | VEC3 vector; 96 | f32 constant; 97 | 98 | void Set(const VEC3 *a, const VEC3 *b, const VEC3 *c); 99 | }; 100 | 101 | struct AABB { 102 | VEC3 minPoint, maxPoint; 103 | 104 | void Set(const VEC3 *pointArray, u32 pointCount); 105 | void Set(const AABB *otherAABB, const MTX34 *matrix); 106 | }; 107 | 108 | struct FRUSTUM { 109 | MTX34 matrix; 110 | PLANE left, right, top, bottom; 111 | f32 near, far; 112 | AABB aabb; 113 | PLANE planes[6]; 114 | 115 | void Set(f32 fovy, f32 aspect, f32 near, f32 far, const MTX34 &matrix); 116 | void Set(f32 top, f32 bottom, f32 left, f32 right, f32 near, f32 far, const MTX34 &matrix); 117 | int IntersectAABB_Ex(const AABB *otherAabb); 118 | }; 119 | 120 | struct SEGMENT3 { VEC3 a, b; }; 121 | struct LINE3 { VEC3 point, direction; }; 122 | struct SPHERE { VEC3 point; f32 radius; }; 123 | 124 | f32 DistSqSegment3ToSegment3(const SEGMENT3 *a, const SEGMENT3 *b, f32 *pOut1, f32 *pOut2); 125 | int IntersectionLine3Sphere(const LINE3 *a, const SPHERE *b, f32 *pOut1, f32 *pOut2); 126 | int IntersectionSegment3Sphere(const SEGMENT3 *a, const SPHERE *b, f32 *pOut1, f32 *pOut2); 127 | bool IntersectionAABB(const AABB *a, const AABB *b); 128 | } 129 | 130 | 131 | namespace lyt { 132 | class Pane; 133 | class DrawInfo; 134 | 135 | class AnimTransform; 136 | class AnimResource; 137 | class AnimationLink; 138 | class ResourceAccessor; 139 | class Group; 140 | class GroupContainer; 141 | 142 | namespace detail { 143 | class TexCoordAry { 144 | public: 145 | TexCoordAry(); 146 | void Free(); 147 | void Reserve(u8 count); 148 | void SetSize(u8 count); 149 | void Copy(const void *source, u8 count); 150 | 151 | u8 reservedSize, usedSize; 152 | void *data; 153 | }; 154 | } 155 | 156 | class Layout { 157 | public: 158 | Layout(); 159 | virtual ~Layout(); 160 | 161 | virtual bool Build(const void *data, ResourceAccessor *resAcc); 162 | 163 | virtual AnimTransform *CreateAnimTransform(); 164 | virtual AnimTransform *CreateAnimTransform(const void *data, ResourceAccessor *resAcc); 165 | virtual AnimTransform *CreateAnimTransform(const AnimResource &res, ResourceAccessor *resAcc); 166 | 167 | virtual void BindAnimation(AnimTransform *anim); 168 | virtual void UnbindAnimation(AnimTransform *anim); 169 | virtual void UnbindAllAnimation(); 170 | virtual bool BindAnimationAuto(const AnimResource &res, ResourceAccessor *resAcc); 171 | 172 | virtual void SetAnimationEnable(AnimTransform *anim, bool unk); 173 | 174 | virtual void CalculateMtx(const DrawInfo &info); 175 | 176 | virtual void/*?*/ Draw(const DrawInfo &info); 177 | virtual void/*?*/ Animate(ulong flag); 178 | 179 | virtual void/*?*/ SetTagProcessor(ut::TagProcessorBase *tagProc); 180 | 181 | ut::LinkList animations; 182 | 183 | Pane *rootPane; 184 | GroupContainer *groupContainer; 185 | 186 | float width; 187 | float height; 188 | }; 189 | 190 | 191 | class DrawInfo { 192 | public: 193 | DrawInfo(); 194 | virtual ~DrawInfo(); 195 | 196 | Mtx matrix; 197 | float left; 198 | float top; 199 | float right; 200 | float bottom; 201 | float scaleX; 202 | float scaleY; 203 | float alpha; 204 | u8 _50; // this is actually a bitfield. todo: investigate how CW handles bitfields, and so on 205 | }; 206 | 207 | 208 | class TexMap { 209 | public: 210 | void *image, *palette; 211 | u16 width, height; 212 | f32 minLOD, magLOD; 213 | u16 lodBias, palEntryNum; 214 | u32 settingsBitfield; 215 | 216 | int getFormat() { return (settingsBitfield >> 28); } 217 | void setFormat(int fmt) { 218 | settingsBitfield = (fmt << 28) | (settingsBitfield & 0xFFFFFFF); 219 | } 220 | 221 | void ReplaceImage(TPLPalette *tpl, unsigned long id); 222 | }; 223 | 224 | class Material { 225 | public: 226 | virtual ~Material(); 227 | 228 | // cheating a bit here 229 | u8 _[0x3C]; 230 | // this is actually a pointer to more stuff, not just texmaps 231 | TexMap *texMaps; 232 | }; 233 | 234 | class Pane { 235 | public: 236 | //Pane(nw4r::lyt::res::Pane const *); // todo: this struct 237 | Pane(void *); 238 | virtual ~Pane(); 239 | 240 | virtual void *GetRuntimeTypeInfo() const; 241 | virtual void CalculateMtx(const DrawInfo &info); 242 | 243 | virtual void Draw(const DrawInfo &info); 244 | virtual void DrawSelf(const DrawInfo &info); 245 | virtual void Animate(ulong flag); 246 | virtual void AnimateSelf(ulong flag); 247 | 248 | virtual ut::Color GetVtxColor(ulong id) const; 249 | virtual void SetVtxColor(ulong id, ut::Color color); 250 | virtual uchar GetColorElement(ulong id) const; 251 | virtual void SetColorElement(ulong id, uchar value); 252 | virtual uchar GetVtxColorElement(ulong id) const; 253 | virtual void SetVtxColorElement(ulong id, uchar value); 254 | 255 | virtual Pane *FindPaneByName(const char *name, bool recursive); 256 | virtual Material *FindMaterialByName(const char *name, bool recursive); 257 | 258 | virtual void/*?*/ BindAnimation(AnimTransform *anim, bool unk1, bool unk2); 259 | virtual void UnbindAnimation(AnimTransform *anim, bool unk); 260 | virtual void UnbindAllAnimation(bool unk); 261 | virtual void UnbindAnimationSelf(AnimTransform *anim); 262 | 263 | virtual ut::LinkListNode *FindAnimationLinkSelf(AnimTransform *anim); 264 | virtual ut::LinkListNode *FindAnimationLinkSelf(const AnimResource &anim); 265 | 266 | virtual void SetAnimationEnable(AnimTransform *anim, bool unk1, bool unk2); 267 | virtual void SetAnimationEnable(const AnimResource &anim, bool unk1, bool unk2); 268 | 269 | virtual ulong GetMaterialNum() const; 270 | virtual Material *GetMaterial() const; 271 | virtual Material *GetMaterial(ulong id) const; 272 | 273 | virtual void LoadMtx(const DrawInfo &info); 274 | 275 | void AppendChild(Pane *child); 276 | 277 | ut::Rect GetPaneRect(const DrawInfo &info) const; 278 | 279 | ut::LinkListNode *AddAnimationLink(AnimationLink *link); 280 | 281 | Vec2 GetVtxPos() const; 282 | 283 | u16 GetExtUserDataNum() const; // 802AC5A0 284 | 285 | 286 | ut::LinkListNode parentLink; 287 | Pane *parent; 288 | 289 | ut::LinkList children; 290 | ut::LinkList animations; 291 | 292 | Material *material; 293 | 294 | Vec trans; 295 | Vec rotate; 296 | Vec2 scale; 297 | Vec2 size; 298 | 299 | Mtx calcMtx; 300 | Mtx effectiveMtx; 301 | 302 | float _B4; 303 | 304 | u8 alpha; 305 | u8 effectiveAlpha; 306 | u8 origin; 307 | u8 flag; 308 | 309 | char name[0x11]; 310 | char userdata[9]; 311 | 312 | u8 paneIsOwnedBySomeoneElse; 313 | u8 _D7; 314 | 315 | void SetVisible(bool value) { 316 | if (value) 317 | flag |= 1; 318 | else 319 | flag &= ~1; 320 | } 321 | }; 322 | 323 | class TextBox : public Pane { 324 | public: 325 | TextBox(void *, void *); // todo: TextBox((res::TextBox const *,ResBlockSet const &)) 326 | ~TextBox(); 327 | 328 | void *GetRuntimeTypeInfo() const; 329 | 330 | void DrawSelf(const DrawInfo &info); 331 | 332 | ut::Color GetVtxColor(ulong id) const; 333 | void SetVtxColor(ulong id, ut::Color color); 334 | uchar GetVtxColorElement(ulong id) const; 335 | void SetVtxColorElement(ulong id, uchar value); 336 | 337 | void LoadMtx(const DrawInfo &info); 338 | 339 | virtual void AllocStringBuffer(u16 size); 340 | virtual void FreeStringBuffer(); 341 | 342 | virtual u16 SetString(const wchar_t *str, u16 destOffset = 0); 343 | virtual u16 SetString(const wchar_t *str, u16 destOffset, u16 length); 344 | 345 | wchar_t *stringBuf; 346 | 347 | ut::Color colour1, colour2; 348 | void *font; // actually a ut::ResFont or whatever 349 | 350 | float fontSizeX, fontSizeY; 351 | float lineSpace, charSpace; 352 | 353 | void *tagProc; // actually a TagProcessor 354 | 355 | u16 bufferLength; 356 | u16 stringLength; 357 | 358 | u8 alignment; 359 | u8 flags; 360 | }; 361 | 362 | class Picture : public Pane { 363 | public: 364 | Picture(void *, void *); // todo: Picture((res::Picture const *,ResBlockSet const &)) 365 | ~Picture(); 366 | 367 | void *GetRuntimeTypeInfo() const; 368 | 369 | void DrawSelf(const DrawInfo &info); 370 | 371 | ut::Color GetVtxColor(ulong id) const; 372 | void SetVtxColor(ulong id, ut::Color color); 373 | uchar GetVtxColorElement(ulong id) const; 374 | void SetVtxColorElement(ulong id, uchar value); 375 | 376 | virtual void Append(const GXTexObj &obj); 377 | 378 | ut::Color colours[4]; 379 | detail::TexCoordAry texCoords; 380 | }; 381 | } 382 | 383 | } 384 | 385 | #endif 386 | -------------------------------------------------------------------------------- /loader/README.md: -------------------------------------------------------------------------------- 1 | # The Kamek loader 2 | 3 | *kamekLoader.cpp* in this directory is the runtime loader for the "Kamekfiles" that Kamek generates in `-dynamic` mode. The loader itself is built in *static* mode, since it needs to either be applied directly to a DOL file or built as a Riivolution memory patch. 4 | 5 | Although the core loader code should work with most games, some game-specific code (a "bootstrap") is needed to invoke it. An example bootstrap for New Super Mario Bros. Wii is included. 6 | 7 | This guide assumes you want to build the loader as a Riivolution patch, since that's the most common method by far. If you want to inject it directly into a DOL instead, use the `-input-dol` and `-output-dol` Kamek arguments instead of `-output-code` and `-output-riiv`. 8 | 9 | ## kamekLoader.cpp 10 | 11 | This is the "core" part of the loader, which should work for most games. 12 | 13 | The main API entrypoint is `loadKamekBinaryFromDisc()`, which loads a Kamekfile and applies its patches to the game. In addition to the Kamekfile path, it takes a structure of pointers to SDK functions it needs, which allows the bootstrap to determine their addresses at runtime if necessary. 14 | 15 | ## New Super Mario Bros. Wii bootstrap 16 | 17 | *nsmbw.cpp* is the Kamek loader bootstrap for New Super Mario Bros. Wii, bridging the gap between *kamekLoader.cpp* and NSMBW's code. It's designed to support *all* versions of the game by detecting the one it finds itself in at runtime and invoking the loader appropriately. 18 | 19 | Since you may want to adapt this bootstrap to a different game, here's an overview of the most important parts of its code: 20 | 21 | - A set of `kmCondWritePointer()` hooks. These put a `loadIntoNSMBW()` function pointer (see below) into a table of function pointers that the game runs at startup, replacing an entry that (conveniently) originally pointed to an empty function. Multiple conditional hooks are needed since the table's address varies in different game versions. 22 | - A "`loaderFunctionsEx`" struct that wraps the loader's `loaderFunctions` struct and adds some additional pointers useful for the NSMBW bootstrap. 23 | - Some "adapters" that implement the `kamekAlloc()` and `kamekFree()` interfaces using appropriate functions from the game. 24 | - A const `loaderFunctionsEx` instance for each game version, providing all the function addresses as hardcoded values. 25 | - The `loadIntoNSMBW()` function, which: 26 | - Determines the running game version by checking some specific memory addresses, 27 | - Selects the appropriate `loaderFunctions` and Kamekfile path string, and 28 | - Calls `loadKamekBinaryFromDisc()`. 29 | 30 | By default, it loads a Kamekfile from one of the following paths on the disc / in the Riivolution patch, depending on the detected game version: 31 | 32 | - *Code/P1.bin* (international: SMNP v1) 33 | - *Code/P2.bin* (international: SMNP v2) 34 | - *Code/E1.bin* (North American: SMNE v1) 35 | - *Code/E2.bin* (North American: SMNE v2) 36 | - *Code/J1.bin* (Japanese: SMNJ v1) 37 | - *Code/J2.bin* (Japanese: SMNJ v2) 38 | - *Code/K.bin* (South Korean: SMNK) 39 | - *Code/W.bin* (Taiwanese / Hong Kong: SMNW) 40 | 41 | ### Building the bootstrap and loader 42 | 43 | To build the bootstrap and loader, place the CodeWarrior EXEs, DLLs, and *license.dat* in `../cw`, then run one of: 44 | 45 | - `build_nsmbw.bat` (Windows) 46 | - `build_nsmbw.sh` (sh, tries to invoke CodeWarrior without Wine -- appropriate for WSL) 47 | - `build_nsmbw.sh --wine` (sh, uses Wine) 48 | 49 | This will create two files: 50 | 51 | - *loader.bin*: the loader code blob, to be placed at address 0x80001900 (by default). 52 | - *loader.xml*: a **partial** Riivolution XML that includes a reference to *loader.bin* and the game hooks to invoke it. 53 | 54 | Copy the contents of *loader.xml* into your Riivolution XML's `` tag, and put *loader.bin* in your Riivolution patch's *Code* directory alongside your Kamekfiles. 55 | -------------------------------------------------------------------------------- /loader/build_nsmbw.bat: -------------------------------------------------------------------------------- 1 | ..\cw\mwcceppc.exe -i . -I- -i ../k_stdlib -Cpp_exceptions off -enum int -Os -use_lmw_stmw on -fp hard -rostr -sdata 0 -sdata2 0 -c -o kamekLoader.o kamekLoader.cpp 2 | ..\cw\mwcceppc.exe -i . -I- -i ../k_stdlib -Cpp_exceptions off -enum int -Os -use_lmw_stmw on -fp hard -rostr -sdata 0 -sdata2 0 -c -o nsmbw.o nsmbw.cpp 3 | 4 | ..\Kamek\bin\Debug\net6.0\Kamek kamekLoader.o nsmbw.o -static=0x80001900 -output-code=loader.bin -output-riiv=loader.xml -valuefile=Code/loader.bin 5 | 6 | REM Or to inject directly into a DOL: 7 | REM ..\Kamek\bin\Debug\net6.0\Kamek kamekLoader.o nsmbw.o -static=0x80001900 -input-dol=nsmbw.dol -output-dol=nsmbw_kamek.dol 8 | 9 | -------------------------------------------------------------------------------- /loader/build_nsmbw.sh: -------------------------------------------------------------------------------- 1 | if [ "$1" = "--wine" ]; then 2 | CC='wine ../cw/mwcceppc.exe' 3 | else 4 | CC='../cw/mwcceppc.exe' 5 | fi 6 | 7 | $CC -i . -I- -i ../k_stdlib -Cpp_exceptions off -enum int -Os -use_lmw_stmw on -fp hard -rostr -sdata 0 -sdata2 0 -c -o kamekLoader.o kamekLoader.cpp 8 | $CC -i . -I- -i ../k_stdlib -Cpp_exceptions off -enum int -Os -use_lmw_stmw on -fp hard -rostr -sdata 0 -sdata2 0 -c -o nsmbw.o nsmbw.cpp 9 | 10 | ../Kamek/bin/Debug/net6.0/Kamek kamekLoader.o nsmbw.o -static=0x80001900 -output-code=loader.bin -output-riiv=loader.xml -valuefile=Code/loader.bin 11 | 12 | # Or to inject directly into a DOL: 13 | # ../Kamek/bin/Debug/net6.0/Kamek kamekLoader.o nsmbw.o -static=0x80001900 -input-dol=nsmbw.dol -output-dol=nsmbw_kamek.dol 14 | -------------------------------------------------------------------------------- /loader/kamekLoader.cpp: -------------------------------------------------------------------------------- 1 | #include "kamekLoader.h" 2 | 3 | #define KM_FILE_VERSION 2 4 | #define STRINGIFY_(x) #x 5 | #define STRINGIFY(x) STRINGIFY_(x) 6 | 7 | struct KBHeader { 8 | u32 magic1; 9 | u16 magic2; 10 | u16 version; 11 | u32 bssSize; 12 | u32 codeSize; 13 | u32 ctorStart; 14 | u32 ctorEnd; 15 | u32 _pad[2]; 16 | }; 17 | 18 | 19 | #define kAddr32 1 20 | #define kAddr16Lo 4 21 | #define kAddr16Hi 5 22 | #define kAddr16Ha 6 23 | #define kRel24 10 24 | #define kWrite32 32 25 | #define kWrite16 33 26 | #define kWrite8 34 27 | #define kCondWritePointer 35 28 | #define kCondWrite32 36 29 | #define kCondWrite16 37 30 | #define kCondWrite8 38 31 | #define kBranch 64 32 | #define kBranchLink 65 33 | 34 | 35 | void kamekError(const loaderFunctions *funcs, const char *str) 36 | { 37 | u32 fg = 0xFFFFFFFF, bg = 0; 38 | funcs->OSFatal(&fg, &bg, str); 39 | } 40 | 41 | 42 | static inline u32 resolveAddress(u32 text, u32 address) { 43 | if (address & 0x80000000) 44 | return address; 45 | else 46 | return text + address; 47 | } 48 | 49 | 50 | #define kCommandHandler(name) \ 51 | static inline const u8 *kHandle##name(const u8 *input, u32 text, u32 address) 52 | #define kDispatchCommand(name) \ 53 | case k##name: input = kHandle##name(input, text, address); break 54 | 55 | kCommandHandler(Addr32) { 56 | u32 target = resolveAddress(text, *(const u32 *)input); 57 | *(u32 *)address = target; 58 | return input + 4; 59 | } 60 | kCommandHandler(Addr16Lo) { 61 | u32 target = resolveAddress(text, *(const u32 *)input); 62 | *(u16 *)address = target & 0xFFFF; 63 | return input + 4; 64 | } 65 | kCommandHandler(Addr16Hi) { 66 | u32 target = resolveAddress(text, *(const u32 *)input); 67 | *(u16 *)address = target >> 16; 68 | return input + 4; 69 | } 70 | kCommandHandler(Addr16Ha) { 71 | u32 target = resolveAddress(text, *(const u32 *)input); 72 | *(u16 *)address = target >> 16; 73 | if (target & 0x8000) 74 | *(u16 *)address += 1; 75 | return input + 4; 76 | } 77 | kCommandHandler(Rel24) { 78 | u32 target = resolveAddress(text, *(const u32 *)input); 79 | u32 delta = target - address; 80 | *(u32 *)address &= 0xFC000003; 81 | *(u32 *)address |= (delta & 0x3FFFFFC); 82 | return input + 4; 83 | } 84 | kCommandHandler(Write32) { 85 | u32 value = *(const u32 *)input; 86 | *(u32 *)address = value; 87 | return input + 4; 88 | } 89 | kCommandHandler(Write16) { 90 | u32 value = *(const u32 *)input; 91 | *(u16 *)address = value & 0xFFFF; 92 | return input + 4; 93 | } 94 | kCommandHandler(Write8) { 95 | u32 value = *(const u32 *)input; 96 | *(u8 *)address = value & 0xFF; 97 | return input + 4; 98 | } 99 | kCommandHandler(CondWritePointer) { 100 | u32 target = resolveAddress(text, *(const u32 *)input); 101 | u32 original = ((const u32 *)input)[1]; 102 | if (*(u32 *)address == original) 103 | *(u32 *)address = target; 104 | return input + 8; 105 | } 106 | kCommandHandler(CondWrite32) { 107 | u32 value = *(const u32 *)input; 108 | u32 original = ((const u32 *)input)[1]; 109 | if (*(u32 *)address == original) 110 | *(u32 *)address = value; 111 | return input + 8; 112 | } 113 | kCommandHandler(CondWrite16) { 114 | u32 value = *(const u32 *)input; 115 | u32 original = ((const u32 *)input)[1]; 116 | if (*(u16 *)address == (original & 0xFFFF)) 117 | *(u16 *)address = value & 0xFFFF; 118 | return input + 8; 119 | } 120 | kCommandHandler(CondWrite8) { 121 | u32 value = *(const u32 *)input; 122 | u32 original = ((const u32 *)input)[1]; 123 | if (*(u8 *)address == (original & 0xFF)) 124 | *(u8 *)address = value & 0xFF; 125 | return input + 8; 126 | } 127 | kCommandHandler(Branch) { 128 | *(u32 *)address = 0x48000000; 129 | return kHandleRel24(input, text, address); 130 | } 131 | kCommandHandler(BranchLink) { 132 | *(u32 *)address = 0x48000001; 133 | return kHandleRel24(input, text, address); 134 | } 135 | 136 | 137 | inline void cacheInvalidateAddress(u32 address) { 138 | register u32 addressRegister = address; 139 | asm { 140 | dcbst 0, addressRegister 141 | sync 142 | icbi 0, addressRegister 143 | } 144 | } 145 | 146 | 147 | void loadKamekBinary(const loaderFunctions *funcs, const void *binary, u32 binaryLength) 148 | { 149 | const KBHeader *header = (const KBHeader *)binary; 150 | if (header->magic1 != 'Kame' || header->magic2 != 'k\0') 151 | kamekError(funcs, "FATAL ERROR: Corrupted file, please check your game's Kamek files"); 152 | if (header->version != KM_FILE_VERSION) { 153 | char err[512]; 154 | funcs->sprintf(err, "FATAL ERROR: Incompatible file (version %d, expected " STRINGIFY(KM_FILE_VERSION) ");\nplease upgrade your Kamek %s", 155 | header->version, 156 | ((header->version > KM_FILE_VERSION) ? "Loader" : "file")); 157 | kamekError(funcs, err); 158 | } 159 | 160 | funcs->OSReport("header: bssSize=%u, codeSize=%u, ctors=%u-%u\n", 161 | header->bssSize, header->codeSize, header->ctorStart, header->ctorEnd); 162 | 163 | u32 textSize = header->codeSize + header->bssSize; 164 | u32 text = (u32)funcs->kamekAlloc(textSize, true, funcs); 165 | if (!text) 166 | kamekError(funcs, "FATAL ERROR: Out of code memory"); 167 | 168 | const u8 *input = ((const u8 *)binary) + sizeof(KBHeader); 169 | const u8 *inputEnd = ((const u8 *)binary) + binaryLength; 170 | u8 *output = (u8 *)text; 171 | 172 | // Create text + bss sections 173 | for (u32 i = 0; i < header->codeSize; i++){ 174 | *output = *(input++); 175 | cacheInvalidateAddress((u32)(output++)); 176 | } 177 | for (u32 i = 0; i < header->bssSize; i++){ 178 | *output = 0; 179 | cacheInvalidateAddress((u32)(output++)); 180 | } 181 | 182 | while (input < inputEnd) { 183 | u32 cmdHeader = *((u32 *)input); 184 | input += 4; 185 | 186 | u8 cmd = cmdHeader >> 24; 187 | u32 address = cmdHeader & 0xFFFFFF; 188 | if (address == 0xFFFFFE) { 189 | // Absolute address 190 | address = *((u32 *)input); 191 | input += 4; 192 | } else { 193 | // Relative address 194 | address += text; 195 | } 196 | 197 | switch (cmd) { 198 | kDispatchCommand(Addr32); 199 | kDispatchCommand(Addr16Lo); 200 | kDispatchCommand(Addr16Hi); 201 | kDispatchCommand(Addr16Ha); 202 | kDispatchCommand(Rel24); 203 | kDispatchCommand(Write32); 204 | kDispatchCommand(Write16); 205 | kDispatchCommand(Write8); 206 | kDispatchCommand(CondWritePointer); 207 | kDispatchCommand(CondWrite32); 208 | kDispatchCommand(CondWrite16); 209 | kDispatchCommand(CondWrite8); 210 | kDispatchCommand(Branch); 211 | kDispatchCommand(BranchLink); 212 | default: 213 | funcs->OSReport("Unknown command: %d\n", cmd); 214 | } 215 | 216 | cacheInvalidateAddress(address); 217 | } 218 | 219 | __sync(); 220 | __isync(); 221 | 222 | typedef void (*Func)(void); 223 | for (Func* f = (Func*)(text + header->ctorStart); f < (Func*)(text + header->ctorEnd); f++) { 224 | (*f)(); 225 | } 226 | } 227 | 228 | 229 | void loadKamekBinaryFromDisc(const loaderFunctions *funcs, const char *path) 230 | { 231 | funcs->OSReport("{Kamek by Treeki}\nLoading Kamek binary '%s'...\n", path); 232 | 233 | int entrynum = funcs->DVDConvertPathToEntrynum(path); 234 | if (entrynum < 0) { 235 | char err[512]; 236 | funcs->sprintf(err, "FATAL ERROR: Failed to locate file on the disc: %s", path); 237 | kamekError(funcs, err); 238 | } 239 | 240 | DVDHandle handle; 241 | if (!funcs->DVDFastOpen(entrynum, &handle)) 242 | kamekError(funcs, "FATAL ERROR: Failed to open file!"); 243 | 244 | funcs->OSReport("DVD file located: addr=%p, size=%d\n", handle.address, handle.length); 245 | 246 | u32 length = handle.length, roundedLength = (handle.length + 0x1F) & ~0x1F; 247 | void *buffer = funcs->kamekAlloc(roundedLength, false, funcs); 248 | if (!buffer) 249 | kamekError(funcs, "FATAL ERROR: Out of file memory"); 250 | 251 | funcs->DVDReadPrio(&handle, buffer, roundedLength, 0, 2); 252 | funcs->DVDClose(&handle); 253 | 254 | loadKamekBinary(funcs, buffer, handle.length); 255 | 256 | funcs->kamekFree(buffer, false, funcs); 257 | funcs->OSReport("All done!\n"); 258 | } 259 | -------------------------------------------------------------------------------- /loader/kamekLoader.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct DVDHandle 4 | { 5 | u32 _unk[12]; 6 | u32 address, length; 7 | u32 _unk38; 8 | }; 9 | 10 | struct loaderFunctions; 11 | 12 | typedef void (*OSReport_t) (const char *str, ...); 13 | typedef void (*OSFatal_t) (u32 *fg, u32 *bg, const char *str, ...); 14 | typedef int (*DVDConvertPathToEntrynum_t) (const char *path); 15 | typedef bool (*DVDFastOpen_t) (int entrynum, DVDHandle *handle); 16 | typedef int (*DVDReadPrio_t) (DVDHandle *handle, void *buffer, int length, int offset, int unk); 17 | typedef bool (*DVDClose_t) (DVDHandle *handle); 18 | typedef int (*sprintf_t) (char *str, const char *format, ...); 19 | typedef void *(*KamekAlloc_t) (u32 size, bool isForCode, const loaderFunctions *funcs); 20 | typedef void (*KamekFree_t) (void *buffer, bool isForCode, const loaderFunctions *funcs); 21 | 22 | 23 | struct loaderFunctions { 24 | OSReport_t OSReport; 25 | OSFatal_t OSFatal; 26 | DVDConvertPathToEntrynum_t DVDConvertPathToEntrynum; 27 | DVDFastOpen_t DVDFastOpen; 28 | DVDReadPrio_t DVDReadPrio; 29 | DVDClose_t DVDClose; 30 | sprintf_t sprintf; 31 | KamekAlloc_t kamekAlloc; 32 | KamekFree_t kamekFree; 33 | }; 34 | 35 | void loadKamekBinaryFromDisc(const loaderFunctions *funcs, const char *path); 36 | 37 | -------------------------------------------------------------------------------- /loader/nsmbw.cpp: -------------------------------------------------------------------------------- 1 | #include "kamekLoader.h" 2 | 3 | int loadIntoNSMBW(); 4 | kmCondWritePointer(0x80328478, 0x8015BC60, loadIntoNSMBW); // EU 5 | kmCondWritePointer(0x80328130, 0x8015BB20, loadIntoNSMBW); // US 6 | kmCondWritePointer(0x80327E98, 0x8015B930, loadIntoNSMBW); // JP 7 | kmCondWritePointer(0x80334E60, 0x8015C060, loadIntoNSMBW); // KR 8 | kmCondWritePointer(0x80333218, 0x8015C060, loadIntoNSMBW); // TW 9 | 10 | typedef void *(*EGG_Heap_Alloc_t) (u32 size, s32 align, void *heap); 11 | typedef void (*EGG_Heap_Free_t) (void *buffer, void *heap); 12 | 13 | struct loaderFunctionsEx { 14 | loaderFunctions base; 15 | EGG_Heap_Alloc_t eggAlloc; 16 | EGG_Heap_Free_t eggFree; 17 | void **gameHeapPtr; 18 | void **archiveHeapPtr; 19 | }; 20 | 21 | void *allocAdapter(u32 size, bool isForCode, const loaderFunctions *funcs) { 22 | const loaderFunctionsEx *funcsEx = (const loaderFunctionsEx *)funcs; 23 | void **heapPtr = isForCode ? funcsEx->gameHeapPtr : funcsEx->archiveHeapPtr; 24 | return funcsEx->eggAlloc(size, 0x20, *heapPtr); 25 | } 26 | void freeAdapter(void *buffer, bool isForCode, const loaderFunctions *funcs) { 27 | const loaderFunctionsEx *funcsEx = (const loaderFunctionsEx *)funcs; 28 | void **heapPtr = isForCode ? funcsEx->gameHeapPtr : funcsEx->archiveHeapPtr; 29 | funcsEx->eggFree(buffer, *heapPtr); 30 | } 31 | 32 | 33 | const loaderFunctionsEx functions_p = { 34 | {(OSReport_t) 0x8015F870, 35 | (OSFatal_t) 0x801AF710, 36 | (DVDConvertPathToEntrynum_t) 0x801CA7C0, 37 | (DVDFastOpen_t) 0x801CAAD0, 38 | (DVDReadPrio_t) 0x801CAC60, 39 | (DVDClose_t) 0x801CAB40, 40 | (sprintf_t) 0x802E1ACC, 41 | allocAdapter, 42 | freeAdapter}, 43 | (EGG_Heap_Alloc_t) 0x802B8E00, 44 | (EGG_Heap_Free_t) 0x802B90B0, 45 | (void **) 0x80377F48, 46 | (void **) 0x8042A72C 47 | }; 48 | const loaderFunctionsEx functions_e = { 49 | {(OSReport_t) 0x8015F730, 50 | (OSFatal_t) 0x801AF5D0, 51 | (DVDConvertPathToEntrynum_t) 0x801CA680, 52 | (DVDFastOpen_t) 0x801CA990, 53 | (DVDReadPrio_t) 0x801CAB20, 54 | (DVDClose_t) 0x801CAA00, 55 | (sprintf_t) 0x802E17DC, 56 | allocAdapter, 57 | freeAdapter}, 58 | (EGG_Heap_Alloc_t) 0x802B8CC0, 59 | (EGG_Heap_Free_t) 0x802B8F70, 60 | (void **) 0x80377C48, 61 | (void **) 0x8042A44C 62 | }; 63 | const loaderFunctionsEx functions_j = { 64 | {(OSReport_t) 0x8015F540, 65 | (OSFatal_t) 0x801AF3E0, 66 | (DVDConvertPathToEntrynum_t) 0x801CA490, 67 | (DVDFastOpen_t) 0x801CA7A0, 68 | (DVDReadPrio_t) 0x801CA930, 69 | (DVDClose_t) 0x801CA810, 70 | (sprintf_t) 0x802E15EC, 71 | allocAdapter, 72 | freeAdapter}, 73 | (EGG_Heap_Alloc_t) 0x802B8AD0, 74 | (EGG_Heap_Free_t) 0x802B8D80, 75 | (void **) 0x803779C8, 76 | (void **) 0x8042A16C 77 | }; 78 | const loaderFunctionsEx functions_k = { 79 | {(OSReport_t) 0x8015FC70, 80 | (OSFatal_t) 0x801AFB10, 81 | (DVDConvertPathToEntrynum_t) 0x801CABC0, 82 | (DVDFastOpen_t) 0x801CAED0, 83 | (DVDReadPrio_t) 0x801CB060, 84 | (DVDClose_t) 0x801CAF40, 85 | (sprintf_t) 0x802E1D1C, 86 | allocAdapter, 87 | freeAdapter}, 88 | (EGG_Heap_Alloc_t) 0x802B9200, 89 | (EGG_Heap_Free_t) 0x802B94B0, 90 | (void **) 0x80384948, 91 | (void **) 0x804370EC 92 | }; 93 | const loaderFunctionsEx functions_w = { 94 | {(OSReport_t) 0x8015FC70, 95 | (OSFatal_t) 0x801AFB10, 96 | (DVDConvertPathToEntrynum_t) 0x801CABC0, 97 | (DVDFastOpen_t) 0x801CAED0, 98 | (DVDReadPrio_t) 0x801CB060, 99 | (DVDClose_t) 0x801CAF40, 100 | (sprintf_t) 0x802E1D1C, 101 | allocAdapter, 102 | freeAdapter}, 103 | (EGG_Heap_Alloc_t) 0x802B9200, 104 | (EGG_Heap_Free_t) 0x802B94B0, 105 | (void **) 0x80382D48, 106 | (void **) 0x804354EC 107 | }; 108 | 109 | void unknownVersion() 110 | { 111 | // can't do much here! 112 | // we can't output a message on screen without a valid OSFatal addr; 113 | // all we can really do is set the screen to solid red before we die 114 | // (note: Dolphin Emulator currently ignores this) 115 | unsigned int *HW_VISOLID = (unsigned int*)0xcd000024; 116 | *HW_VISOLID = 0x5aef5101; 117 | 118 | for (;;); 119 | } 120 | 121 | int loadIntoNSMBW() 122 | { 123 | int version = 0, region = 0; 124 | 125 | switch (*((u32*)0x800CF6CC)) 126 | { 127 | case 0x40820030: region = 'P'; version = 1; break; 128 | case 0x40820038: region = 'P'; version = 2; break; 129 | case 0x48000465: region = 'E'; version = 1; break; 130 | case 0x2C030000: region = 'E'; version = 2; break; 131 | case 0x480000B4: region = 'J'; version = 1; break; 132 | case 0x4082000C: region = 'J'; version = 2; break; 133 | case 0x38A00001: 134 | switch (*((u8*)0x8000423A)) 135 | { 136 | case 0xC8: region = 'K'; break; 137 | case 0xAC: region = 'W'; break; 138 | default: unknownVersion(); 139 | } 140 | break; 141 | default: unknownVersion(); 142 | } 143 | 144 | 145 | // choose functions 146 | // (these are all the same in v1 and v2, thankfully) 147 | const loaderFunctions *funcs = NULL; 148 | switch (region) 149 | { 150 | case 'P': funcs = &functions_p.base; break; 151 | case 'E': funcs = &functions_e.base; break; 152 | case 'J': funcs = &functions_j.base; break; 153 | case 'K': funcs = &functions_k.base; break; 154 | case 'W': funcs = &functions_w.base; break; 155 | } 156 | 157 | char path[64]; 158 | if (version == 0) 159 | funcs->sprintf(path, "/Code/%c.bin", region); 160 | else 161 | funcs->sprintf(path, "/Code/%c%d.bin", region, version); 162 | loadKamekBinaryFromDisc(funcs, path); 163 | 164 | return 1; 165 | } 166 | 167 | -------------------------------------------------------------------------------- /preproc_demo.cpp: -------------------------------------------------------------------------------- 1 | #include "k_stdlib/kamek.h" 2 | 3 | 4 | kmWrite32(0x80123456, 0x99998888); 5 | kmWrite32(0x80123456, 0x99998888); 6 | kmWrite32(0x80123456, 0x99998888); 7 | kmWrite32(0x80123456, 0x99998888); 8 | kmWrite32(0x80123456, 0x99998888); 9 | kmWrite32(0x80123456, 0x99998888); 10 | kmWrite32(0x80123456, 0x99998888); 11 | kmWrite32(0x80123456, 0x99998888); 12 | kmWrite32(0x80123456, 0x99998888); 13 | 14 | kmWrite32(0x23232323, "beep"); 15 | 16 | extern int foo(); 17 | 18 | int multiplier(int a, int b) 19 | { 20 | return a * b; 21 | } 22 | 23 | int foo_added() 24 | { 25 | return foo() + foo(); 26 | } 27 | 28 | 29 | int namedFn(int value) 30 | { 31 | foo(); 32 | return value; 33 | } 34 | kmBranch(0x80B6C488, namedFn); 35 | 36 | kmBranchDefCpp(0x80B6C488, NULL, int, int state) 37 | { 38 | foo(); 39 | foo(); 40 | foo(); 41 | return state; 42 | } 43 | 44 | kmBranchDefAsm(0x80B6C488, 0x80B6C48C) 45 | { 46 | li r5, 1; 47 | } 48 | 49 | 50 | extern "C" void baz(); 51 | asm void tryMe() 52 | { 53 | li r3, 1; 54 | li r4, 2; 55 | b baz; 56 | } 57 | 58 | kmWrite32(0x64646464, foo_added); 59 | kmWrite32(0x90000000, tryMe); 60 | 61 | -------------------------------------------------------------------------------- /shield-fix/addresses.txt: -------------------------------------------------------------------------------- 1 | normalise__Q23EGG8Vector3fFv=0x802C0D60 2 | strcmp=0x802E50D0 3 | 4 | -------------------------------------------------------------------------------- /shield-fix/build.bat: -------------------------------------------------------------------------------- 1 | ..\cw\mwcceppc.exe -i . -I- -i ../k_stdlib -Cpp_exceptions off -enum int -Os -use_lmw_stmw on -fp hard -rostr -sdata 0 -sdata2 0 -c -o shield-fix.o shield-fix.cpp 2 | ..\Kamek\bin\Debug\net6.0\Kamek shield-fix.o -externals=addresses.txt -static=0x80002000 -output-code=loader.bin -output-riiv=loader.xml -output-gecko=gecko.txt -input-dol=cn_original.dol -output-dol=cn_patched.dol 3 | 4 | 5 | -------------------------------------------------------------------------------- /shield-fix/shield-fix.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | * A tiny patch to make the Nvidia Shield port of NSMBW work on Dolphin. 5 | * 6 | * This may or may not work on a Wii. The sloppy edits Nvidia/Lightspeed did 7 | * to daLiftRemoconSeesaw_c (the player-tiltable platform seen in World 1-2) 8 | * cause the game to read from 0x00000074 when nobody is riding the platform 9 | * as it tries to check the device type of a null pointer. 10 | * 11 | * Amazing. 12 | */ 13 | 14 | namespace EGG { 15 | struct Vector3f { 16 | float x, y, z; 17 | void normalise(); 18 | }; 19 | } 20 | 21 | struct MTX34 { float m[3][4]; }; 22 | 23 | struct DRMStruct { 24 | u32 in_1; 25 | f32 in_screenHeight; 26 | f32 in_screenWidth; 27 | EGG::Vector3f in_camPos; 28 | EGG::Vector3f in_camTarget; 29 | EGG::Vector3f in_camUp; 30 | EGG::Vector3f out_camPos0; 31 | EGG::Vector3f out_camTarget0; 32 | EGG::Vector3f out_camUp0; 33 | EGG::Vector3f out_camPos1; 34 | EGG::Vector3f out_camTarget1; 35 | EGG::Vector3f out_camUp1; 36 | f32 out_orthoTop; 37 | f32 out_orthoBottom; 38 | f32 out_orthoLeft; 39 | f32 out_orthoRight; 40 | MTX34 out_camMtx0; 41 | MTX34 out_camMtx1; 42 | }; 43 | 44 | void calculateCamera(const EGG::Vector3f &pos, const EGG::Vector3f &target, const EGG::Vector3f &up, MTX34 &mtx) { 45 | EGG::Vector3f a = { pos.x - target.x, pos.y - target.y, pos.z - target.z }; 46 | a.normalise(); 47 | EGG::Vector3f b = { up.y * a.z - up.z * a.y, up.z * a.x - up.x * a.z, up.x * a.y - up.y * a.x }; 48 | b.normalise(); 49 | EGG::Vector3f c = { a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x }; 50 | c.normalise(); 51 | 52 | mtx.m[0][0] = b.x; 53 | mtx.m[0][1] = b.y; 54 | mtx.m[0][2] = b.z; 55 | mtx.m[0][3] = -(b.z * pos.z + b.x * pos.x + b.y * pos.y); 56 | mtx.m[1][0] = c.x; 57 | mtx.m[1][1] = c.y; 58 | mtx.m[1][2] = c.z; 59 | mtx.m[1][3] = -(c.z * pos.z + c.x * pos.x + c.y * pos.y); 60 | mtx.m[2][0] = a.x; 61 | mtx.m[2][1] = a.y; 62 | mtx.m[2][2] = a.z; 63 | mtx.m[2][3] = -(a.z * pos.z + a.x * pos.x + a.y * pos.y); 64 | } 65 | 66 | // lingcod_callNVSISnippet 67 | kmBranchDefCpp(0x80100840, NULL, void, DRMStruct *data) { 68 | // This logic is stripped out of the game and executed by 69 | // the "NVSI" DRM logic in the Lingcod emulator. 70 | // So, we reimplement it here 71 | data->out_camPos0 = data->in_camPos; 72 | data->out_camTarget0 = data->in_camTarget; 73 | data->out_camUp0 = data->in_camUp; 74 | calculateCamera(data->out_camPos0, data->out_camTarget0, data->out_camUp0, data->out_camMtx0); 75 | 76 | f32 y = 0.5f * data->in_screenHeight; 77 | data->out_orthoTop = y; 78 | data->out_orthoBottom = -y; 79 | f32 x = 0.5f * -data->in_screenWidth; 80 | data->out_orthoLeft = x; 81 | data->out_orthoRight = -x; 82 | 83 | data->out_camPos1 = (EGG::Vector3f){ 0.0f, 0.0f, 0.0f }; 84 | data->out_camTarget1 = (EGG::Vector3f){ 0.0f, 0.0f, 0.0f - 100.0f }; 85 | data->out_camUp1 = data->in_camUp; 86 | calculateCamera(data->out_camPos1, data->out_camTarget1, data->out_camUp1, data->out_camMtx1); 87 | } 88 | 89 | // lingcod_getIniState 90 | kmBranchDefCpp(0x801007B0, NULL, bool, char *section, char *key) { 91 | if (strcmp(section, "NSMB") == 0) { 92 | // reference: e_assets/ini/nsmb/nsmb.ini 93 | if (strcmp(key, "AUTO_PILOTING") == 0) return false; 94 | if (strcmp(key, "CREDIT_SCREEN_SHORTCUT") == 0) return false; 95 | } 96 | 97 | if (strcmp(section, "SPLASH_SCREENS") == 0) { 98 | // reference: e_assets/ini/debug.ini 99 | if (strcmp(key, "SKIP_STRAP_SCREEN") == 0) return true; 100 | if (strcmp(key, "SKIP_CONTROLLER_INFO") == 0) return true; 101 | } 102 | 103 | return false; 104 | } 105 | 106 | // lingcod_getIniKeyValueInt16 107 | kmBranchDefCpp(0x801007D0, NULL, short, char *section, char *key, short defaultValue) { 108 | if (strcmp(section, "NSMB") == 0) { 109 | // reference: e_assets/ini/nsmb/nsmb.ini 110 | if (strcmp(key, "NOTCH_SPEED") == 0) return 0x1C0; 111 | if (strcmp(key, "CANNON_SPEED") == 0) return 0x140; 112 | if (strcmp(key, "WIRE_ADD_RATE") == 0) return 0x500; 113 | if (strcmp(key, "WIRE_SUB_RATE") == 0) return 0x500; 114 | 115 | // can be set to a value from 0 to 68 to run a specific course 116 | // else, runs through all courses 117 | if (strcmp(key, "AUTO_PILOTING_COURSE_INDEX") == 0) return -1; 118 | } 119 | 120 | return defaultValue; 121 | } 122 | 123 | // lingcod_getIniKeyValueFloat 124 | kmBranchDefCpp(0x801007E0, NULL, float, char *section, char *key, float defaultValue) { 125 | if (strcmp(section, "NSMB") == 0) { 126 | // reference: e_assets/ini/nsmb/nsmb.ini 127 | if (strcmp(key, "NINTENDO_LOGO_TIME") == 0) return 2.0f; 128 | if (strcmp(key, "NINTENDO_LOGO_CANCEL_TIME") == 0) return 1.0f; 129 | if (strcmp(key, "NV_LOGO_TIME") == 0) return 2.0f; 130 | if (strcmp(key, "NV_LOGO_CANCEL_TIME") == 0) return 0.0f; 131 | if (strcmp(key, "MOC_SCREEN_TIME") == 0) return 2.0f; 132 | if (strcmp(key, "MOC_SCREEN_CANCEL_TIME") == 0) return 0.0f; 133 | 134 | // not used...? 135 | if (strcmp(key, "DOF_SCALE") == 0) return 1.5f; 136 | } 137 | 138 | return defaultValue; 139 | } 140 | 141 | --------------------------------------------------------------------------------