Add project files.

This commit is contained in:
Gericom 2017-06-12 12:18:17 +02:00
parent b57add0b63
commit 4b3548b6ab
16 changed files with 2926 additions and 0 deletions

34
dspatch.sln Normal file
View File

@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dspatch", "dspatch\dspatch.csproj", "{C8CAE5E5-40A9-4840-AE8A-ABBD2AE50749}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C8CAE5E5-40A9-4840-AE8A-ABBD2AE50749}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C8CAE5E5-40A9-4840-AE8A-ABBD2AE50749}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C8CAE5E5-40A9-4840-AE8A-ABBD2AE50749}.Debug|x64.ActiveCfg = Debug|Any CPU
{C8CAE5E5-40A9-4840-AE8A-ABBD2AE50749}.Debug|x64.Build.0 = Debug|Any CPU
{C8CAE5E5-40A9-4840-AE8A-ABBD2AE50749}.Debug|x86.ActiveCfg = Debug|Any CPU
{C8CAE5E5-40A9-4840-AE8A-ABBD2AE50749}.Debug|x86.Build.0 = Debug|Any CPU
{C8CAE5E5-40A9-4840-AE8A-ABBD2AE50749}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C8CAE5E5-40A9-4840-AE8A-ABBD2AE50749}.Release|Any CPU.Build.0 = Release|Any CPU
{C8CAE5E5-40A9-4840-AE8A-ABBD2AE50749}.Release|x64.ActiveCfg = Release|Any CPU
{C8CAE5E5-40A9-4840-AE8A-ABBD2AE50749}.Release|x64.Build.0 = Release|Any CPU
{C8CAE5E5-40A9-4840-AE8A-ABBD2AE50749}.Release|x86.ActiveCfg = Release|Any CPU
{C8CAE5E5-40A9-4840-AE8A-ABBD2AE50749}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

6
dspatch/App.config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>

89
dspatch/DS/DemoMenu.cs Normal file
View File

@ -0,0 +1,89 @@
using dspatch.IO;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace dspatch.DS
{
public class DemoMenu
{
public byte[] Write()
{
MemoryStream m = new MemoryStream();
EndianBinaryWriter er = new EndianBinaryWriter(m);
er.Write((byte)entries.Length);
er.Write((byte)0);
er.Write((byte)0);
er.Write((byte)0);
foreach (var e in entries)
e.Write(er);
er.Write((uint)0);
byte[] result = m.ToArray();
er.Close();
return result;
}
public DemoMenuEntry[] entries;
public class DemoMenuEntry
{
private string createString(string text, int length, bool nullTerminated = true)
{
if (text == null)
text = "";
if (text.Length > (length - (nullTerminated ? 1 : 0)))
return text.Substring(0, length - (nullTerminated ? 1 : 0));
return text.PadRight(length, '\0');
}
public void Write(EndianBinaryWriter er)
{
er.Write(bannerImage, 0, 512);
er.Write(bannerPalette, 0, 32);
er.Write(createString(bannerText1, 0x80), Encoding.ASCII, false);
er.Write(createString(bannerText2, 0x80), Encoding.ASCII, false);
er.Write(rating);
er.Write(guideMode);
er.Write(createString(selectButtonText, 0x20), Encoding.ASCII, false);
er.Write(createString(startButtonText, 0x20), Encoding.ASCII, false);
er.Write(createString(aButtonText, 0x20), Encoding.ASCII, false);
er.Write(createString(bButtonText, 0x20), Encoding.ASCII, false);
er.Write(createString(xButtonText, 0x20), Encoding.ASCII, false);
er.Write(createString(yButtonText, 0x20), Encoding.ASCII, false);
er.Write(createString(lButtonText, 0x20), Encoding.ASCII, false);
er.Write(createString(rButtonText, 0x20), Encoding.ASCII, false);
er.Write(createString(dpadText, 0x20), Encoding.ASCII, false);
er.Write(createString(unknown1Text, 0x20), Encoding.ASCII, false);
er.Write(createString(unknown2Text, 0x20), Encoding.ASCII, false);
er.Write(createString(unknown3Text, 0x20), Encoding.ASCII, false);
er.Write(createString(touchText1, 0x20), Encoding.ASCII, false);
er.Write(createString(touchText2, 0x20), Encoding.ASCII, false);
er.Write(padding, 0, 0x40);
er.Write(createString(internalName, 0xA, false), Encoding.ASCII, false);
}
public byte[] bannerImage;
public byte[] bannerPalette;
public string bannerText1;
public string bannerText2;
public byte rating = 0x1;//0x1 = Everyone
public byte guideMode = 0x11;//0x11 = nothing, 0x13 = only buttons, 0x15 = only touch, 0x17 = buttons + touch
public string selectButtonText;
public string startButtonText;
public string aButtonText;
public string bButtonText;
public string xButtonText;
public string yButtonText;
public string lButtonText;
public string rButtonText;
public string dpadText;
public string unknown1Text;
public string unknown2Text;
public string unknown3Text;
public string touchText1;
public string touchText2;
public byte[] padding = new byte[0x40];//?
public string internalName;
}
}
}

View File

@ -0,0 +1,374 @@
using System.Text;
using System.Runtime.InteropServices;
using System;
using System.IO;
namespace dspatch.IO
{
public class EndianBinaryReader : IDisposable
{
private bool disposed;
private byte[] buffer;
public Stream BaseStream { get; private set; }
public Endianness Endianness { get; set; }
public Endianness SystemEndianness { get { return BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian; } }
private bool Reverse { get { return SystemEndianness != Endianness; } }
public EndianBinaryReader(Stream baseStream)
: this(baseStream, Endianness.BigEndian)
{ }
public EndianBinaryReader(Stream baseStream, Endianness endianness)
{
if (baseStream == null) throw new ArgumentNullException("baseStream");
if (!baseStream.CanRead) throw new ArgumentException("baseStream");
BaseStream = baseStream;
Endianness = endianness;
}
~EndianBinaryReader()
{
Dispose(false);
}
private void FillBuffer(int bytes, int stride)
{
if (buffer == null || buffer.Length < bytes)
buffer = new byte[bytes];
BaseStream.Read(buffer, 0, bytes);
if (Reverse)
for (int i = 0; i < bytes; i += stride)
{
Array.Reverse(buffer, i, stride);
}
}
public byte ReadByte()
{
FillBuffer(1, 1);
return buffer[0];
}
public byte[] ReadBytes(int count)
{
byte[] temp;
FillBuffer(count, 1);
temp = new byte[count];
Array.Copy(buffer, 0, temp, 0, count);
return temp;
}
public sbyte ReadSByte()
{
FillBuffer(1, 1);
unchecked
{
return (sbyte)buffer[0];
}
}
public sbyte[] ReadSBytes(int count)
{
sbyte[] temp;
temp = new sbyte[count];
FillBuffer(count, 1);
unchecked
{
for (int i = 0; i < count; i++)
{
temp[i] = (sbyte)buffer[i];
}
}
return temp;
}
public char ReadChar(Encoding encoding)
{
int size;
size = GetEncodingSize(encoding);
FillBuffer(size, size);
return encoding.GetChars(buffer, 0, size)[0];
}
public char[] ReadChars(Encoding encoding, int count)
{
int size;
size = GetEncodingSize(encoding);
FillBuffer(size * count, size);
return encoding.GetChars(buffer, 0, size * count);
}
private static int GetEncodingSize(Encoding encoding)
{
if (encoding == Encoding.UTF8 || encoding == Encoding.ASCII)
return 1;
else if (encoding == Encoding.Unicode || encoding == Encoding.BigEndianUnicode)
return 2;
return 1;
}
public string ReadStringNT(Encoding encoding)
{
string text;
text = "";
do
{
text += ReadChar(encoding);
} while (!text.EndsWith("\0", StringComparison.Ordinal));
return text.Remove(text.Length - 1);
}
public string ReadString(Encoding encoding, int count)
{
return new string(ReadChars(encoding, count));
}
public double ReadDouble()
{
const int size = sizeof(double);
FillBuffer(size, size);
return BitConverter.ToDouble(buffer, 0);
}
public double[] ReadDoubles(int count)
{
const int size = sizeof(double);
double[] temp;
temp = new double[count];
FillBuffer(size * count, size);
for (int i = 0; i < count; i++)
{
temp[i] = BitConverter.ToDouble(buffer, size * i);
}
return temp;
}
public Single ReadSingle()
{
const int size = sizeof(Single);
FillBuffer(size, size);
return BitConverter.ToSingle(buffer, 0);
}
public Single[] ReadSingles(int count)
{
const int size = sizeof(Single);
Single[] temp;
temp = new Single[count];
FillBuffer(size * count, size);
for (int i = 0; i < count; i++)
{
temp[i] = BitConverter.ToSingle(buffer, size * i);
}
return temp;
}
public Single ReadFx16()
{
return ReadInt16() / 4096f;
}
public Single ReadFx32()
{
return ReadInt32() / 4096f;
}
public Single[] ReadFx32s(int count)
{
Single[] result = new float[count];
for (int i = 0; i < count; i++)
{
result[i] = ReadInt32() / 4096f;
}
return result;
}
public Int32 ReadInt32()
{
const int size = sizeof(Int32);
FillBuffer(size, size);
return BitConverter.ToInt32(buffer, 0);
}
public Int32[] ReadInt32s(int count)
{
const int size = sizeof(Int32);
Int32[] temp;
temp = new Int32[count];
FillBuffer(size * count, size);
for (int i = 0; i < count; i++)
{
temp[i] = BitConverter.ToInt32(buffer, size * i);
}
return temp;
}
public Int64 ReadInt64()
{
const int size = sizeof(Int64);
FillBuffer(size, size);
return BitConverter.ToInt64(buffer, 0);
}
public Int64[] ReadInt64s(int count)
{
const int size = sizeof(Int64);
Int64[] temp;
temp = new Int64[count];
FillBuffer(size * count, size);
for (int i = 0; i < count; i++)
{
temp[i] = BitConverter.ToInt64(buffer, size * i);
}
return temp;
}
public Int16 ReadInt16()
{
const int size = sizeof(Int16);
FillBuffer(size, size);
return BitConverter.ToInt16(buffer, 0);
}
public Int16[] ReadInt16s(int count)
{
const int size = sizeof(Int16);
Int16[] temp;
temp = new Int16[count];
FillBuffer(size * count, size);
for (int i = 0; i < count; i++)
{
temp[i] = BitConverter.ToInt16(buffer, size * i);
}
return temp;
}
public UInt16 ReadUInt16()
{
const int size = sizeof(UInt16);
FillBuffer(size, size);
return BitConverter.ToUInt16(buffer, 0);
}
public UInt16[] ReadUInt16s(int count)
{
const int size = sizeof(UInt16);
UInt16[] temp;
temp = new UInt16[count];
FillBuffer(size * count, size);
for (int i = 0; i < count; i++)
{
temp[i] = BitConverter.ToUInt16(buffer, size * i);
}
return temp;
}
public UInt32 ReadUInt32()
{
const int size = sizeof(UInt32);
FillBuffer(size, size);
return BitConverter.ToUInt32(buffer, 0);
}
public UInt32[] ReadUInt32s(int count)
{
const int size = sizeof(UInt32);
UInt32[] temp;
temp = new UInt32[count];
FillBuffer(size * count, size);
for (int i = 0; i < count; i++)
{
temp[i] = BitConverter.ToUInt32(buffer, size * i);
}
return temp;
}
public UInt64 ReadUInt64()
{
const int size = sizeof(UInt64);
FillBuffer(size, size);
return BitConverter.ToUInt64(buffer, 0);
}
public UInt64[] ReadUInt64s(int count)
{
const int size = sizeof(UInt64);
UInt64[] temp;
temp = new UInt64[count];
FillBuffer(size * count, size);
for (int i = 0; i < count; i++)
{
temp[i] = BitConverter.ToUInt64(buffer, size * i);
}
return temp;
}
public void Close()
{
Dispose();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
if (BaseStream != null)
{
BaseStream.Close();
}
}
buffer = null;
disposed = true;
}
}
}
public enum Endianness
{
BigEndian,
LittleEndian,
}
}

View File

@ -0,0 +1,344 @@
using System.Text;
using System.IO;
using System;
namespace dspatch.IO
{
public class EndianBinaryWriter : IDisposable
{
private bool disposed;
private byte[] buffer;
public Stream BaseStream { get; private set; }
public Endianness Endianness { get; set; }
public Endianness SystemEndianness { get { return BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian; } }
private bool Reverse { get { return SystemEndianness != Endianness; } }
public EndianBinaryWriter(Stream baseStream)
: this(baseStream, Endianness.BigEndian)
{ }
public EndianBinaryWriter(Stream baseStream, Endianness endianness)
{
if (baseStream == null) throw new ArgumentNullException("baseStream");
if (!baseStream.CanWrite) throw new ArgumentException("baseStream");
BaseStream = baseStream;
Endianness = endianness;
}
~EndianBinaryWriter()
{
Dispose(false);
}
private void WriteBuffer(int bytes, int stride)
{
if (Reverse)
for (int i = 0; i < bytes; i += stride)
{
Array.Reverse(buffer, i, stride);
}
BaseStream.Write(buffer, 0, bytes);
}
private void CreateBuffer(int size)
{
if (buffer == null || buffer.Length < size)
buffer = new byte[size];
}
public void Write(byte value)
{
CreateBuffer(1);
buffer[0] = value;
WriteBuffer(1, 1);
}
public void Write(byte[] value, int offset, int count)
{
CreateBuffer(count);
Array.Copy(value, offset, buffer, 0, count);
WriteBuffer(count, 1);
}
public void Write(sbyte value)
{
CreateBuffer(1);
unchecked
{
buffer[0] = (byte)value;
}
WriteBuffer(1, 1);
}
public void Write(sbyte[] value, int offset, int count)
{
CreateBuffer(count);
unchecked
{
for (int i = 0; i < count; i++)
{
buffer[i] = (byte)value[i + offset];
}
}
WriteBuffer(count, 1);
}
public void Write(char value, Encoding encoding)
{
int size;
size = GetEncodingSize(encoding);
CreateBuffer(size);
Array.Copy(encoding.GetBytes(new string(value, 1)), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(char[] value, int offset, int count, Encoding encoding)
{
int size;
size = GetEncodingSize(encoding);
CreateBuffer(size * count);
Array.Copy(encoding.GetBytes(value, offset, count), 0, buffer, 0, count * size);
WriteBuffer(size * count, size);
}
private static int GetEncodingSize(Encoding encoding)
{
if (encoding == Encoding.UTF8 || encoding == Encoding.ASCII)
return 1;
else if (encoding == Encoding.Unicode || encoding == Encoding.BigEndianUnicode)
return 2;
return 1;
}
public void Write(string value,Encoding encoding, bool nullTerminated)
{
Write(value.ToCharArray(), 0, value.Length, encoding);
if (nullTerminated)
Write('\0', encoding);
}
public void Write(double value)
{
const int size = sizeof(double);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(double[] value, int offset, int count)
{
const int size = sizeof(double);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void Write(Single value)
{
const int size = sizeof(Single);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(Single[] value, int offset, int count)
{
const int size = sizeof(Single);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void Write(Int32 value)
{
const int size = sizeof(Int32);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(Int32[] value, int offset, int count)
{
const int size = sizeof(Int32);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void Write(Int64 value)
{
const int size = sizeof(Int64);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(Int64[] value, int offset, int count)
{
const int size = sizeof(Int64);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void Write(Int16 value)
{
const int size = sizeof(Int16);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(Int16[] value, int offset, int count)
{
const int size = sizeof(Int16);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void Write(UInt16 value)
{
const int size = sizeof(UInt16);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(UInt16[] value, int offset, int count)
{
const int size = sizeof(UInt16);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void Write(UInt32 value)
{
const int size = sizeof(UInt32);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(UInt32[] value, int offset, int count)
{
const int size = sizeof(UInt32);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void Write(UInt64 value)
{
const int size = sizeof(UInt64);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(UInt64[] value, int offset, int count)
{
const int size = sizeof(UInt64);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void WriteFx16(Single Value)
{
Write((Int16)(Value * 4096f));
}
public void WriteFx32(Single Value)
{
Write((Int32)(Value * 4096f));
}
public void Close()
{
Dispose();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
if (BaseStream != null)
{
BaseStream.Close();
}
}
buffer = null;
disposed = true;
}
}
}
}

119
dspatch/IO/IOUtil.cs Normal file
View File

@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace dspatch.IO
{
public class IOUtil
{
public static short ReadS16LE(byte[] Data, int Offset)
{
return (short)(Data[Offset] | (Data[Offset + 1] << 8));
}
public static short[] ReadS16sLE(byte[] Data, int Offset, int Count)
{
short[] res = new short[Count];
for (int i = 0; i < Count; i++) res[i] = ReadS16LE(Data, Offset + i * 2);
return res;
}
public static short ReadS16BE(byte[] Data, int Offset)
{
return (short)((Data[Offset] << 8) | Data[Offset + 1]);
}
public static void WriteS16LE(byte[] Data, int Offset, short Value)
{
Data[Offset] = (byte)(Value & 0xFF);
Data[Offset + 1] = (byte)((Value >> 8) & 0xFF);
}
public static void WriteS16sLE(byte[] Data, int Offset, short[] Values)
{
for (int i = 0; i < Values.Length; i++)
{
WriteS16LE(Data, Offset + i * 2, Values[i]);
}
}
public static ushort ReadU16LE(byte[] Data, int Offset)
{
return (ushort)(Data[Offset] | (Data[Offset + 1] << 8));
}
public static ushort ReadU16BE(byte[] Data, int Offset)
{
return (ushort)((Data[Offset] << 8) | Data[Offset + 1]);
}
public static void WriteU16LE(byte[] Data, int Offset, ushort Value)
{
Data[Offset] = (byte)(Value & 0xFF);
Data[Offset + 1] = (byte)((Value >> 8) & 0xFF);
}
public static uint ReadU24LE(byte[] Data, int Offset)
{
return (uint)(Data[Offset] | (Data[Offset + 1] << 8) | (Data[Offset + 2] << 16));
}
public static uint ReadU32LE(byte[] Data, int Offset)
{
return (uint)(Data[Offset] | (Data[Offset + 1] << 8) | (Data[Offset + 2] << 16) | (Data[Offset + 3] << 24));
}
public static uint ReadU32BE(byte[] Data, int Offset)
{
return (uint)((Data[Offset] << 24) | (Data[Offset + 1] << 16) | (Data[Offset + 2] << 8) | Data[Offset + 3]);
}
public static void WriteU32LE(byte[] Data, int Offset, uint Value)
{
Data[Offset] = (byte)(Value & 0xFF);
Data[Offset + 1] = (byte)((Value >> 8) & 0xFF);
Data[Offset + 2] = (byte)((Value >> 16) & 0xFF);
Data[Offset + 3] = (byte)((Value >> 24) & 0xFF);
}
public static void WriteU32BE(byte[] Data, int Offset, uint Value)
{
Data[Offset + 0] = (byte)((Value >> 24) & 0xFF);
Data[Offset + 1] = (byte)((Value >> 16) & 0xFF);
Data[Offset + 2] = (byte)((Value >> 8) & 0xFF);
Data[Offset + 3] = (byte)(Value & 0xFF);
}
public static ulong ReadU64LE(byte[] Data, int Offset)
{
return (ulong)Data[Offset] | ((ulong)Data[Offset + 1] << 8) | ((ulong)Data[Offset + 2] << 16) | ((ulong)Data[Offset + 3] << 24) | ((ulong)Data[Offset + 4] << 32) | ((ulong)Data[Offset + 5] << 40) | ((ulong)Data[Offset + 6] << 48) | ((ulong)Data[Offset + 7] << 56);
}
public static ulong ReadU64BE(byte[] Data, int Offset)
{
return ((ulong)Data[Offset] << 56) | ((ulong)Data[Offset + 1] << 48) | ((ulong)Data[Offset + 2] << 40) | ((ulong)Data[Offset + 3] << 32) | ((ulong)Data[Offset + 4] << 24) | ((ulong)Data[Offset + 5] << 16) | ((ulong)Data[Offset + 6] << 8) | ((ulong)Data[Offset + 7] << 0);
}
public static void WriteU64LE(byte[] Data, int Offset, ulong Value)
{
Data[Offset] = (byte)(Value & 0xFF);
Data[Offset + 1] = (byte)((Value >> 8) & 0xFF);
Data[Offset + 2] = (byte)((Value >> 16) & 0xFF);
Data[Offset + 3] = (byte)((Value >> 24) & 0xFF);
Data[Offset + 4] = (byte)((Value >> 32) & 0xFF);
Data[Offset + 5] = (byte)((Value >> 40) & 0xFF);
Data[Offset + 6] = (byte)((Value >> 48) & 0xFF);
Data[Offset + 7] = (byte)((Value >> 56) & 0xFF);
}
public static void WriteSingleLE(byte[] Data, int Offset, float Value)
{
byte[] a = BitConverter.GetBytes(Value);
Data[0 + Offset] = a[0];
Data[1 + Offset] = a[1];
Data[2 + Offset] = a[2];
Data[3 + Offset] = a[3];
}
}
}

235
dspatch/IO/SFSDirectory.cs Normal file
View File

@ -0,0 +1,235 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace dspatch.IO
{
public class SFSDirectory
{
public SFSDirectory(string Name, bool Root)
{
DirectoryName = Name;
IsRoot = Root;
SubDirectories = new List<SFSDirectory>();
Files = new List<SFSFile>();
}
public SFSDirectory(UInt16 Id)
{
DirectoryID = Id;
IsRoot = false;
SubDirectories = new List<SFSDirectory>();
Files = new List<SFSFile>();
}
public static SFSDirectory FromDirectory(String Path)
{
SFSDirectory Root = new SFSDirectory("/", true);
FillSFSDirFromDisk(new DirectoryInfo(Path), Root);
return Root;
}
private static void FillSFSDirFromDisk(DirectoryInfo Dir, SFSDirectory Dst)
{
foreach (var v in Dir.EnumerateFiles())
{
Dst.Files.Add(new SFSFile(-1, v.Name, Dst) { Data = File.ReadAllBytes(v.FullName) });
}
foreach (var v in Dir.EnumerateDirectories())
{
SFSDirectory d = new SFSDirectory(v.Name, false) { Parent = Dst };
Dst.SubDirectories.Add(d);
FillSFSDirFromDisk(v, d);
}
}
public String DirectoryName;
public UInt16 DirectoryID;
public SFSDirectory Parent;
public List<SFSDirectory> SubDirectories;
public List<SFSFile> Files;
public Boolean IsRoot { get; set; }
public SFSFile this[string index]
{
get
{
foreach (SFSFile f in Files)
{
if (f.FileName == index) return f;
}
return null;
//throw new FileNotFoundException("The file " + index + " does not exist in this directory.");
}
set
{
for (int i = 0; i < Files.Count; i++)
{
if (Files[i].FileName == index)
{
Files[i] = value;
return;
}
}
throw new System.IO.FileNotFoundException("The file " + index + " does not exist in this directory.");
}
}
public UInt32 TotalNrSubDirectories
{
get
{
UInt32 nr = (UInt32)SubDirectories.Count;
foreach (SFSDirectory d in SubDirectories) nr += d.TotalNrSubDirectories;
return nr;
}
}
public UInt32 TotalNrSubFiles
{
get
{
UInt32 nr = (UInt32)Files.Count;
foreach (SFSDirectory d in SubDirectories) nr += d.TotalNrSubFiles;
return nr;
}
}
public SFSFile GetFileByID(int Id)
{
foreach (SFSFile f in Files)
{
if (f.FileID == Id) return f;
}
foreach (SFSDirectory d in SubDirectories)
{
SFSFile f = d.GetFileByID(Id);
if (f != null) return f;
}
return null;
}
public SFSDirectory GetDirectoryByPath(String Path)
{
if (Path.TrimStart('/').StartsWith("../"))
{
if (Parent == null) return null;
return Parent.GetDirectoryByPath("/" + Path.TrimStart('/').Substring(2));
}
if (!Path.StartsWith(DirectoryName)) return null;
if (Path == DirectoryName) return this;
Path = Path.Substring(DirectoryName.Length);
if (Path.Length == 0 || !Path.StartsWith("/")) return null;
String dir = Path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries)[0];
foreach(var v in SubDirectories)
{
if(v.DirectoryName == dir) return v.GetDirectoryByPath(Path.Substring(1));
}
return null;
}
public SFSFile GetFileByPath(String Path)
{
if (Path.TrimStart('/').StartsWith("../"))
{
if (Parent == null) return null;
return Parent.GetFileByPath("/" + Path.TrimStart('/').Substring(2));
}
if (!Path.StartsWith(DirectoryName)) return null;
Path = Path.Substring(DirectoryName.Length);
if (Path.Length == 0 || !Path.StartsWith("/")) return null;
//Sarc files may use slashes in their names (since they are just hashes, and do not have a real directory structure)
var vvv = this[Path.Substring(1)];
if (vvv != null) return vvv;
string[] parts = Path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length > 1)
{
String dir = parts[0];
foreach (var v in SubDirectories)
{
if (v.DirectoryName == dir) return v.GetFileByPath(Path.Substring(1));
}
}
else if(parts.Length == 1)
{
return this[parts[0]];
}
return null;
}
public void UpdateIDs(ref int DirId, ref int FileID)
{
this.DirectoryID = (ushort)(0xF000 | DirId++);
foreach (var v in Files)
{
v.FileID = FileID++;
}
foreach (var v in SubDirectories)
{
v.UpdateIDs(ref DirId, ref FileID);
}
}
public bool IsValidName(String Name)
{
char[] invalid = System.IO.Path.GetInvalidFileNameChars();
foreach (char c in invalid)
{
if (Name.Contains(c)) return false;
}
foreach (var v in SubDirectories)
{
if (v.DirectoryName == Name) return false;
}
foreach (var v in Files)
{
if (v.FileName == Name) return false;
}
return true;
}
public void PrintTree(ref int indent)
{
Console.WriteLine(new string(' ', indent * 4) + DirectoryName + " (" + DirectoryID.ToString("X") + ")");
indent++;
foreach (var v in Files)
{
Console.WriteLine(new string(' ', indent * 4) + v.FileName + " (" + v.FileID.ToString("X") + ")");
}
foreach (var v in SubDirectories)
{
v.PrintTree(ref indent);
}
indent--;
}
public void Export(String Path)
{
foreach (var v in Files)
{
System.IO.File.Create(Path + "\\" + v.FileName).Close();
System.IO.File.WriteAllBytes(Path + "\\" + v.FileName, v.Data);
}
foreach (var v in SubDirectories)
{
System.IO.Directory.CreateDirectory(Path + "\\" + v.DirectoryName);
v.Export(Path + "\\" + v.DirectoryName);
}
}
public override string ToString()
{
if (Parent != this)
{
if (!IsRoot) return Parent.ToString() + DirectoryName + "/";
else return "/";
}
else return DirectoryName;
}
}
}

27
dspatch/IO/SFSFile.cs Normal file
View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace dspatch.IO
{
public class SFSFile
{
public SFSFile(Int32 Id, String Name, SFSDirectory Parent)
{
FileID = Id;
FileName = Name;
this.Parent = Parent;
}
public String FileName;
public Int32 FileID;
public Byte[] Data;
public SFSDirectory Parent;
public override string ToString()
{
return Parent.ToString() + FileName;
}
}
}

171
dspatch/Nitro/ARM9.cs Normal file
View File

@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using dspatch.IO;
namespace dspatch.Nitro
{
public class ARM9
{
private UInt32 RamAddress;
private byte[] StaticData;
private UInt32 _start_ModuleParamsOffset;
private CRT0.ModuleParams _start_ModuleParams;
private List<CRT0.AutoLoadEntry> AutoLoadList;
public ARM9(byte[] Data, UInt32 RamAddress)
: this(Data, RamAddress, FindModuleParams(Data)) { }
public ARM9(byte[] Data, UInt32 RamAddress, UInt32 _start_ModuleParamsOffset)
{
//Unimportant static footer! Use it for _start_ModuleParamsOffset and remove it.
if (IOUtil.ReadU32LE(Data, Data.Length - 12) == 0xDEC00621)
{
_start_ModuleParamsOffset = IOUtil.ReadU32LE(Data, Data.Length - 8);
byte[] data_tmp = new byte[Data.Length - 12];
Array.Copy(Data, data_tmp, Data.Length - 12);
Data = data_tmp;
}
this.RamAddress = RamAddress;
this._start_ModuleParamsOffset = _start_ModuleParamsOffset;
_start_ModuleParams = new CRT0.ModuleParams(Data, _start_ModuleParamsOffset);
if (_start_ModuleParams.CompressedStaticEnd != 0)
{
Data = Decompress(Data, _start_ModuleParamsOffset);
_start_ModuleParams = new CRT0.ModuleParams(Data, _start_ModuleParamsOffset);
}
StaticData = new byte[_start_ModuleParams.AutoLoadStart - RamAddress];
Array.Copy(Data, StaticData, _start_ModuleParams.AutoLoadStart - RamAddress);
AutoLoadList = new List<CRT0.AutoLoadEntry>();
uint nr = (_start_ModuleParams.AutoLoadListEnd - _start_ModuleParams.AutoLoadListOffset) / 0xC;
uint Offset = _start_ModuleParams.AutoLoadStart - RamAddress;
for (int i = 0; i < nr; i++)
{
var entry = new CRT0.AutoLoadEntry(Data, _start_ModuleParams.AutoLoadListOffset - RamAddress + (uint)i * 0xC);
entry.Data = new byte[entry.Size];
Array.Copy(Data, Offset, entry.Data, 0, entry.Size);
AutoLoadList.Add(entry);
Offset += entry.Size;
}
}
public byte[] Write()
{
MemoryStream m = new MemoryStream();
EndianBinaryWriter er = new EndianBinaryWriter(m, Endianness.LittleEndian);
er.Write(StaticData, 0, StaticData.Length);
_start_ModuleParams.AutoLoadStart = (uint)er.BaseStream.Position + RamAddress;
foreach (var v in AutoLoadList) er.Write(v.Data, 0, v.Data.Length);
_start_ModuleParams.AutoLoadListOffset = (uint)er.BaseStream.Position + RamAddress;
foreach (var v in AutoLoadList) v.Write(er);
_start_ModuleParams.AutoLoadListEnd = (uint)er.BaseStream.Position + RamAddress;
long curpos = er.BaseStream.Position;
er.BaseStream.Position = _start_ModuleParamsOffset;
_start_ModuleParams.Write(er);
er.BaseStream.Position = curpos;
byte[] data = m.ToArray();
er.Close();
return data;
}
public void AddAutoLoadEntry(UInt32 Address, byte[] Data)
{
AutoLoadList.Add(new CRT0.AutoLoadEntry(Address, Data));
}
public bool WriteU16LE(UInt32 Address, UInt16 Value)
{
if (Address > RamAddress && Address < _start_ModuleParams.AutoLoadStart)
{
IOUtil.WriteU16LE(StaticData, (int)(Address - RamAddress), Value);
return true;
}
foreach (var v in AutoLoadList)
{
if (Address > v.Address && Address < (v.Address + v.Size))
{
IOUtil.WriteU16LE(v.Data, (int)(Address - v.Address), Value);
return true;
}
}
return false;
}
public UInt32 ReadU32LE(UInt32 Address)
{
if (Address > RamAddress && Address < _start_ModuleParams.AutoLoadStart)
{
return IOUtil.ReadU32LE(StaticData, (int)(Address - RamAddress));
}
foreach (var v in AutoLoadList)
{
if (Address > v.Address && Address < (v.Address + v.Size))
{
return IOUtil.ReadU32LE(v.Data, (int)(Address - v.Address));
}
}
return 0xFFFFFFFF;
}
public bool WriteU32LE(UInt32 Address, UInt32 Value)
{
if (Address > RamAddress && Address < _start_ModuleParams.AutoLoadStart)
{
IOUtil.WriteU32LE(StaticData, (int)(Address - RamAddress), Value);
return true;
}
foreach (var v in AutoLoadList)
{
if (Address > v.Address && Address < (v.Address + v.Size))
{
IOUtil.WriteU32LE(v.Data, (int)(Address - v.Address), Value);
return true;
}
}
return false;
}
public static byte[] Decompress(byte[] Data)
{
uint offset = FindModuleParams(Data);
if (offset == 0xffffffe3) return Data;//no moduleparams, so it must be uncompressed
return Decompress(Data, offset);
}
public static byte[] Decompress(byte[] Data, UInt32 _start_ModuleParamsOffset)
{
if (IOUtil.ReadU32LE(Data, (int)_start_ModuleParamsOffset + 0x14) == 0) return Data;//Not Compressed!
byte[] Result = CRT0.MIi_UncompressBackward(Data);
IOUtil.WriteU32LE(Result, (int)_start_ModuleParamsOffset + 0x14, 0);
return Result;
}
private static uint FindModuleParams(byte[] Data)
{
return (uint)IndexOf(Data, new byte[] { 0x21, 0x06, 0xC0, 0xDE, 0xDE, 0xC0, 0x06, 0x21 }) - 0x1C;
}
private static unsafe long IndexOf(byte[] Data, byte[] Search)
{
fixed (byte* H = Data) fixed (byte* N = Search)
{
long i = 0;
for (byte* hNext = H, hEnd = H + Data.LongLength; hNext < hEnd; i++, hNext++)
{
bool Found = true;
for (byte* hInc = hNext, nInc = N, nEnd = N + Search.LongLength; Found && nInc < nEnd; Found = *nInc == *hInc, nInc++, hInc++) ;
if (Found) return i;
}
return -1;
}
}
}
}

62
dspatch/Nitro/CRC16.cs Normal file
View File

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace dspatch.Nitro
{
public class CRC16
{
private readonly static ushort[] CRC16Table =
{
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};
public static ushort GetCRC16(byte[] Data)
{
return GetCRC16(Data, 0, Data.Length);
}
public static ushort GetCRC16(byte[] Data, int start, int length)
{
ushort result = 0xFFFF;
for (int i = start; i < start + length; i++)
{
result = (ushort)((result >> 8) ^ CRC16Table[(result ^ Data[i]) & 0xFF]);
}
return (ushort)result;
}
}
}

110
dspatch/Nitro/CRT0.cs Normal file
View File

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using dspatch.IO;
namespace dspatch.Nitro
{
public class CRT0
{
public class ModuleParams
{
public ModuleParams(byte[] Data, uint Offset)
{
AutoLoadListOffset = IOUtil.ReadU32LE(Data, (int)Offset + 0);
AutoLoadListEnd = IOUtil.ReadU32LE(Data, (int)Offset + 4);
AutoLoadStart = IOUtil.ReadU32LE(Data, (int)Offset + 8);
StaticBssStart = IOUtil.ReadU32LE(Data, (int)Offset + 12);
StaticBssEnd = IOUtil.ReadU32LE(Data, (int)Offset + 16);
CompressedStaticEnd = IOUtil.ReadU32LE(Data, (int)Offset + 20);
SDKVersion = IOUtil.ReadU32LE(Data, (int)Offset + 24);
NitroCodeBE = IOUtil.ReadU32LE(Data, (int)Offset + 28);
NitroCodeLE = IOUtil.ReadU32LE(Data, (int)Offset + 32);
}
public void Write(EndianBinaryWriter er)
{
er.Write(AutoLoadListOffset);
er.Write(AutoLoadListEnd);
er.Write(AutoLoadStart);
er.Write(StaticBssStart);
er.Write(StaticBssEnd);
er.Write(CompressedStaticEnd);
er.Write(SDKVersion);
er.Write(NitroCodeBE);
er.Write(NitroCodeLE);
}
public UInt32 AutoLoadListOffset;
public UInt32 AutoLoadListEnd;
public UInt32 AutoLoadStart;
public UInt32 StaticBssStart;
public UInt32 StaticBssEnd;
public UInt32 CompressedStaticEnd;
public UInt32 SDKVersion;
public UInt32 NitroCodeBE;
public UInt32 NitroCodeLE;
}
public class AutoLoadEntry
{
public AutoLoadEntry(UInt32 Address, byte[] Data)
{
this.Address = Address;
this.Data = Data;
Size = (uint)Data.Length;
BssSize = 0;
}
public AutoLoadEntry(byte[] Data, uint Offset)
{
Address = IOUtil.ReadU32LE(Data, (int)Offset + 0);
Size = IOUtil.ReadU32LE(Data, (int)Offset + 4);
BssSize = IOUtil.ReadU32LE(Data, (int)Offset + 8);
}
public void Write(EndianBinaryWriter er)
{
er.Write(Address);
er.Write(Size);
er.Write(BssSize);
}
public UInt32 Address;
public UInt32 Size;
public UInt32 BssSize;
public byte[] Data;
}
public static byte[] MIi_UncompressBackward(byte[] Data)
{
UInt32 leng = IOUtil.ReadU32LE(Data, Data.Length - 4) + (uint)Data.Length;
byte[] Result = new byte[leng];
Array.Copy(Data, Result, Data.Length);
int Offs = (int)(Data.Length - (IOUtil.ReadU32LE(Data, Data.Length - 8) >> 24));
int dstoffs = (int)leng;
while (true)
{
byte header = Result[--Offs];
for (int i = 0; i < 8; i++)
{
if ((header & 0x80) == 0) Result[--dstoffs] = Result[--Offs];
else
{
byte a = Result[--Offs];
byte b = Result[--Offs];
int offs = (((a & 0xF) << 8) | b) + 2;//+ 1;
int length = (a >> 4) + 2;
do
{
Result[dstoffs - 1] = Result[dstoffs + offs];
dstoffs--;
length--;
}
while (length >= 0);
}
if (Offs <= (Data.Length - (IOUtil.ReadU32LE(Data, Data.Length - 8) & 0xFFFFFF))) return Result;
header <<= 1;
}
}
}
}
}

719
dspatch/Nitro/NDS.cs Normal file
View File

@ -0,0 +1,719 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using dspatch.IO;
using System.Xml.Serialization;
using System.Xml;
namespace dspatch.Nitro
{
public class NDS
{
public NDS() { }
public NDS(byte[] data)
{
EndianBinaryReader er = new EndianBinaryReader(new MemoryStream(data), Endianness.LittleEndian);
Header = new RomHeader(er);
er.BaseStream.Position = Header.MainRomOffset;
MainRom = er.ReadBytes((int)Header.MainSize);
if (er.ReadUInt32() == 0xDEC00621)//Nitro Footer!
{
er.BaseStream.Position -= 4;
StaticFooter = new NitroFooter(er);
}
er.BaseStream.Position = Header.SubRomOffset;
SubRom = er.ReadBytes((int)Header.SubSize);
er.BaseStream.Position = Header.FntOffset;
Fnt = new RomFNT(er);
er.BaseStream.Position = Header.MainOvtOffset;
MainOvt = new RomOVT[Header.MainOvtSize / 32];
for (int i = 0; i < Header.MainOvtSize / 32; i++) MainOvt[i] = new RomOVT(er);
er.BaseStream.Position = Header.SubOvtOffset;
SubOvt = new RomOVT[Header.SubOvtSize / 32];
for (int i = 0; i < Header.SubOvtSize / 32; i++) SubOvt[i] = new RomOVT(er);
er.BaseStream.Position = Header.FatOffset;
Fat = new FileAllocationEntry[Header.FatSize / 8];
for (int i = 0; i < Header.FatSize / 8; i++) Fat[i] = new FileAllocationEntry(er);
if (Header.BannerOffset != 0)
{
er.BaseStream.Position = Header.BannerOffset;
Banner = new RomBanner(er);
}
FileData = new byte[Header.FatSize / 8][];
for (int i = 0; i < Header.FatSize / 8; i++)
{
er.BaseStream.Position = Fat[i].fileTop;
FileData[i] = er.ReadBytes((int)Fat[i].fileSize);
}
//RSA Signature
if (Header.RomSize + 0x88 <= er.BaseStream.Length)
{
er.BaseStream.Position = Header.RomSize;
byte[] RSASig = er.ReadBytes(0x88);
for (int i = 0; i < RSASig.Length; i++)
{
//It could be padding, so check if there is something other than 0xFF or 0x00
if (RSASig[i] != 0xFF || RSASig[i] != 0x00)
{
RSASignature = RSASig;
break;
}
}
}
er.Close();
}
public byte[] Write(bool multiboot = false)
{
MemoryStream m = new MemoryStream();
EndianBinaryWriter er = new EndianBinaryWriter(m, Endianness.LittleEndian);
//Header
//skip the header, and write it afterwards
er.BaseStream.Position = 16384;
Header.HeaderSize = (uint)er.BaseStream.Position;
//MainRom
Header.MainRomOffset = (uint)er.BaseStream.Position;
Header.MainSize = (uint)MainRom.Length;
er.Write(MainRom, 0, MainRom.Length);
//Static Footer
if (StaticFooter != null) StaticFooter.Write(er);
if (MainOvt.Length != 0)
{
while ((er.BaseStream.Position % 0x200) != 0) er.Write((byte)0);
//Main Ovt
Header.MainOvtOffset = (uint)er.BaseStream.Position;
Header.MainOvtSize = (uint)MainOvt.Length * 0x20;
foreach (var v in MainOvt) v.Write(er);
foreach (var v in MainOvt)
{
while ((er.BaseStream.Position % 0x200) != 0) er.Write((byte)0);
Fat[v.FileId].fileTop = (uint)er.BaseStream.Position;
Fat[v.FileId].fileBottom = (uint)er.BaseStream.Position + (uint)FileData[v.FileId].Length;
er.Write(FileData[v.FileId], 0, FileData[v.FileId].Length);
}
}
else
{
Header.MainOvtOffset = 0;
Header.MainOvtSize = 0;
}
while ((er.BaseStream.Position % 0x200) != 0) er.Write((byte)0xFF);
//SubRom
Header.SubRomOffset = (uint)er.BaseStream.Position;
Header.SubSize = (uint)SubRom.Length;
er.Write(SubRom, 0, SubRom.Length);
//I assume this works the same as the main ovt?
if (SubOvt.Length != 0)
{
while ((er.BaseStream.Position % 0x200) != 0) er.Write((byte)0);
//Sub Ovt
Header.SubOvtOffset = (uint)er.BaseStream.Position;
Header.SubOvtSize = (uint)SubOvt.Length * 0x20;
foreach (var v in SubOvt) v.Write(er);
foreach (var v in SubOvt)
{
while ((er.BaseStream.Position % 0x200) != 0) er.Write((byte)0);
Fat[v.FileId].fileTop = (uint)er.BaseStream.Position;
Fat[v.FileId].fileBottom = (uint)er.BaseStream.Position + (uint)FileData[v.FileId].Length;
er.Write(FileData[v.FileId], 0, FileData[v.FileId].Length);
}
}
else
{
Header.SubOvtOffset = 0;
Header.SubOvtSize = 0;
}
while ((er.BaseStream.Position % 0x200) != 0) er.Write((byte)0xFF);
//FNT
Header.FntOffset = (uint)er.BaseStream.Position;
Fnt.Write(er);
Header.FntSize = (uint)er.BaseStream.Position - Header.FntOffset;
while ((er.BaseStream.Position % 0x200) != 0) er.Write((byte)0xFF);
//FAT
Header.FatOffset = (uint)er.BaseStream.Position;
Header.FatSize = (uint)Fat.Length * 8;
//Skip the fat, and write it after writing the data itself
er.BaseStream.Position += Header.FatSize;
//Banner
if (Banner != null)
{
while ((er.BaseStream.Position % 0x200) != 0) er.Write((byte)0xFF);
Header.BannerOffset = (uint)er.BaseStream.Position;
Banner.Write(er);
}
else Header.BannerOffset = 0;
//Files
for (int i = (int)(Header.MainOvtSize / 32 + Header.SubOvtSize / 32); i < FileData.Length; i++)
{
while ((er.BaseStream.Position % 0x200) != 0) er.Write((byte)0xFF);
Fat[i].fileTop = (uint)er.BaseStream.Position;
Fat[i].fileBottom = (uint)er.BaseStream.Position + (uint)FileData[i].Length;
er.Write(FileData[i], 0, FileData[i].Length);
}
while ((er.BaseStream.Position % 4/*0x200*/) != 0) er.Write((byte)0);
long curpos = er.BaseStream.Position;
Header.RomSize = (uint)er.BaseStream.Position;
if (!multiboot)
{
uint CapacitySize = Header.RomSize;
CapacitySize |= CapacitySize >> 16;
CapacitySize |= CapacitySize >> 8;
CapacitySize |= CapacitySize >> 4;
CapacitySize |= CapacitySize >> 2;
CapacitySize |= CapacitySize >> 1;
CapacitySize++;
if (CapacitySize <= 0x20000) CapacitySize = 0x20000;
int Capacity = -18;
while (CapacitySize != 0) { CapacitySize >>= 1; Capacity++; }
Header.DeviceSize = (byte)((Capacity < 0) ? 0 : Capacity);
}
else
Header.DeviceSize = 0x0B;
//RSA!
if (RSASignature != null) er.Write(RSASignature, 0, 0x88);
//Fat
er.BaseStream.Position = Header.FatOffset;
foreach (var v in Fat) v.Write(er);
//Header
er.BaseStream.Position = 0;
Header.Write(er);
byte[] result = m.ToArray();
er.Close();
return result;
}
public RomHeader Header;
[Serializable]
public class RomHeader
{
public RomHeader() { }
public RomHeader(EndianBinaryReader er)
{
GameName = er.ReadString(Encoding.ASCII, 12).Replace("\0", "");
GameCode = er.ReadString(Encoding.ASCII, 4).Replace("\0", "");
MakerCode = er.ReadString(Encoding.ASCII, 2).Replace("\0", "");
ProductId = er.ReadByte();
DeviceType = er.ReadByte();
DeviceSize = er.ReadByte();
ReservedA = er.ReadBytes(9);
GameVersion = er.ReadByte();
Property = er.ReadByte();
MainRomOffset = er.ReadUInt32();
MainEntryAddress = er.ReadUInt32();
MainRamAddress = er.ReadUInt32();
MainSize = er.ReadUInt32();
SubRomOffset = er.ReadUInt32();
SubEntryAddress = er.ReadUInt32();
SubRamAddress = er.ReadUInt32();
SubSize = er.ReadUInt32();
FntOffset = er.ReadUInt32();
FntSize = er.ReadUInt32();
FatOffset = er.ReadUInt32();
FatSize = er.ReadUInt32();
MainOvtOffset = er.ReadUInt32();
MainOvtSize = er.ReadUInt32();
SubOvtOffset = er.ReadUInt32();
SubOvtSize = er.ReadUInt32();
RomParamA = er.ReadBytes(8);
BannerOffset = er.ReadUInt32();
SecureCRC = er.ReadUInt16();
RomParamB = er.ReadBytes(2);
MainAutoloadDone = er.ReadUInt32();
SubAutoloadDone = er.ReadUInt32();
RomParamC = er.ReadBytes(8);
RomSize = er.ReadUInt32();
HeaderSize = er.ReadUInt32();
ReservedB = er.ReadBytes(0x38);
LogoData = er.ReadBytes(0x9C);
LogoCRC = er.ReadUInt16();
HeaderCRC = er.ReadUInt16();
}
public void Write(EndianBinaryWriter er)
{
MemoryStream m = new MemoryStream();
EndianBinaryWriter ew = new EndianBinaryWriter(m, Endianness.LittleEndian);
ew.Write(GameName.PadRight(12, '\0'), Encoding.ASCII, false);
ew.Write(GameCode.PadRight(4, '\0'), Encoding.ASCII, false);
ew.Write(MakerCode.PadRight(2, '\0'), Encoding.ASCII, false);
ew.Write(ProductId);
ew.Write(DeviceType);
ew.Write(DeviceSize);
ew.Write(ReservedA, 0, 9);
ew.Write(GameVersion);
ew.Write(Property);
ew.Write(MainRomOffset);
ew.Write(MainEntryAddress);
ew.Write(MainRamAddress);
ew.Write(MainSize);
ew.Write(SubRomOffset);
ew.Write(SubEntryAddress);
ew.Write(SubRamAddress);
ew.Write(SubSize);
ew.Write(FntOffset);
ew.Write(FntSize);
ew.Write(FatOffset);
ew.Write(FatSize);
ew.Write(MainOvtOffset);
ew.Write(MainOvtSize);
ew.Write(SubOvtOffset);
ew.Write(SubOvtSize);
ew.Write(RomParamA, 0, 8);
ew.Write(BannerOffset);
ew.Write(SecureCRC);
ew.Write(RomParamB, 0, 2);
ew.Write(MainAutoloadDone);
ew.Write(SubAutoloadDone);
ew.Write(RomParamC, 0, 8);
ew.Write(RomSize);
ew.Write(HeaderSize);
ew.Write(ReservedB, 0, 0x38);
ew.Write(LogoData, 0, 0x9C);
LogoCRC = CRC16.GetCRC16(LogoData);
ew.Write(LogoCRC);
byte[] header = m.ToArray();
ew.Close();
HeaderCRC = CRC16.GetCRC16(header);
er.Write(header, 0, header.Length);
er.Write(HeaderCRC);
}
public String GameName;//12
public String GameCode;//4
public String MakerCode;//2
public Byte ProductId;
public Byte DeviceType;
public Byte DeviceSize;
public byte[] ReservedA;//9
public Byte GameVersion;
public Byte Property;
[XmlIgnore]
public UInt32 MainRomOffset;
public UInt32 MainEntryAddress;
public UInt32 MainRamAddress;
[XmlIgnore]
public UInt32 MainSize;
[XmlIgnore]
public UInt32 SubRomOffset;
public UInt32 SubEntryAddress;
public UInt32 SubRamAddress;
[XmlIgnore]
public UInt32 SubSize;
[XmlIgnore]
public UInt32 FntOffset;
[XmlIgnore]
public UInt32 FntSize;
[XmlIgnore]
public UInt32 FatOffset;
[XmlIgnore]
public UInt32 FatSize;
[XmlIgnore]
public UInt32 MainOvtOffset;
[XmlIgnore]
public UInt32 MainOvtSize;
[XmlIgnore]
public UInt32 SubOvtOffset;
[XmlIgnore]
public UInt32 SubOvtSize;
public byte[] RomParamA;//8
[XmlIgnore]
public UInt32 BannerOffset;
public UInt16 SecureCRC;
public byte[] RomParamB;//2
public UInt32 MainAutoloadDone;
public UInt32 SubAutoloadDone;
public byte[] RomParamC;//8
[XmlIgnore]
public UInt32 RomSize;
[XmlIgnore]
public UInt32 HeaderSize;
public byte[] ReservedB;//0x38
public byte[] LogoData;//0x9C
[XmlIgnore]
public UInt16 LogoCRC;
[XmlIgnore]
public UInt16 HeaderCRC;
}
public Byte[] MainRom;
public NitroFooter StaticFooter;
[Serializable]
public class NitroFooter
{
public NitroFooter() { }
public NitroFooter(EndianBinaryReader er)
{
NitroCode = er.ReadUInt32();
_start_ModuleParamsOffset = er.ReadUInt32();
Unknown = er.ReadUInt32();
}
public void Write(EndianBinaryWriter er)
{
er.Write(NitroCode);
er.Write(_start_ModuleParamsOffset);
er.Write(Unknown);
}
public UInt32 NitroCode;
public UInt32 _start_ModuleParamsOffset;
public UInt32 Unknown;
}
public Byte[] SubRom;
public RomFNT Fnt;
public class RomFNT
{
public RomFNT()
{
DirectoryTable = new List<DirectoryTableEntry>();
EntryNameTable = new List<EntryNameTableEntry>();
}
public RomFNT(EndianBinaryReader er)
{
DirectoryTable = new List<DirectoryTableEntry>();
DirectoryTable.Add(new DirectoryTableEntry(er));
for (int i = 0; i < DirectoryTable[0].dirParentID - 1; i++)
{
DirectoryTable.Add(new DirectoryTableEntry(er));
}
EntryNameTable = new List<EntryNameTableEntry>();
int dirend = 0;
while (dirend < DirectoryTable[0].dirParentID)
{
byte entryNameLength = er.ReadByte();
er.BaseStream.Position--;
if (entryNameLength == 0)
{
EntryNameTable.Add(new EntryNameTableEndOfDirectoryEntry(er));
dirend++;
}
else if (entryNameLength < 0x80) EntryNameTable.Add(new EntryNameTableFileEntry(er));
else EntryNameTable.Add(new EntryNameTableDirectoryEntry(er));
}
}
public void Write(EndianBinaryWriter er)
{
foreach (DirectoryTableEntry e in DirectoryTable) e.Write(er);
foreach (EntryNameTableEntry e in EntryNameTable) e.Write(er);
}
public List<DirectoryTableEntry> DirectoryTable;
public List<EntryNameTableEntry> EntryNameTable;
}
public RomOVT[] MainOvt;
public RomOVT[] SubOvt;
[Serializable]
public class RomOVT
{
[Flags]
public enum OVTFlag : byte
{
Compressed = 1,
AuthenticationCode = 2
}
public RomOVT() { }
public RomOVT(EndianBinaryReader er)
{
Id = er.ReadUInt32();
RamAddress = er.ReadUInt32();
RamSize = er.ReadUInt32();
BssSize = er.ReadUInt32();
SinitInit = er.ReadUInt32();
SinitInitEnd = er.ReadUInt32();
FileId = er.ReadUInt32();
UInt32 tmp = er.ReadUInt32();
Compressed = tmp & 0xFFFFFF;
Flag = (OVTFlag)(tmp >> 24);
}
public void Write(EndianBinaryWriter er)
{
er.Write(Id);
er.Write(RamAddress);
er.Write(RamSize);
er.Write(BssSize);
er.Write(SinitInit);
er.Write(SinitInitEnd);
er.Write(FileId);
er.Write((uint)((((uint)Flag) & 0xFF) << 24 | (Compressed & 0xFFFFFF)));
}
[XmlAttribute]
public UInt32 Id;
public UInt32 RamAddress;
public UInt32 RamSize;
public UInt32 BssSize;
public UInt32 SinitInit;
public UInt32 SinitInitEnd;
[XmlIgnore]
public UInt32 FileId;
public UInt32 Compressed;//:24;
[XmlAttribute]
public OVTFlag Flag;// :8;
}
public FileAllocationEntry[] Fat;
public RomBanner Banner;
[Serializable]
public class RomBanner
{
public RomBanner() { }
public RomBanner(EndianBinaryReader er)
{
Header = new BannerHeader(er);
Banner = new BannerV1(er);
}
public void Write(EndianBinaryWriter er)
{
Header.CRC16_v1 = Banner.GetCRC();
Header.Write(er);
Banner.Write(er);
}
public BannerHeader Header;
[Serializable]
public class BannerHeader
{
public BannerHeader() { }
public BannerHeader(EndianBinaryReader er)
{
Version = er.ReadByte();
ReservedA = er.ReadByte();
CRC16_v1 = er.ReadUInt16();
ReservedB = er.ReadBytes(28);
}
public void Write(EndianBinaryWriter er)
{
er.Write(Version);
er.Write(ReservedA);
er.Write(CRC16_v1);
er.Write(ReservedB, 0, 28);
}
public Byte Version;
public Byte ReservedA;
[XmlIgnore]
public UInt16 CRC16_v1;
public Byte[] ReservedB;//28
}
public BannerV1 Banner;
[Serializable]
public class BannerV1
{
public BannerV1() { }
public BannerV1(EndianBinaryReader er)
{
Image = er.ReadBytes(32 * 32 / 2);
Pltt = er.ReadBytes(16 * 2);
GameName = new string[6];
GameName[0] = er.ReadString(Encoding.Unicode, 128).Replace("\0", "");
GameName[1] = er.ReadString(Encoding.Unicode, 128).Replace("\0", "");
GameName[2] = er.ReadString(Encoding.Unicode, 128).Replace("\0", "");
GameName[3] = er.ReadString(Encoding.Unicode, 128).Replace("\0", "");
GameName[4] = er.ReadString(Encoding.Unicode, 128).Replace("\0", "");
GameName[5] = er.ReadString(Encoding.Unicode, 128).Replace("\0", "");
}
public void Write(EndianBinaryWriter er)
{
er.Write(Image, 0, 32 * 32 / 2);
er.Write(Pltt, 0, 16 * 2);
foreach (string s in GameName) er.Write(GameName[0].PadRight(128, '\0'), Encoding.Unicode, false);
}
public Byte[] Image;//32*32/2
public Byte[] Pltt;//16*2
[XmlIgnore]
public String[] GameName;//6, 128 chars (UTF16-LE)
[XmlElement("GameName")]
public String[] Base64GameName
{
get
{
String[] b = new String[6];
for (int i = 0; i < 6; i++)
{
b[i] = Convert.ToBase64String(Encoding.Unicode.GetBytes(GameName[i]));
}
return b;
}
set
{
GameName = new string[6];
for (int i = 0; i < 6; i++)
{
GameName[i] = Encoding.Unicode.GetString(Convert.FromBase64String(value[i]));
}
}
}
public ushort GetCRC()
{
byte[] Data = new byte[2080];
Array.Copy(Image, Data, 512);
Array.Copy(Pltt, 0, Data, 512, 32);
Array.Copy(Encoding.Unicode.GetBytes(GameName[0].PadRight(128, '\0')), 0, Data, 544, 256);
Array.Copy(Encoding.Unicode.GetBytes(GameName[1].PadRight(128, '\0')), 0, Data, 544 + 256, 256);
Array.Copy(Encoding.Unicode.GetBytes(GameName[2].PadRight(128, '\0')), 0, Data, 544 + 256 * 2, 256);
Array.Copy(Encoding.Unicode.GetBytes(GameName[3].PadRight(128, '\0')), 0, Data, 544 + 256 * 3, 256);
Array.Copy(Encoding.Unicode.GetBytes(GameName[4].PadRight(128, '\0')), 0, Data, 544 + 256 * 4, 256);
Array.Copy(Encoding.Unicode.GetBytes(GameName[5].PadRight(128, '\0')), 0, Data, 544 + 256 * 5, 256);
return CRC16.GetCRC16(Data);
}
}
}
public Byte[][] FileData;
public Byte[] RSASignature;
public void FromFileSystem(SFSDirectory Root)
{
int did = 0;
int fid = MainOvt.Length + SubOvt.Length;
Root.UpdateIDs(ref did, ref fid);
//FATB.numFiles = (ushort)Root.TotalNrSubFiles;
//List<byte> Data = new List<byte>();
uint nrfiles = Root.TotalNrSubFiles;
FileAllocationEntry[] overlays = new FileAllocationEntry[MainOvt.Length + SubOvt.Length];
Array.Copy(Fat, overlays, MainOvt.Length + SubOvt.Length);
Fat = new FileAllocationEntry[(MainOvt.Length + SubOvt.Length) + nrfiles];
Array.Copy(overlays, Fat, MainOvt.Length + SubOvt.Length);
byte[][] overlaydata = new byte[MainOvt.Length + SubOvt.Length][];
Array.Copy(FileData, overlaydata, MainOvt.Length + SubOvt.Length);
FileData = new byte[(MainOvt.Length + SubOvt.Length) + nrfiles][];
Array.Copy(overlaydata, FileData, MainOvt.Length + SubOvt.Length);
//FATB.allocationTable.Clear();
for (ushort i = (ushort)(MainOvt.Length + SubOvt.Length); i < nrfiles + MainOvt.Length + SubOvt.Length; i++)
{
var f = Root.GetFileByID(i);
Fat[i] = new FileAllocationEntry(0, 0);
FileData[i] = f.Data;
}
Fnt.DirectoryTable.Clear();
NitroFSUtil.GenerateDirectoryTable(Fnt.DirectoryTable, Root);
uint offset2 = Fnt.DirectoryTable[0].dirEntryStart;
ushort fileId = (ushort)(MainOvt.Length + SubOvt.Length);//0;
Fnt.EntryNameTable.Clear();
NitroFSUtil.GenerateEntryNameTable(Fnt.DirectoryTable, Fnt.EntryNameTable, Root, ref offset2, ref fileId);
}
public SFSDirectory ToFileSystem()
{
bool treereconstruct = false;//Some programs do not write the Directory Table well, so sometimes I need to reconstruct the tree based on the fnt, which is bad!
List<SFSDirectory> dirs = new List<SFSDirectory>();
dirs.Add(new SFSDirectory("/", true));
dirs[0].DirectoryID = 0xF000;
uint nrdirs = Fnt.DirectoryTable[0].dirParentID;
for (int i = 1; i < nrdirs; i++)
{
dirs.Add(new SFSDirectory((ushort)(0xF000 + i)));
}
for (int i = 1; i < nrdirs; i++)
{
if (Fnt.DirectoryTable[i].dirParentID - 0xF000 == i)
{
treereconstruct = true;
foreach (var v in dirs)
{
v.Parent = null;
}
break;
}
dirs[i].Parent = dirs[Fnt.DirectoryTable[i].dirParentID - 0xF000];
}
if (!treereconstruct)
{
for (int i = 0; i < nrdirs; i++)
{
for (int j = 0; j < nrdirs; j++)
{
if (dirs[i] == dirs[j].Parent)
{
dirs[i].SubDirectories.Add(dirs[j]);
}
}
}
}
uint offset = nrdirs * 8;
ushort fileid = Fnt.DirectoryTable[0].dirEntryFileID;
SFSDirectory curdir = null;
foreach (EntryNameTableEntry e in Fnt.EntryNameTable)
{
for (int i = 0; i < nrdirs; i++)
{
if (offset == Fnt.DirectoryTable[i].dirEntryStart)
{
curdir = dirs[i];
break;
}
}
if (e is EntryNameTableEndOfDirectoryEntry)
{
curdir = null;
offset++;
}
else if (e is EntryNameTableFileEntry)
{
curdir.Files.Add(new SFSFile(fileid++, ((EntryNameTableFileEntry)e).entryName, curdir));
offset += 1u + e.entryNameLength;
}
else if (e is EntryNameTableDirectoryEntry)
{
if (treereconstruct)
{
dirs[((EntryNameTableDirectoryEntry)e).directoryID - 0xF000].Parent = curdir;
curdir.SubDirectories.Add(dirs[((EntryNameTableDirectoryEntry)e).directoryID - 0xF000]);
}
dirs[((EntryNameTableDirectoryEntry)e).directoryID - 0xF000].DirectoryName = ((EntryNameTableDirectoryEntry)e).entryName;
offset += 3u + (e.entryNameLength & 0x7Fu);
}
}
for (int i = (MainOvt.Length + SubOvt.Length); i < Fat.Length; i++)
{
//byte[] data = new byte[fat[i].fileSize];
//Array.Copy(FileData FIMG.fileImage, fat[i].fileTop, data, 0, data.Length);
dirs[0].GetFileByID((ushort)i).Data = FileData[i];//data;
}
return dirs[0];
}
public byte[] GetDecompressedARM9()
{
if (StaticFooter != null) return ARM9.Decompress(MainRom, StaticFooter._start_ModuleParamsOffset);
else return ARM9.Decompress(MainRom);
}
}
}

165
dspatch/Nitro/NitroFS.cs Normal file
View File

@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using dspatch.IO;
namespace dspatch.Nitro
{
public class FileAllocationEntry
{
public FileAllocationEntry(UInt32 Offset, UInt32 Size)
{
fileTop = Offset;
fileBottom = Offset + Size;
}
public FileAllocationEntry(EndianBinaryReader er)
{
fileTop = er.ReadUInt32();
fileBottom = er.ReadUInt32();
}
public void Write(EndianBinaryWriter er)
{
er.Write(fileTop);
er.Write(fileBottom);
}
public UInt32 fileTop;
public UInt32 fileBottom;
public UInt32 fileSize
{
get { return fileBottom - fileTop; }
}
}
public class DirectoryTableEntry
{
public DirectoryTableEntry() { }
public DirectoryTableEntry(EndianBinaryReader er)
{
dirEntryStart = er.ReadUInt32();
dirEntryFileID = er.ReadUInt16();
dirParentID = er.ReadUInt16();
}
public void Write(EndianBinaryWriter er)
{
er.Write(dirEntryStart);
er.Write(dirEntryFileID);
er.Write(dirParentID);
}
public UInt32 dirEntryStart;
public UInt16 dirEntryFileID;
public UInt16 dirParentID;
}
public class EntryNameTableEntry
{
protected EntryNameTableEntry() { }
public EntryNameTableEntry(EndianBinaryReader er)
{
entryNameLength = er.ReadByte();
}
public virtual void Write(EndianBinaryWriter er)
{
er.Write(entryNameLength);
}
public Byte entryNameLength;
}
public class EntryNameTableEndOfDirectoryEntry : EntryNameTableEntry
{
public EntryNameTableEndOfDirectoryEntry() { }
public EntryNameTableEndOfDirectoryEntry(EndianBinaryReader er)
: base(er) { }
public override void Write(EndianBinaryWriter er)
{
base.Write(er);
}
}
public class EntryNameTableFileEntry : EntryNameTableEntry
{
public EntryNameTableFileEntry(String Name)
{
entryNameLength = (byte)Name.Length;
entryName = Name;
}
public EntryNameTableFileEntry(EndianBinaryReader er)
: base(er)
{
entryName = er.ReadString(Encoding.ASCII, entryNameLength);
}
public override void Write(EndianBinaryWriter er)
{
base.Write(er);
er.Write(entryName, Encoding.ASCII, false);
}
public String entryName;
}
public class EntryNameTableDirectoryEntry : EntryNameTableEntry
{
public EntryNameTableDirectoryEntry(String Name, UInt16 DirectoryID)
{
entryNameLength = (byte)(Name.Length | 0x80);
entryName = Name;
directoryID = DirectoryID;
}
public EntryNameTableDirectoryEntry(EndianBinaryReader er)
: base(er)
{
entryName = er.ReadString(Encoding.ASCII, entryNameLength & 0x7F);
directoryID = er.ReadUInt16();
}
public override void Write(EndianBinaryWriter er)
{
base.Write(er);
er.Write(entryName, Encoding.ASCII, false);
er.Write(directoryID);
}
public String entryName;
public UInt16 directoryID;
}
public class NitroFSUtil
{
public static void GenerateDirectoryTable(List<DirectoryTableEntry> directoryTable, SFSDirectory dir)
{
DirectoryTableEntry cur = new DirectoryTableEntry();
if (dir.IsRoot)
{
cur.dirParentID = (ushort)(dir.TotalNrSubDirectories + 1);
cur.dirEntryStart = cur.dirParentID * 8u;
}
else cur.dirParentID = dir.Parent.DirectoryID;
dir.DirectoryID = (ushort)(0xF000 + directoryTable.Count);
directoryTable.Add(cur);
foreach (SFSDirectory d in dir.SubDirectories)
{
GenerateDirectoryTable(directoryTable, d);
}
}
public static void GenerateEntryNameTable(List<DirectoryTableEntry> directoryTable, List<EntryNameTableEntry> entryNameTable, SFSDirectory dir, ref uint Offset, ref ushort FileId)
{
directoryTable[dir.DirectoryID - 0xF000].dirEntryStart = Offset;
directoryTable[dir.DirectoryID - 0xF000].dirEntryFileID = FileId;
foreach (SFSDirectory d in dir.SubDirectories)
{
entryNameTable.Add(new EntryNameTableDirectoryEntry(d.DirectoryName, d.DirectoryID));
Offset += (uint)d.DirectoryName.Length + 3u;
}
foreach (SFSFile f in dir.Files)
{
f.FileID = FileId;
entryNameTable.Add(new EntryNameTableFileEntry(f.FileName));
Offset += (uint)f.FileName.Length + 1u;
FileId++;
}
entryNameTable.Add(new EntryNameTableEndOfDirectoryEntry());
Offset++;
foreach (SFSDirectory d in dir.SubDirectories)
{
GenerateEntryNameTable(directoryTable, entryNameTable, d, ref Offset, ref FileId);
}
}
}
}

360
dspatch/Program.cs Normal file
View File

@ -0,0 +1,360 @@
using dspatch.DS;
using dspatch.IO;
using dspatch.Nitro;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace dspatch
{
class Program
{
static byte[] lockIconImage =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x8A,
0x00, 0xA0, 0x88, 0x88, 0x00, 0x88, 0x57, 0x34, 0x80, 0x58, 0x33, 0x33,
0x8A, 0x34, 0x04, 0x00, 0xA0, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xA8, 0x0A, 0x00, 0x00, 0x88, 0x88, 0x0A, 0x00,
0x43, 0x75, 0x88, 0x00, 0x33, 0x33, 0x85, 0x08, 0x00, 0x40, 0x43, 0x88,
0x00, 0x00, 0x37, 0x85, 0x00, 0x00, 0x40, 0x73, 0x00, 0x00, 0x00, 0x53,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33,
0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33,
0x00, 0x00, 0x00, 0x33, 0xDD, 0xDD, 0xDD, 0xDD, 0xA8, 0x00, 0x00, 0x00,
0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
0xDD, 0xED, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x29, 0xFF, 0xFF, 0xBF, 0x11, 0xFF, 0xFF, 0x9F, 0x11,
0xFF, 0xFF, 0xBF, 0x11, 0xFF, 0xFF, 0xFF, 0x16, 0xDD, 0xDD, 0xDD, 0xDD,
0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0x92, 0xDD, 0xDD, 0xDD,
0x11, 0xDB, 0xDD, 0xDD, 0x11, 0xD9, 0xDD, 0xDD, 0x11, 0xDB, 0xDD, 0xDD,
0x61, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0x00, 0x00, 0xDD, 0xDD, 0x00, 0x00,
0xDD, 0xDD, 0x00, 0x00, 0xDD, 0xDD, 0x00, 0x00, 0xDD, 0xDD, 0x00, 0x00,
0xDD, 0xDD, 0x00, 0x00, 0xDD, 0xDD, 0x00, 0x00, 0xDD, 0xDD, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x16,
0xFF, 0xFF, 0xFF, 0x12, 0xFF, 0xFF, 0xCF, 0x11, 0xFF, 0xFF, 0x9F, 0x11,
0xFF, 0xFF, 0x9F, 0x11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xDD, 0xDD, 0xDD, 0x21, 0xDD, 0xDD, 0xDD,
0x11, 0xDB, 0xDD, 0xDD, 0x11, 0xD9, 0xDD, 0xDD, 0x11, 0xD9, 0xDD, 0xDD,
0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
0xDD, 0xDD, 0x00, 0x00, 0xDD, 0xDD, 0x00, 0x00, 0xDD, 0xDD, 0x00, 0x00,
0xDD, 0xDD, 0x00, 0x00, 0xDD, 0xDD, 0x00, 0x00, 0xDD, 0xDD, 0x00, 0x00,
0xDD, 0xDD, 0x00, 0x00, 0xDD, 0xED, 0x00, 0x00
};
static byte[] lockIconPltt =
{
0x1C, 0x3C, 0x07, 0x25, 0x49, 0x25, 0xAA, 0x3D, 0xCB, 0x3D, 0xEC, 0x41,
0xEF, 0x25, 0x0E, 0x4A, 0x2F, 0x4E, 0x74, 0x26, 0x71, 0x56, 0xF9, 0x2A,
0x5B, 0x2F, 0x5E, 0x1B, 0x7E, 0x37, 0xBF, 0x2F
};
static byte[] haxxStationIconImage =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x90, 0x61, 0x66, 0x66, 0x69, 0x66, 0x66, 0x66, 0x61, 0xC8, 0xCC, 0xCC,
0x66, 0xCC, 0xCC, 0x6C, 0x66, 0xCC, 0xCC, 0x6C, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0xCC, 0xCC, 0xCC, 0xCC, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x16, 0x09, 0x66, 0x66, 0x66, 0x96,
0xCC, 0xCC, 0x8C, 0x16, 0xC6, 0xCC, 0xCC, 0x66, 0xC6, 0xCC, 0xCC, 0x66,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0xCC, 0xCC, 0x6C,
0x66, 0xCC, 0xCC, 0x6C, 0x66, 0xCC, 0xCC, 0x6C, 0x66, 0xCC, 0xCC, 0x6C,
0x66, 0xCC, 0xCC, 0x6C, 0x66, 0x8C, 0xCC, 0x6C, 0x45, 0x65, 0x41, 0x85,
0x89, 0xE2, 0xC0, 0x27, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x27, 0x16, 0x54, 0xB7, 0xD5, 0xE0, 0x9D, 0x14,
0xC6, 0xCC, 0xCC, 0x66, 0xC6, 0xCC, 0xCC, 0x66, 0xC6, 0xCC, 0xCC, 0x66,
0xC6, 0xCC, 0xCC, 0x66, 0xC6, 0xCC, 0xCC, 0x66, 0xC6, 0x99, 0xCC, 0x66,
0x5C, 0x11, 0xC5, 0x66, 0x42, 0xEE, 0x24, 0x44, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x20, 0x0E, 0x00,
0x00, 0x79, 0x00, 0x0B, 0x00, 0x01, 0x70, 0x08, 0xD0, 0x04, 0x2E, 0x00,
0xB0, 0x07, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0x00, 0x77, 0x0C,
0x24, 0x87, 0x52, 0x14, 0x66, 0x14, 0xC9, 0x6C, 0x66, 0xAC, 0xCC, 0x6C,
0x66, 0xCC, 0xC1, 0x6C, 0x66, 0x1C, 0x16, 0x6C, 0x66, 0xCC, 0xC1, 0x6C,
0x00, 0x00, 0x00, 0x00, 0x80, 0x21, 0xA7, 0x00, 0x41, 0x88, 0x47, 0x41,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xF3, 0x63, 0x66, 0x66,
0x36, 0x3F, 0x66, 0x66, 0x36, 0x3F, 0x66, 0x66, 0x0D, 0x00, 0xD0, 0x99,
0x00, 0x88, 0x00, 0x00, 0x18, 0x44, 0x81, 0x99, 0x76, 0xCC, 0x57, 0x44,
0xC6, 0xCC, 0xCC, 0x66, 0xC6, 0x6C, 0xCC, 0x66, 0xC6, 0xC6, 0xC6, 0x66,
0xC6, 0x6C, 0xCC, 0x66, 0x90, 0x09, 0x89, 0x00, 0xB0, 0x07, 0x4D, 0x00,
0xD0, 0x04, 0x10, 0x00, 0x00, 0x01, 0x80, 0x05, 0x00, 0x78, 0x00, 0x0E,
0x00, 0x10, 0x0D, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
0x66, 0xCC, 0xCC, 0x6C, 0x66, 0xCC, 0xCC, 0x6C, 0x66, 0xCC, 0xCC, 0x6C,
0x61, 0xC8, 0xCC, 0xCC, 0x68, 0x66, 0x66, 0x66, 0x80, 0x61, 0x66, 0x66,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF3, 0x63, 0xFF, 0x6F,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xCC, 0xCC, 0xCC, 0xCC,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xC6, 0xCC, 0xCC, 0x66, 0xC6, 0xCC, 0xCC, 0x66,
0xC6, 0xCC, 0xCC, 0x66, 0xCC, 0xCC, 0x8C, 0x16, 0x66, 0x66, 0x66, 0x86,
0x66, 0x66, 0x16, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static byte[] haxxStationIconPltt =
{
0x1F, 0x7C, 0x42, 0x04, 0x83, 0x0C, 0xC5, 0x08, 0xC6, 0x18, 0xE7, 0x1C,
0x00, 0x00, 0x29, 0x25, 0x8C, 0x2D, 0xAD, 0x35, 0xCE, 0x39, 0x0F, 0x3E,
0x10, 0x42, 0x51, 0x46, 0x94, 0x52, 0x35, 0x23
};
static byte[] haxxStationServer =
{
0x52, 0x43, 0x31, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x30, 0x31, 0x20,
0x32, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x65, 0x72, 0x69,
0x63, 0x6F, 0x6D, 0x00, 0x00, 0x00, 0x48, 0x61, 0x78, 0x78, 0x53, 0x74,
0x61, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x79, 0x20, 0x47, 0x65, 0x72,
0x69, 0x63, 0x6F, 0x6D, 0x2C, 0x20, 0x73, 0x68, 0x75, 0x74, 0x74, 0x65,
0x72, 0x62, 0x75, 0x67, 0x32, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x6E, 0x64, 0x20, 0x41, 0x70,
0x61, 0x63, 0x68, 0x65, 0x20, 0x54, 0x68, 0x75, 0x6E, 0x64, 0x65, 0x72,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static byte[] dsHash =
{
0xF1, 0x8B, 0x55, 0xF3, 0xE1, 0x25, 0x9C, 0x03, 0xE1, 0x0D, 0x0E, 0xCB,
0x54, 0x96, 0x93, 0xB4, 0x29, 0x05, 0xCE, 0xB5
};
static byte[] exploitData =
{
0x44, 0x30, 0x9F, 0xE5, 0x2C, 0x00, 0x93, 0xE5, 0x2C, 0x10, 0x9F, 0xE5,
0x01, 0x00, 0x40, 0xE0, 0x28, 0x10, 0x9F, 0xE5, 0x01, 0x00, 0x80, 0xE0,
0x24, 0x10, 0x9F, 0xE5, 0x24, 0x20, 0x9F, 0xE5, 0x28, 0xE0, 0x9F, 0xE5,
0x3E, 0xFF, 0x2F, 0xE1, 0x24, 0xE0, 0x9F, 0xE5, 0x3E, 0xFF, 0x2F, 0xE1,
0x01, 0x00, 0xA0, 0xE3, 0x1C, 0xE0, 0x9F, 0xE5, 0x1E, 0xFF, 0x2F, 0xE1,
0x00, 0xAE, 0x11, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x20, 0x6D, 0x11, 0x00, 0x40, 0xE8, 0x3F, 0x02, 0x08, 0xBB, 0x32, 0x02,
0x24, 0xAF, 0x32, 0x02, 0x78, 0x3A, 0x32, 0x02
};
static byte[] arm7Fix =
{
0x2C, 0x00, 0x9F, 0xE5, 0x8E, 0x07, 0x80, 0xE2, 0x1C, 0x10, 0x9F, 0xE5,
0x1C, 0x20, 0x9F, 0xE5, 0x01, 0x30, 0xD0, 0xE4, 0x01, 0x30, 0xC1, 0xE4,
0x01, 0x20, 0x52, 0xE2, 0xFB, 0xFF, 0xFF, 0xCA, 0x00, 0x00, 0x9F, 0xE5,
0x10, 0xFF, 0x2F, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00
};
static void PrintUsage()
{
Console.WriteLine("Usage: dspatch download_station.nds rom_to_boot.nds result.nds");
}
static void Main(string[] args)
{
Console.WriteLine("== DS Download Station Patcher v1.0 ==");
Console.WriteLine("Exploit by Gericom, shutterbug2000 and Apache Thunder\n");
if (args.Length != 3)
PrintUsage();
else
{
byte[] dsdata = File.ReadAllBytes(args[0]);
byte[] sha1 = SHA1.Create().ComputeHash(dsdata);
for (int i = 0; i < 20; i++)
{
if (sha1[i] != dsHash[i])
{
Console.WriteLine("Error: Invalid download station rom!");
Console.WriteLine("The patcher is only compatible with:");
Console.WriteLine("xxxx - DS Download Station - Volume 1 (Kiosk WiFi Demo Cart) (U)(Independent).nds");
Console.WriteLine("SHA1: F18B55F3E1259C03E10D0ECB549693B42905CEB5");
return;
}
}
NDS downloadStation = new NDS(dsdata);
var fs = downloadStation.ToFileSystem();
byte[] newRom = File.ReadAllBytes(args[1]);
NDS newRom2 = new NDS(newRom);
if (newRom2.Header.SubRamAddress >= 0x03000000)
{
byte[] newArm7 = new byte[newRom2.SubRom.Length + arm7Fix.Length];
Array.Copy(arm7Fix, newArm7, arm7Fix.Length);
Array.Copy(newRom2.SubRom, 0, newArm7, arm7Fix.Length, newRom2.SubRom.Length);
IOUtil.WriteU32LE(newArm7, 0x28, newRom2.Header.SubEntryAddress);
IOUtil.WriteU32LE(newArm7, 0x2C, newRom2.Header.SubRamAddress);
IOUtil.WriteU32LE(newArm7, 0x30, newRom2.Header.SubSize);
newRom2.SubRom = newArm7;
newRom2.Header.SubSize = (uint)newArm7.Length;
newRom2.Header.SubRamAddress = 0x02380000;
newRom2.Header.SubEntryAddress = 0x02380000;
}
byte[] newRomFixed = newRom2.Write(true);
{
uint arm9offset = (uint)(newRomFixed[0x20] | (newRomFixed[0x21] << 8) | (newRomFixed[0x22] << 16) | (newRomFixed[0x23] << 24));
uint arm9loadaddr = (uint)(newRomFixed[0x28] | (newRomFixed[0x29] << 8) | (newRomFixed[0x2A] << 16) | (newRomFixed[0x2B] << 24));
uint arm9size = (uint)(newRomFixed[0x2C] | (newRomFixed[0x2D] << 8) | (newRomFixed[0x2E] << 16) | (newRomFixed[0x2F] << 24));
uint arm7offset = (uint)(newRomFixed[0x30] | (newRomFixed[0x31] << 8) | (newRomFixed[0x32] << 16) | (newRomFixed[0x33] << 24));
//arm9 offset becomes 0x180
newRomFixed[0x20] = 0x80;
newRomFixed[0x21] = 0x01;
newRomFixed[0x22] = 0x00;
newRomFixed[0x23] = 0x00;
//arm9 load becomes 0x02332C40 (rsa_GetDecodedHash)
newRomFixed[0x28] = 0x40;
newRomFixed[0x29] = 0x2C;
newRomFixed[0x2A] = 0x33;
newRomFixed[0x2B] = 0x02;
//arm9 size becomes 0x100
newRomFixed[0x2C] = 0x00;
newRomFixed[0x2D] = 0x01;
newRomFixed[0x2E] = 0x00;
newRomFixed[0x2F] = 0x00;
ushort newcrc = CRC16.GetCRC16(newRomFixed, 0, 0x15E);
newRomFixed[0x15E] = (byte)(newcrc & 0xFF);
newRomFixed[0x15F] = (byte)(newcrc >> 8);
exploitData[0x3C] = (byte)(arm7offset & 0xFF);
exploitData[0x3D] = (byte)((arm7offset >> 8) & 0xFF);
exploitData[0x3E] = (byte)((arm7offset >> 16) & 0xFF);
exploitData[0x3F] = (byte)((arm7offset >> 24) & 0xFF);
exploitData[0x40] = (byte)(arm9offset & 0xFF);
exploitData[0x41] = (byte)((arm9offset >> 8) & 0xFF);
exploitData[0x42] = (byte)((arm9offset >> 16) & 0xFF);
exploitData[0x43] = (byte)((arm9offset >> 24) & 0xFF);
exploitData[0x44] = (byte)(arm9loadaddr & 0xFF);
exploitData[0x45] = (byte)((arm9loadaddr >> 8) & 0xFF);
exploitData[0x46] = (byte)((arm9loadaddr >> 16) & 0xFF);
exploitData[0x47] = (byte)((arm9loadaddr >> 24) & 0xFF);
exploitData[0x48] = (byte)(arm9size & 0xFF);
exploitData[0x49] = (byte)((arm9size >> 8) & 0xFF);
exploitData[0x4A] = (byte)((arm9size >> 16) & 0xFF);
exploitData[0x4B] = (byte)((arm9size >> 24) & 0xFF);
Array.Copy(exploitData, 0, newRomFixed, 0x180, exploitData.Length);
}
SFSDirectory d = fs.GetDirectoryByPath("//ds_demo");
d.Files.Add(new SFSFile(-1, "rom1.nds", d) { Data = newRomFixed });
//this is shitty...
d.Files.Remove(d.GetFileByPath("ds_demo/ANDEdemo"));
d.Files.Remove(d.GetFileByPath("ds_demo/AGFEdemo"));
d.Files.Remove(d.GetFileByPath("ds_demo/AMFEclip"));
d.Files.Remove(d.GetFileByPath("ds_demo/AMTEdemo"));
d.Files.Remove(d.GetFileByPath("ds_demo/APTEdemo"));
d.Files.Remove(d.GetFileByPath("ds_demo/ATTEdemo"));
d.Files.Remove(d.GetFileByPath("ds_demo/ATTEpush"));
d.Files.Remove(d.GetFileByPath("ds_demo/testcode"));
d.Files.Remove(d.GetFileByPath("ds_demo/AMCEdemo"));
DemoMenu menu = new DemoMenu();
menu.entries = new DemoMenu.DemoMenuEntry[]
{
new DemoMenu.DemoMenuEntry()
{
rating = 1,
guideMode = 0x15,
touchText1 = "HaxxStation by Gericom,",
touchText2 = "shutterbug2000, Apache Thunder",
internalName = "rom1.nds"
},
new DemoMenu.DemoMenuEntry()
{
bannerImage = new byte[512],
bannerPalette = new byte[32],
bannerText1 = "",
bannerText2 = "",
rating = 1,
guideMode = 0x15,
touchText1 = "HaxxStation by Gericom,",
touchText2 = "shutterbug2000, Apache Thunder",
internalName = "rom1.nds"
},
new DemoMenu.DemoMenuEntry()
{
bannerImage = new byte[512],
bannerPalette = new byte[32],
bannerText1 = "",
bannerText2 = "",
rating = 1,
guideMode = 0x15,
touchText1 = "HaxxStation by Gericom,",
touchText2 = "shutterbug2000, Apache Thunder",
internalName = "rom1.nds"
}
};
if (newRom2.Banner != null)
{
string[] lines = newRom2.Banner.Banner.GameName[1].Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
if (lines.Length > 0)
menu.entries[0].bannerText1 = lines[0];
else
menu.entries[0].bannerText1 = "Bannerless Homebrew";
if (lines.Length > 1)
menu.entries[0].bannerText2 = lines[1];
else
menu.entries[0].bannerText2 = "";
menu.entries[0].bannerImage = newRom2.Banner.Banner.Image;
menu.entries[0].bannerPalette = newRom2.Banner.Banner.Pltt;
}
else
{
menu.entries[0].bannerText1 = "Bannerless Homebrew";
menu.entries[0].bannerText2 = "";
menu.entries[0].bannerImage = new byte[512];
menu.entries[0].bannerPalette = new byte[32];
}
byte[] demoMenu = menu.Write();
fs.GetFileByPath("//ds_demo/demomenu").Data = demoMenu;
fs.GetFileByPath("//mb/server").Data = haxxStationServer;
fs.GetFileByPath("//mb/icon.nbfc").Data = haxxStationIconImage;
fs.GetFileByPath("//mb/icon.nbfp").Data = haxxStationIconPltt;
downloadStation.FromFileSystem(fs);
//change banner
downloadStation.Banner.Banner.Image = haxxStationIconImage;
downloadStation.Banner.Banner.Pltt = haxxStationIconPltt;
for(int i = 0; i < 6; i++)
downloadStation.Banner.Banner.GameName[i] = "HaxxStation\nBy Gericom, shutterbug2000\nand Apache Thunder";
byte[] finalResult = downloadStation.Write();
File.Create(args[2]).Close();
File.WriteAllBytes(args[2], finalResult);
}
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("DS Download Station Patcher")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DS Download Station Patcher")]
[assembly: AssemblyCopyright("Copyright © 2017 Gericom, shutterbug2000 and Apache Thunder")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c8cae5e5-40a9-4840-ae8a-abbd2ae50749")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

75
dspatch/dspatch.csproj Normal file
View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C8CAE5E5-40A9-4840-AE8A-ABBD2AE50749}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>dspatch</RootNamespace>
<AssemblyName>dspatch</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="DS\DemoMenu.cs" />
<Compile Include="IO\IOUtil.cs" />
<Compile Include="Nitro\ARM9.cs" />
<Compile Include="Nitro\CRC16.cs" />
<Compile Include="IO\EndianBinaryReader.cs" />
<Compile Include="IO\EndianBinaryWriter.cs" />
<Compile Include="Nitro\CRT0.cs" />
<Compile Include="Nitro\NDS.cs" />
<Compile Include="Nitro\NitroFS.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="IO\SFSDirectory.cs" />
<Compile Include="IO\SFSFile.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Folder Include="bin\Debug\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>