Refactor to add in 3ds rebuilding code

Big thanks to SciresM for writing the IO code from 3DS-Builder. Improved
a bit to make use of static methods (no more void methods with no
inputs!)
This commit is contained in:
Kurt
2015-05-15 23:24:14 -07:00
parent e8f63b8d27
commit d252b38f05
13 changed files with 4453 additions and 28 deletions

97
pk3DS/3DS/AES.cs Normal file
View File

@@ -0,0 +1,97 @@
using System;
using System.Linq;
using System.Security.Cryptography;
namespace CTR
{
public class AesCtr
{
private AesManaged Aes = new AesManaged();
private ICryptoTransform Encryptor;
private AesCounter Counter;
public AesCtr(byte[] key, byte[] iv)
{
Aes.Key = key;
Aes.Mode = CipherMode.ECB;
Aes.Padding = PaddingMode.None;
Counter = new AesCounter(iv);
Encryptor = Aes.CreateEncryptor();
}
public AesCtr(byte[] key, ulong PartitionID, ulong InitialCount)
{
Aes.Key = key;
Aes.Mode = CipherMode.ECB;
Aes.Padding = PaddingMode.None;
Counter = new AesCounter(PartitionID, InitialCount);
Encryptor = Aes.CreateEncryptor();
}
public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
int BlockLength;
for (int i = 0; i < inputCount; i += BlockLength)
{
BlockLength = inputCount - i > AesCounter.BufferSize ? AesCounter.BufferSize : inputCount - i;
Encryptor.TransformBlock(Counter.ManageBufferCounters(BlockLength), 0, BlockLength, outputBuffer, outputOffset + i);
for (int BlockWalker = i; BlockWalker < (i + BlockLength); BlockWalker += 8)
{
Array.Copy(BitConverter.GetBytes(BitConverter.ToInt64(outputBuffer, outputOffset + BlockWalker) ^ BitConverter.ToInt64(inputBuffer, inputOffset + BlockWalker)), 0, outputBuffer, outputOffset + BlockWalker, 8);
}
}
return inputCount;
}
}
public class AesCounter
{
public const int BufferSize = 0x400000; //4 MB Buffer
private byte[] Counter = new byte[0x10];
private byte[] Buffer = new byte[BufferSize];
public AesCounter(ulong high, ulong low)
{
Array.Copy(BitConverter.GetBytes(high).Reverse().ToArray(), Counter, 0x8);
Array.Copy(BitConverter.GetBytes(low).Reverse().ToArray(), 0, Counter, 0x8, 0x8);
}
public AesCounter(byte[] iv)
{
Array.Copy(BitConverter.GetBytes(BitConverter.ToUInt64(iv, 0)).Reverse().ToArray(), Counter, 0x10);
}
public void Increment()
{
for (int i = Counter.Length - 1; i >= 0; i--)
{
if ((++Counter[i]) != 0)
return;
}
}
public byte[] ManageBufferCounters(int size)
{
for (int i = 0; i < size; i += 0x10)
{
Array.Copy(Counter, 0, Buffer, i, 0x10);
Increment();
}
return Buffer;
}
public ulong SwapBytes(ulong value)
{
ulong uvalue = value;
ulong swapped =
((0x00000000000000FF) & (uvalue >> 56)
| (0x000000000000FF00) & (uvalue >> 40)
| (0x0000000000FF0000) & (uvalue >> 24)
| (0x00000000FF000000) & (uvalue >> 8)
| (0x000000FF00000000) & (uvalue << 8)
| (0x0000FF0000000000) & (uvalue << 24)
| (0x00FF000000000000) & (uvalue << 40)
| (0xFF00000000000000) & (uvalue << 56));
return swapped;
}
}
}

View File

@@ -24,7 +24,7 @@
using System.IO;
using System.Windows.Forms;
namespace blz
namespace BLZ
{
public class BLZCoder
{

344
pk3DS/3DS/CTR.cs Normal file
View File

@@ -0,0 +1,344 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;
using pk3DS.Properties;
namespace CTR
{
public class CTR
{
public const uint MEDIA_UNIT_SIZE = 0x200;
// Main wrapper that assembles the ROM based on the following specifications:
internal static bool buildROM(bool Card2, string LOGO_NAME,
string EXEFS_PATH, string ROMFS_PATH, string EXHEADER_PATH,
string SERIAL_TEXT, string SAVE_PATH,
ProgressBar PB_Show = null, RichTextBox TB_Progress = null)
{
PB_Show = PB_Show ?? new ProgressBar();
TB_Progress = TB_Progress ?? new RichTextBox();
// Sanity check the input files.
if (!(File.Exists(EXEFS_PATH) && File.Exists(ROMFS_PATH) && File.Exists(EXHEADER_PATH))) return false;
var NCCH = setNCCH(EXEFS_PATH, ROMFS_PATH, EXHEADER_PATH, SERIAL_TEXT, LOGO_NAME, PB_Show, TB_Progress);
var NCSD = setNCSD(NCCH, Card2, PB_Show, TB_Progress);
bool success = writeROM(NCSD, NCCH, SAVE_PATH, PB_Show, TB_Progress);
return success;
}
// Sub methods that drive the operation
internal static NCCH setNCCH(string EXEFS_PATH, string ROMFS_PATH, string EXHEADER_PATH, string TB_Serial, string LOGO_NAME,
ProgressBar PB_Show = null, RichTextBox TB_Progress = null)
{
PB_Show = PB_Show ?? new ProgressBar();
TB_Progress = TB_Progress ?? new RichTextBox();
SHA256Managed sha = new SHA256Managed();
NCCH Content = new NCCH();
updateTB(TB_Progress, "Adding Exheader...");
Content.exheader = new Exheader(EXHEADER_PATH);
Content.plainregion = new byte[0]; //No plain region by default.
if (Content.exheader.isPokemon())
{
updateTB(TB_Progress, "Detected Pokemon Game. Adding Plain Region...");
if (Content.exheader.isXY())
{
Content.plainregion = (byte[])Resources.ResourceManager.GetObject("XY");
}
else if (Content.exheader.isORAS())
{
Content.plainregion = (byte[])Resources.ResourceManager.GetObject("ORAS");
}
}
updateTB(TB_Progress, "Adding ExeFS...");
Content.exefs = new ExeFS(EXEFS_PATH);
updateTB(TB_Progress, "Adding RomFS...");
Content.romfs = new RomFS(ROMFS_PATH);
updateTB(TB_Progress, "Adding Logo...");
Content.logo = (byte[])Resources.ResourceManager.GetObject(LOGO_NAME);
updateTB(TB_Progress, "Building NCCH Header...");
ulong Len = 0x200; //NCCH Signature + NCCH Header
Content.header = new NCCH.Header { Signature = new byte[0x100], Magic = 0x4843434E };
Content.header.TitleId = Content.header.ProgramId = Content.exheader.TitleID;
Content.header.MakerCode = 0x3130; //01
Content.header.FormatVersion = 0x2; //Default
Content.header.LogoHash = sha.ComputeHash(Content.logo);
Content.header.ProductCode = Encoding.ASCII.GetBytes(TB_Serial);
Array.Resize(ref Content.header.ProductCode, 0x10);
Content.header.ExheaderHash = Content.exheader.GetSuperBlockHash();
Content.header.ExheaderSize = (uint)(Content.exheader.Data.Length);
Len += Content.header.ExheaderSize + (uint)Content.exheader.AccessDescriptor.Length;
Content.header.Flags = new byte[0x8];
//FLAGS
Content.header.Flags[3] = 0; // Crypto: 0 = <7.x, 1=7.x;
Content.header.Flags[4] = 1; // Content Platform: 1 = CTR;
Content.header.Flags[5] = 0x3; // Content Type Bitflags: 1=Data, 2=Executable, 4=SysUpdate, 8=Manual, 0x10=Trial;
Content.header.Flags[6] = 0; // MEDIA_UNIT_SIZE = 0x200*Math.Pow(2, Content.header.Flags[6]);
Content.header.Flags[7] = 1; // FixedCrypto = 1, NoMountRomfs = 2; NoCrypto=4;
Content.header.LogoOffset = (uint)(Len / MEDIA_UNIT_SIZE);
Content.header.LogoSize = (uint)(Content.logo.Length / MEDIA_UNIT_SIZE);
Len += (uint)Content.logo.Length;
Content.header.PlainRegionOffset = (uint)((Content.plainregion.Length > 0) ? Len / MEDIA_UNIT_SIZE : 0);
Content.header.PlainRegionSize = (uint)Content.plainregion.Length / MEDIA_UNIT_SIZE;
Len += (uint)Content.plainregion.Length;
Content.header.ExefsOffset = (uint)(Len / MEDIA_UNIT_SIZE);
Content.header.ExefsSize = (uint)(Content.exefs.Data.Length / MEDIA_UNIT_SIZE);
Content.header.ExefsSuperBlockSize = 0x200 / MEDIA_UNIT_SIZE; //Static 0x200 for exefs superblock
Len += (uint)Content.exefs.Data.Length;
Len = (uint)Align(Len, 0x1000); //Romfs Start is aligned to 0x1000
Content.header.RomfsOffset = (uint)(Len / MEDIA_UNIT_SIZE);
Content.header.RomfsSize = (uint)((new FileInfo(Content.romfs.FileName)).Length / MEDIA_UNIT_SIZE);
Content.header.RomfsSuperBlockSize = Content.romfs.SuperBlockLen / MEDIA_UNIT_SIZE;
Len += Content.header.RomfsSize * MEDIA_UNIT_SIZE;
Content.header.ExefsHash = Content.exefs.SuperBlockHash;
Content.header.RomfsHash = Content.romfs.SuperBlockHash;
Content.header.Size = (uint)(Len / MEDIA_UNIT_SIZE);
//Build the Header byte[].
Content.header.BuildHeader();
return Content;
}
internal static NCSD setNCSD(NCCH Content, bool Card2,
ProgressBar PB_Show = null, RichTextBox TB_Progress = null)
{
PB_Show = PB_Show ?? new ProgressBar();
TB_Progress = TB_Progress ?? new RichTextBox();
NCSD Rom = new NCSD { NCCH_Array = new List<NCCH>() };
Rom.NCCH_Array.Add(Content);
updateTB(TB_Progress, "Building NCSD Header...");
Rom.Card2 = Card2;
Rom.header = new NCSD.Header { Signature = new byte[0x100], Magic = 0x4453434E };
ulong Length = 0x80 * 0x100000; // 128 MB
while (Length <= Content.header.Size * MEDIA_UNIT_SIZE + 0x400000) //Extra 4 MB for potential save data
{
Length *= 2;
}
Rom.header.MediaSize = (uint)(Length / MEDIA_UNIT_SIZE);
Rom.header.TitleId = Content.exheader.TitleID;
Rom.header.OffsetSizeTable = new NCSD.NCCH_Meta[8];
ulong OSOfs = 0x4000;
for (int i = 0; i < Rom.header.OffsetSizeTable.Length; i++)
{
NCSD.NCCH_Meta ncchm = new NCSD.NCCH_Meta();
if (i < Rom.NCCH_Array.Count)
{
ncchm.Offset = (uint)(OSOfs / MEDIA_UNIT_SIZE);
ncchm.Size = Rom.NCCH_Array[i].header.Size;
}
else
{
ncchm.Offset = 0;
ncchm.Size = 0;
}
Rom.header.OffsetSizeTable[i] = ncchm;
OSOfs += ncchm.Size * MEDIA_UNIT_SIZE;
}
Rom.header.flags = new byte[0x8];
Rom.header.flags[0] = 0; // 0-255 seconds of waiting for save writing.
Rom.header.flags[3] = (byte)(Rom.Card2 ? 2 : 1); // Media Card Device: 1 = NOR Flash, 2 = None, 3 = BT
Rom.header.flags[4] = 1; // Media Platform Index: 1 = CTR
Rom.header.flags[5] = (byte)(Rom.Card2 ? 2 : 1); // Media Type Index: 0 = Inner Device, 1 = Card1, 2 = Card2, 3 = Extended Device
Rom.header.flags[6] = 0; // Media Unit Size. Same as NCCH.
Rom.header.flags[7] = 0; // Old Media Card Device.
Rom.header.NCCHIdTable = new ulong[8];
for (int i = 0; i < Rom.NCCH_Array.Count; i++)
{
Rom.header.NCCHIdTable[i] = Rom.NCCH_Array[i].header.TitleId;
}
Rom.cardinfoheader = new NCSD.CardInfoHeader
{
WritableAddress = (uint)(Rom.GetWritableAddress()),
CardInfoBitmask = 0,
CIN = new NCSD.CardInfoHeader.CardInfoNotes
{
Reserved0 = new byte[0xF8],
MediaSizeUsed = OSOfs,
Reserved1 = 0,
Unknown = 0,
Reserved2 = new byte[0xC],
CVerTitleId = 0,
CVerTitleVersion = 0,
Reserved3 = new byte[0xCD6]
},
NCCH0TitleId = Rom.NCCH_Array[0].header.TitleId,
Reserved0 = 0,
InitialData = new byte[0x30]
};
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] randbuffer = new byte[0x2C];
rng.GetBytes(randbuffer);
Array.Copy(randbuffer, Rom.cardinfoheader.InitialData, randbuffer.Length);
Rom.cardinfoheader.Reserved1 = new byte[0xC0];
Rom.cardinfoheader.NCCH0Header = new byte[0x100];
Array.Copy(Rom.NCCH_Array[0].header.Data, 0x100, Rom.cardinfoheader.NCCH0Header, 0, 0x100);
Rom.BuildHeader();
//NCSD is Initialized
return Rom;
}
internal static bool writeROM(NCSD Rom, NCCH Content, string SAVE_PATH,
ProgressBar PB_Show = null, RichTextBox TB_Progress = null)
{
PB_Show = PB_Show ?? new ProgressBar();
TB_Progress = TB_Progress ?? new RichTextBox();
using (FileStream OutFileStream = new FileStream(SAVE_PATH, FileMode.Create))
{
updateTB(TB_Progress, "Writing NCSD Header...");
OutFileStream.Write(Rom.Data, 0, Rom.Data.Length);
updateTB(TB_Progress, "Writing NCCH...");
OutFileStream.Write(Rom.NCCH_Array[0].header.Data, 0, Rom.NCCH_Array[0].header.Data.Length); //Write NCCH header
//AES time.
byte[] key = new byte[0x10]; //Fixed-Crypto key is all zero.
for (int i = 0; i < 3; i++)
{
AesCtr aesctr = new AesCtr(key, Rom.NCCH_Array[0].header.ProgramId, ((ulong)(i + 1)) << 56); //CTR is ProgramID, section id<<88
switch (i)
{
case 0: //Exheader + AccessDesc
updateTB(TB_Progress, "Writing Exheader...");
byte[] inEncExheader = new byte[Rom.NCCH_Array[0].exheader.Data.Length + Rom.NCCH_Array[0].exheader.AccessDescriptor.Length];
byte[] outEncExheader = new byte[Rom.NCCH_Array[0].exheader.Data.Length + Rom.NCCH_Array[0].exheader.AccessDescriptor.Length];
Array.Copy(Rom.NCCH_Array[0].exheader.Data, inEncExheader, Rom.NCCH_Array[0].exheader.Data.Length);
Array.Copy(Rom.NCCH_Array[0].exheader.AccessDescriptor, 0, inEncExheader, Rom.NCCH_Array[0].exheader.Data.Length, Rom.NCCH_Array[0].exheader.AccessDescriptor.Length);
aesctr.TransformBlock(inEncExheader, 0, inEncExheader.Length, outEncExheader, 0);
OutFileStream.Write(outEncExheader, 0, outEncExheader.Length); // Write Exheader
break;
case 1: //Exefs
updateTB(TB_Progress, "Writing Exefs...");
OutFileStream.Seek(0x4000 + Rom.NCCH_Array[0].header.ExefsOffset * MEDIA_UNIT_SIZE, SeekOrigin.Begin);
byte[] OutExefs = new byte[Rom.NCCH_Array[0].exefs.Data.Length];
aesctr.TransformBlock(Rom.NCCH_Array[0].exefs.Data, 0, Rom.NCCH_Array[0].exefs.Data.Length, OutExefs, 0);
OutFileStream.Write(OutExefs, 0, OutExefs.Length);
break;
case 2: //Romfs
updateTB(TB_Progress, "Writing Romfs...");
OutFileStream.Seek(0x4000 + Rom.NCCH_Array[0].header.RomfsOffset * MEDIA_UNIT_SIZE, SeekOrigin.Begin);
using (FileStream InFileStream = new FileStream(Rom.NCCH_Array[0].romfs.FileName, FileMode.Open, FileAccess.Read))
{
uint BUFFER_SIZE = 0;
ulong RomfsLen = Rom.NCCH_Array[0].header.RomfsSize * MEDIA_UNIT_SIZE;
PB_Show.Invoke((Action)(() =>
{
PB_Show.Minimum = 0;
PB_Show.Maximum = (int)(RomfsLen / 0x400000);
PB_Show.Value = 0;
PB_Show.Step = 1;
}));
for (ulong j = 0; j < (RomfsLen); j += BUFFER_SIZE)
{
BUFFER_SIZE = (RomfsLen - j) > 0x400000 ? 0x400000 : (uint)(RomfsLen - j);
byte[] buf = new byte[BUFFER_SIZE];
byte[] outbuf = new byte[BUFFER_SIZE];
InFileStream.Read(buf, 0, (int)BUFFER_SIZE);
aesctr.TransformBlock(buf, 0, (int)BUFFER_SIZE, outbuf, 0);
OutFileStream.Write(outbuf, 0, (int)BUFFER_SIZE);
PB_Show.Invoke((Action)(PB_Show.PerformStep));
}
}
break;
}
}
updateTB(TB_Progress, "Writing Logo...");
OutFileStream.Seek(0x4000 + Rom.NCCH_Array[0].header.LogoOffset * MEDIA_UNIT_SIZE, SeekOrigin.Begin);
OutFileStream.Write(Rom.NCCH_Array[0].logo, 0, Rom.NCCH_Array[0].logo.Length);
if (Rom.NCCH_Array[0].plainregion.Length > 0)
{
updateTB(TB_Progress, "Writing Plain Region...");
OutFileStream.Seek(0x4000 + Rom.NCCH_Array[0].header.PlainRegionOffset * MEDIA_UNIT_SIZE, SeekOrigin.Begin);
OutFileStream.Write(Rom.NCCH_Array[0].plainregion, 0, Rom.NCCH_Array[0].plainregion.Length);
}
//NCSD Padding
OutFileStream.Seek(Rom.header.OffsetSizeTable[Rom.NCCH_Array.Count - 1].Offset * MEDIA_UNIT_SIZE + Rom.header.OffsetSizeTable[Rom.NCCH_Array.Count - 1].Size * MEDIA_UNIT_SIZE, SeekOrigin.Begin);
ulong TotalLen = Rom.header.MediaSize * MEDIA_UNIT_SIZE;
byte[] Buffer = Enumerable.Repeat((byte)0xFF, 0x400000).ToArray();
updateTB(TB_Progress, "Writing NCSD Padding...");
while ((ulong)OutFileStream.Position < TotalLen)
{
int BUFFER_LEN = ((TotalLen - (ulong)OutFileStream.Position) < 0x400000) ? (int)(TotalLen - (ulong)OutFileStream.Position) : 0x400000;
OutFileStream.Write(Buffer, 0, BUFFER_LEN);
}
}
//Delete Temporary Romfs File
File.Delete(Content.romfs.FileName);
updateTB(TB_Progress, "Done!");
return true;
}
// Utility
internal static bool isValid(string exeFS, string romFS, string exeheader, string path, string serial, bool Card2)
{
bool isSerialValid = true;
if (serial.Length == 10)
{
string[] subs = serial.Split('-');
if (subs.Length != 3)
isSerialValid = false;
else
{
if (subs[0].Length != 3 || subs[1].Length != 1 || subs[2].Length != 4)
isSerialValid = false;
else if (subs[0] != "CTR" && subs[0] != "KTR")
isSerialValid = false;
else if (subs[1] != "P" && subs[1] != "N" && subs[2] != "U")
isSerialValid = false;
else
{
foreach (char c in subs[2].Where(c => !Char.IsLetterOrDigit(c)))
isSerialValid = false;
}
}
}
else
{
isSerialValid = false;
}
if (exeFS == string.Empty
|| romFS == string.Empty
|| exeheader == string.Empty
|| path == string.Empty
|| !isSerialValid)
return false;
Exheader exh = new Exheader(exeheader);
return !exh.isPokemon() || Card2;
}
internal static void updateTB(RichTextBox RTB, string progress)
{
try
{
if (RTB.InvokeRequired)
RTB.Invoke((MethodInvoker)delegate
{
RTB.AppendText(Environment.NewLine + progress);
RTB.SelectionStart = RTB.Text.Length;
RTB.ScrollToCaret();
});
else
{
RTB.SelectionStart = RTB.Text.Length;
RTB.ScrollToCaret();
RTB.AppendText(progress + Environment.NewLine);
}
}
catch { }
}
internal static ulong Align(ulong input, ulong alignsize)
{
ulong output = input;
if (output % alignsize != 0)
{
output += (alignsize - (output % alignsize));
}
return output;
}
}
}

View File

@@ -4,9 +4,9 @@
using System.Text;
using System.Security.Cryptography;
namespace pk3DS
namespace CTR
{
public class ExeFS //A lot of this is lifted directly from pk3DS.
public class ExeFS
{
public byte[] Data;
public byte[] SuperBlockHash;

94
pk3DS/3DS/Exheader.cs Normal file
View File

@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using pk3DS.Properties;
namespace CTR
{
public class Exheader
{
public byte[] Data;
public byte[] AccessDescriptor;
public ulong TitleID;
public Exheader(string EXHEADER_PATH)
{
Data = File.ReadAllBytes(EXHEADER_PATH);
AccessDescriptor = Data.Skip(0x400).Take(0x400).ToArray();
Data = Data.Take(0x400).ToArray();
TitleID = BitConverter.ToUInt64(Data, 0x200);
}
public byte[] GetSuperBlockHash()
{
SHA256Managed sha = new SHA256Managed();
return sha.ComputeHash(Data, 0, 0x400);
}
public string GetSerial()
{
const string output = "CTR-P-";
var RecognizedGames = new Dictionary<ulong, string[]>();
string[] lines = Resources.ResourceManager.GetString("_3dsgames").Split('\n').ToArray();
foreach (string l in lines)
{
string[] vars = l.Split('\t').ToArray();
ulong titleid = Convert.ToUInt64(vars[0], 16);
if (RecognizedGames.ContainsKey(titleid))
{
char lc = RecognizedGames[titleid].ToArray()[0].ToCharArray()[3];
char lc2 = vars[1].ToCharArray()[3];
if (lc2 == 'A' || lc2 == 'E' || (lc2 == 'P' && lc == 'J')) //Prefer games in order US, PAL, JP
{
RecognizedGames[titleid] = vars.Skip(1).Take(2).ToArray();
}
}
else
{
RecognizedGames.Add(titleid, vars.Skip(1).Take(2).ToArray());
}
}
return output + RecognizedGames[TitleID][0];
}
public bool isPokemon()
{
return (isORAS() || isXY());
}
public bool isORAS()
{
return (((TitleID & 0xFFFFFFFF) >> 8) == 0x11C5) || (((TitleID & 0xFFFFFFFF) >> 8) == 0x11C4);
}
public bool isXY()
{
return (((TitleID & 0xFFFFFFFF) >> 8) == 0x55D) || (((TitleID & 0xFFFFFFFF) >> 8) == 0x55E);
}
public string GetPokemonSerial()
{
if (!isPokemon())
return "CTR-P-XXXX";
string name = string.Empty;
switch ((TitleID & 0xFFFFFFFF) >> 8)
{
case 0x11C5: //Alpha Sapphire
name = "ECLA";
break;
case 0x11C4: //Omega Ruby
name = "ECRA";
break;
case 0x55D: //X
name = "EKJA";
break;
case 0x55E: //Y
name = "EK2A";
break;
default:
name = "XXXX";
break;
}
return "CTR-P-" + name;
}
}
}

78
pk3DS/3DS/NCCH.cs Normal file
View File

@@ -0,0 +1,78 @@
using System;
namespace CTR
{
public class NCCH
{
public Header header;
public ExeFS exefs;
public RomFS romfs;
public Exheader exheader;
public byte[] logo;
public byte[] plainregion;
public const uint MEDIA_UNIT_SIZE = 0x200;
public class Header
{
public byte[] Signature; //Size: 0x100
public uint Magic;
public uint Size;
public ulong TitleId;
public ushort MakerCode;
public ushort FormatVersion;
//public uint padding0;
public ulong ProgramId;
//public byte[0x10] padding1;
public byte[] LogoHash; // Size: 0x20
public byte[] ProductCode; // Size: 0x10
public byte[] ExheaderHash; // Size: 0x20
public uint ExheaderSize;
//public uint padding2;
public byte[] Flags; // Size: 8
public uint PlainRegionOffset;
public uint PlainRegionSize;
public uint LogoOffset;
public uint LogoSize;
public uint ExefsOffset;
public uint ExefsSize;
public uint ExefsSuperBlockSize;
//public uint padding4;
public uint RomfsOffset;
public uint RomfsSize;
public uint RomfsSuperBlockSize;
//public uint padding5;
public byte[] ExefsHash; // Size: 0x20
public byte[] RomfsHash; // Size: 0x20
public byte[] Data;
public void BuildHeader()
{
Data = new byte[0x200];
Array.Copy(Signature, Data, 0x100);
Array.Copy(BitConverter.GetBytes(Magic), 0, Data, 0x100, 4);
Array.Copy(BitConverter.GetBytes(Size), 0, Data, 0x104, 4);
Array.Copy(BitConverter.GetBytes(TitleId), 0, Data, 0x108, 8);
Array.Copy(BitConverter.GetBytes(MakerCode), 0, Data, 0x110, 2);
Array.Copy(BitConverter.GetBytes(FormatVersion), 0, Data, 0x112, 2);
//4 Byte Padding
Array.Copy(BitConverter.GetBytes(ProgramId), 0, Data, 0x118, 8);
//0x10 Byte Padding
Array.Copy(LogoHash, 0, Data, 0x130, 0x20);
Array.Copy(ProductCode, 0, Data, 0x150, 0x10);
Array.Copy(ExheaderHash, 0, Data, 0x160, 0x20);
Array.Copy(BitConverter.GetBytes(ExheaderSize), 0, Data, 0x180, 4);
//4 Byte Padding
Array.Copy(Flags, 0, Data, 0x188, 0x8);
uint ofs = 0x190;
foreach (uint val in new uint[] { PlainRegionOffset, PlainRegionSize, LogoOffset, LogoSize, ExefsOffset, ExefsSize, ExefsSuperBlockSize, 0, RomfsOffset, RomfsSize, RomfsSuperBlockSize, 0 })
{
Array.Copy(BitConverter.GetBytes(val), 0, Data, ofs, 4);
ofs += 4;
}
Array.Copy(ExefsHash, 0, Data, 0x1C0, 0x20);
Array.Copy(RomfsHash, 0, Data, 0x1E0, 0x20);
}
}
}
}

110
pk3DS/3DS/NCSD.cs Normal file
View File

@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace CTR
{
public class NCSD
{
public Header header;
public CardInfoHeader cardinfoheader;
public List<NCCH> NCCH_Array;
public bool Card2;
public byte[] Data;
public class Header
{
public byte[] Signature; //Size 0x100;
public uint Magic;
public uint MediaSize;
public ulong TitleId;
//public byte[] padding; //Size: 0x10
public NCCH_Meta[] OffsetSizeTable; //Size: 8
//public byte[] padding; //Size: 0x28
public byte[] flags; //Size: 0x8
public ulong[] NCCHIdTable; //Size: 0x8;
//public byte[] Padding2; //Size: 0x30;
}
public class CardInfoHeader
{
public uint WritableAddress;
public uint CardInfoBitmask;
public CardInfoNotes CIN;
public ulong NCCH0TitleId;
public ulong Reserved0;
public byte[] InitialData; // Size: 0x30
public byte[] Reserved1; // Size: 0xC0
public byte[] NCCH0Header; // Size: 0x100
public class CardInfoNotes
{
public byte[] Reserved0; // Size: 0xF8;
public ulong MediaSizeUsed;
public ulong Reserved1;
public uint Unknown;
public byte[] Reserved2; //Size: 0xC;
public ulong CVerTitleId;
public ushort CVerTitleVersion;
public byte[] Reserved3; //Size: 0xCD6;
}
}
public class NCCH_Meta
{
public uint Offset;
public uint Size;
}
public ulong GetWritableAddress()
{
const ulong MEDIA_UNIT_SIZE = 0x200;
return Card2
? (Align(header.OffsetSizeTable[NCCH_Array.Count - 1].Offset * NCCH.MEDIA_UNIT_SIZE
+ header.OffsetSizeTable[NCCH_Array.Count - 1].Size * NCCH.MEDIA_UNIT_SIZE + 0x1000, 0x10000) / MEDIA_UNIT_SIZE)
: 0x00000000FFFFFFFF;
}
public void BuildHeader()
{
Data = new byte[0x4000];
Array.Copy(header.Signature, Data, 0x100);
Array.Copy(BitConverter.GetBytes(header.Magic), 0, Data, 0x100, 4);
Array.Copy(BitConverter.GetBytes(header.MediaSize), 0, Data, 0x104, 4);
Array.Copy(BitConverter.GetBytes(header.TitleId), 0, Data, 0x108, 8);
for (int i = 0; i < header.OffsetSizeTable.Length; i++)
{
Array.Copy(BitConverter.GetBytes(header.OffsetSizeTable[i].Offset), 0, Data, 0x120 + 8 * i, 4);
Array.Copy(BitConverter.GetBytes(header.OffsetSizeTable[i].Size), 0, Data, 0x124 + 8 * i, 4);
}
Array.Copy(header.flags, 0, Data, 0x188, header.flags.Length);
for (int i = 0; i < header.NCCHIdTable.Length; i++)
{
Array.Copy(BitConverter.GetBytes(header.NCCHIdTable[i]), 0, Data, 0x190 + 8 * i, 8);
}
//CardInfoHeader
Array.Copy(BitConverter.GetBytes(cardinfoheader.WritableAddress), 0, Data, 0x200, 4);
Array.Copy(BitConverter.GetBytes(cardinfoheader.CardInfoBitmask), 0, Data, 0x204, 4);
Array.Copy(cardinfoheader.CIN.Reserved0, 0, Data, 0x208, cardinfoheader.CIN.Reserved0.Length);
Array.Copy(BitConverter.GetBytes(cardinfoheader.CIN.MediaSizeUsed), 0, Data, 0x300, 8);
Array.Copy(BitConverter.GetBytes(cardinfoheader.CIN.Reserved1), 0, Data, 0x308, 8);
Array.Copy(BitConverter.GetBytes(cardinfoheader.CIN.Unknown), 0, Data, 0x310, 4);
Array.Copy(cardinfoheader.CIN.Reserved2, 0, Data, 0x314, cardinfoheader.CIN.Reserved2.Length);
Array.Copy(BitConverter.GetBytes(cardinfoheader.CIN.CVerTitleId), 0, Data, 0x320, 8);
Array.Copy(BitConverter.GetBytes(cardinfoheader.CIN.CVerTitleVersion), 0, Data, 0x328, 2);
Array.Copy(cardinfoheader.CIN.Reserved3, 0, Data, 0x32A, cardinfoheader.CIN.Reserved3.Length);
Array.Copy(BitConverter.GetBytes(cardinfoheader.NCCH0TitleId), 0, Data, 0x1000, 8);
Array.Copy(BitConverter.GetBytes(cardinfoheader.Reserved0), 0, Data, 0x1008, 8);
Array.Copy(cardinfoheader.InitialData, 0, Data, 0x1010, cardinfoheader.InitialData.Length);
Array.Copy(cardinfoheader.Reserved1, 0, Data, 0x1040, cardinfoheader.Reserved1.Length);
Array.Copy(cardinfoheader.NCCH0Header, 0, Data, 0x1100, cardinfoheader.NCCH0Header.Length);
Array.Copy(Enumerable.Repeat((byte)0xFF, 0x2E00).ToArray(), 0, Data, 0x1200, 0x2E00);
}
internal static ulong Align(ulong input, ulong alignsize)
{
ulong output = input;
if (output % alignsize != 0)
{
output += (alignsize - (output % alignsize));
}
return output;
}
}
}

View File

@@ -5,11 +5,11 @@
using System.Text;
using System.Windows.Forms;
namespace pk3DS
namespace CTR
{
// Info acquisition
public class RomFS
{
// Get Info
public string FileName;
public bool isTempFile;
public byte[] SuperBlockHash;
@@ -35,10 +35,8 @@ public RomFS(string fn)
}
}
}
}
// File operation
public class RomFSTool
{
// Build
internal const int PADDING_ALIGN = 16;
internal static string ROOT_DIR;
internal const string TempFile = "tempRomFS.bin";
@@ -77,7 +75,7 @@ internal static void BuildRomFS(string outfile, string infile, RichTextBox TB_Pr
updateTB(TB_Progress, "Creating Layout...");
for (int i = 0; i < FNT.NumFiles; i++)
{
In[i] = new LayoutManager.Input {FilePath = FNT.NameEntryTable[i].FullName, AlignmentSize = 0x10};
In[i] = new LayoutManager.Input { FilePath = FNT.NameEntryTable[i].FullName, AlignmentSize = 0x10 };
}
LayoutManager.Output[] Out = LayoutManager.Create(In);
for (int i = 0; i < Out.Length; i++)
@@ -110,10 +108,10 @@ internal static ulong Align(ulong input, ulong alignsize)
internal static void MakeRomFSData(RomfsFile[] RomFiles, MemoryStream metadata, RichTextBox TB_Progress = null, ProgressBar PB_Show = null)
{
updateTB(TB_Progress, "Computing IVFC Header Data...");
IVFCInfo ivfc = new IVFCInfo {Levels = new IVFCLevel[3]};
IVFCInfo ivfc = new IVFCInfo { Levels = new IVFCLevel[3] };
for (int i = 0; i < ivfc.Levels.Length; i++)
{
ivfc.Levels[i] = new IVFCLevel {BlockSize = 0x1000};
ivfc.Levels[i] = new IVFCLevel { BlockSize = 0x1000 };
}
ivfc.Levels[2].DataLength = RomfsFile.GetDataBlockLength(RomFiles, (ulong)metadata.Length);
ivfc.Levels[1].DataLength = (Align(ivfc.Levels[2].DataLength, ivfc.Levels[2].BlockSize) / ivfc.Levels[2].BlockSize) * 0x20; //0x20 per SHA256 hash
@@ -154,7 +152,7 @@ internal static void MakeRomFSData(RomfsFile[] RomFiles, MemoryStream metadata,
if (PB_Show.InvokeRequired)
PB_Show.Invoke((MethodInvoker)delegate { PB_Show.Minimum = 0; PB_Show.Step = 1; PB_Show.Value = 0; PB_Show.Maximum = RomFiles.Length; });
else { PB_Show.Minimum = 0; PB_Show.Step = 1; PB_Show.Value = 0; PB_Show.Maximum = RomFiles.Length; }
foreach (RomfsFile t in RomFiles)
{
OutFileStream.Seek(baseOfs + (long)t.Offset, SeekOrigin.Begin);
@@ -181,9 +179,9 @@ internal static void MakeRomFSData(RomfsFile[] RomFiles, MemoryStream metadata,
byte[] buffer = new byte[(int)ivfc.Levels[i].BlockSize];
if (PB_Show.InvokeRequired)
PB_Show.Invoke((MethodInvoker)delegate { PB_Show.Minimum = 0; PB_Show.Step = 1; PB_Show.Value = 0; PB_Show.Maximum = (int)(ivfc.Levels[i].DataLength / ivfc.Levels[i].BlockSize); });
PB_Show.Invoke((MethodInvoker)delegate { PB_Show.Minimum = 0; PB_Show.Step = 1; PB_Show.Value = 0; PB_Show.Maximum = (int)(ivfc.Levels[i].DataLength / ivfc.Levels[i].BlockSize); });
else { PB_Show.Minimum = 0; PB_Show.Step = 1; PB_Show.Value = 0; PB_Show.Maximum = (int)(ivfc.Levels[i].DataLength / ivfc.Levels[i].BlockSize); }
for (long ofs = 0; ofs < (long)ivfc.Levels[i].DataLength; ofs += ivfc.Levels[i].BlockSize)
{
OutFileStream.Seek(hOfs, SeekOrigin.Begin);
@@ -227,7 +225,7 @@ internal static void MakeRomFSData(RomfsFile[] RomFiles, MemoryStream metadata,
}
updateTB(TB_Progress, "RomFS Super Block Hash: " + ByteArrayToString(SuperBlockHash));
updateTB(TB_Progress, "Saving to destination...");
if (File.Exists(OutFile)) File.Delete(OutFile);
File.Move(TempFile, OutFile);
}
@@ -264,7 +262,7 @@ internal static void WriteBinary(string tempFile, string outFile, RichTextBox TB
}
}
File.Delete(TempFile);
Util.Alert("Wrote RomFS to path:", outFile);
updateTB(TB_Progress, "Wrote RomFS to path:" + Environment.NewLine + outFile);
}
internal static string ByteArrayToString(IEnumerable<byte> input)
{
@@ -317,7 +315,7 @@ internal static void CalcRomfsSize(Romfs_MetaData MetaData)
MetaData.M_DirHashTableEntry = GetHashTableEntryCount(MetaData.DirNum);
MetaData.M_FileHashTableEntry = GetHashTableEntryCount(MetaData.FileNum);
uint MetaDataSize = (uint)Align((0x28 + MetaData.M_DirHashTableEntry * 4 + MetaData.M_DirTableLen + MetaData.M_FileHashTableEntry * 4 + MetaData.M_FileTableLen), PADDING_ALIGN);
uint MetaDataSize = (uint)Align((0x28 + MetaData.M_DirHashTableEntry * 4 + MetaData.M_DirTableLen + MetaData.M_FileHashTableEntry * 4 + MetaData.M_FileTableLen), PADDING_ALIGN);
for (int i = 0; i < MetaData.M_DirHashTableEntry; i++)
MetaData.DirHashTable.Add(ROMFS_UNUSED_ENTRY);
@@ -484,14 +482,14 @@ internal static void AddDir(Romfs_MetaData MetaData, DirectoryInfo Dir, uint par
if (!DoSubs)
{
uint CurrentDir = MetaData.DirTableLen;
Romfs_DirEntry Entry = new Romfs_DirEntry {ParentOffset = parent};
Romfs_DirEntry Entry = new Romfs_DirEntry { ParentOffset = parent };
Entry.ChildOffset = Entry.HashKeyPointer = Entry.FileOffset = ROMFS_UNUSED_ENTRY;
Entry.SiblingOffset = sibling;
Entry.FullName = Dir.FullName;
Entry.Name = (Entry.FullName == ROOT_DIR) ? "" : Dir.Name;
Entry.Offset = CurrentDir;
MetaData.DirTable.DirectoryTable.Add(Entry);
MetaData.DirTableLen += (CurrentDir == 0) ? 0x18 : 0x18 + (uint) Align((ulong) Dir.Name.Length*2, 4);
MetaData.DirTableLen += (CurrentDir == 0) ? 0x18 : 0x18 + (uint)Align((ulong)Dir.Name.Length * 2, 4);
// int ParentIndex = GetRomfsDirEntry(MetaData, Dir.FullName);
// uint poff = MetaData.DirTable.DirectoryTable[ParentIndex].Offset;
}
@@ -611,12 +609,11 @@ internal static void WriteMetaDataToStream(Romfs_MetaData MetaData, MemoryStream
//Padding
while (stream.Position % PADDING_ALIGN != 0)
stream.Write(new byte[PADDING_ALIGN - (stream.Position % 0x10)], 0, (int)(PADDING_ALIGN - (stream.Position % 0x10)));
stream.Write(new byte[PADDING_ALIGN - (stream.Position % 0x10)], 0, (int)(PADDING_ALIGN - (stream.Position % 0x10)));
//All Done.
}
//GetRomfs[...]Entry Functions are all O(n)
internal static int GetRomfsDirEntry(Romfs_MetaData MetaData, string FullName)
{
for (int i = 0; i < MetaData.DirTable.DirectoryTable.Count; i++)

View File

@@ -20,7 +20,7 @@
using System.Media;
using System.Threading;
using System.Windows.Forms;
using blz;
using BLZ;
namespace pk3DS
{
@@ -271,7 +271,7 @@ private void rebuildRomFS(object sender, EventArgs e)
updateStatus(Environment.NewLine + "Building RomFS binary. Please wait until the program finishes.");
threads++;
RomFSTool.BuildRomFS(sfd.FileName, RomFSPath, RTB_Status, pBar1);
CTR.RomFS.BuildRomFS(sfd.FileName, RomFSPath, RTB_Status, pBar1);
threads--;
updateStatus("RomFS binary saved." + Environment.NewLine);
@@ -490,7 +490,7 @@ private void rebuildExeFS(object sender, EventArgs e)
threads++;
new BLZCoder(new[] { "-en", files[file] }, pBar1);
Util.Alert("Compressed!");
ExeFS.set(Directory.GetFiles(ExeFSPath), sfd.FileName);
CTR.ExeFS.set(Directory.GetFiles(ExeFSPath), sfd.FileName);
threads--;
}).Start();
}

View File

@@ -4540,6 +4540,26 @@ internal class Resources {
}
}
/// <summary>
/// Looks up a localized string similar to 0004000000030500 ASSJ スーパーストリートファイターIV 3D EDITION
///0004000000030B00 ADAJ nintendogs + cats 柴 &amp; Newフレンズ
///0004000000031000 ADBJ nintendogs + cats フレンチ・ブル &amp; Newフレンズ
///0004000000031500 ADCJ nintendogs + cats トイ・プードル &amp; Newフレンズ
///0004000000031E00 A66J 戦国無双 Chronicle
///0004000000032A00 ABBJ とびだす!パズルボブル 3D
///0004000000032900 AEEJ ウイニングイレブン 3DSoccer
///0004000000031F00 AKKJ レイトン教授と奇跡の仮面
///0004000000034500 ARBJ ラビッツ タイム・トラベル
///0004000000032800 ARRJ リッジレーサー3D
///0004000000034400 ASCJ Tom Clancy&apos;s スプリンターセル 3D
///0004000000033300 ASFJ ASPHALT [rest of string was truncated]&quot;;.
/// </summary>
internal static string _3dsgames {
get {
return ResourceManager.GetString("_3dsgames", resourceCulture);
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
@@ -10430,6 +10450,16 @@ internal class Resources {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] Distributed {
get {
object obj = ResourceManager.GetObject("Distributed", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
@@ -10450,6 +10480,26 @@ internal class Resources {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] iQue {
get {
object obj = ResourceManager.GetObject("iQue", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] iQueForSystem {
get {
object obj = ResourceManager.GetObject("iQueForSystem", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
@@ -14640,6 +14690,36 @@ internal class Resources {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] Licensed {
get {
object obj = ResourceManager.GetObject("Licensed", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] Nintendo {
get {
object obj = ResourceManager.GetObject("Nintendo", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] ORAS {
get {
object obj = ResourceManager.GetObject("ORAS", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
@@ -14649,5 +14729,15 @@ internal class Resources {
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] XY {
get {
object obj = ResourceManager.GetObject("XY", resourceCulture);
return ((byte[])(obj));
}
}
}
}

View File

@@ -4495,4 +4495,28 @@
<data name="helditem" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\img\item\helditem.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Distributed" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\romdata\bin\Logo\Distributed.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="iQue" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\romdata\bin\Logo\iQue.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="iQueForSystem" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\romdata\bin\Logo\iQueForSystem.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="Licensed" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\romdata\bin\Logo\Licensed.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="Nintendo" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\romdata\bin\Logo\Nintendo.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="ORAS" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\romdata\bin\PlainRegion\ORAS.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="XY" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\romdata\bin\PlainRegion\XY.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="_3dsgames" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\romdata\txt\3dsgames.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
</root>

File diff suppressed because it is too large Load Diff

View File

@@ -50,12 +50,17 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="3DS\AES.cs" />
<Compile Include="3DS\Exheader.cs" />
<Compile Include="3DS\NCCH.cs" />
<Compile Include="3DS\NCSD.cs" />
<Compile Include="ARCUtil.cs" />
<Compile Include="BLZ.cs" />
<Compile Include="ExeFS.cs" />
<Compile Include="3DS\BLZ.cs" />
<Compile Include="3DS\CTR.cs" />
<Compile Include="3DS\ExeFS.cs" />
<Compile Include="png2bclim.cs" />
<Compile Include="Randomizer.cs" />
<Compile Include="RomFS.cs" />
<Compile Include="3DS\RomFS.cs" />
<Compile Include="Subforms\TitleScreen.cs">
<SubType>Form</SubType>
</Compile>
@@ -278,6 +283,13 @@
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<None Include="Resources\romdata\bin\Logo\Distributed.bin" />
<None Include="Resources\romdata\bin\Logo\iQue.bin" />
<None Include="Resources\romdata\bin\Logo\iQueForSystem.bin" />
<None Include="Resources\romdata\bin\Logo\Licensed.bin" />
<None Include="Resources\romdata\bin\Logo\Nintendo.bin" />
<None Include="Resources\romdata\bin\PlainRegion\ORAS.bin" />
<None Include="Resources\romdata\bin\PlainRegion\XY.bin" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\img\Pokemon Sprites\2.png" />
@@ -4656,6 +4668,9 @@
<ItemGroup>
<None Include="Resources\img\item\helditem.png" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\romdata\txt\3dsgames.txt" />
</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.