diff --git a/pk3DS.Core/CTR/CTR.cs b/pk3DS.Core/CTR/CTR.cs index 225d76f..bcfa596 100644 --- a/pk3DS.Core/CTR/CTR.cs +++ b/pk3DS.Core/CTR/CTR.cs @@ -17,7 +17,7 @@ public class CTRUtil public 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) + bool trimmed = false, ProgressBar PB_Show = null, RichTextBox TB_Progress = null) { PB_Show = PB_Show ?? new ProgressBar(); TB_Progress = TB_Progress ?? new RichTextBox(); @@ -37,7 +37,7 @@ public class CTRUtil NCCH NCCH = setNCCH(EXEFS_PATH, ROMFS_PATH, EXHEADER_PATH, SERIAL_TEXT, LOGO_NAME, PB_Show, TB_Progress); NCSD NCSD = setNCSD(NCCH, Card2, PB_Show, TB_Progress); - bool success = writeROM(NCSD, SAVE_PATH, PB_Show, TB_Progress); + bool success = writeROM(NCSD, SAVE_PATH, trimmed, PB_Show, TB_Progress); return success; } @@ -62,6 +62,10 @@ public class CTRUtil NCCH.plainregion = (byte[])Resources.ResourceManager.GetObject("XY"); else if (NCCH.exheader.isORAS()) NCCH.plainregion = (byte[])Resources.ResourceManager.GetObject("ORAS"); + else if (NCCH.exheader.isSuMo()) + NCCH.plainregion = (byte[])Resources.ResourceManager.GetObject("SuMo"); + else if (NCCH.exheader.isUSUM()) + NCCH.plainregion = (byte[])Resources.ResourceManager.GetObject("USUM"); } updateTB(TB_Progress, "Adding ExeFS..."); NCCH.exefs = new ExeFS(EXEFS_PATH); @@ -196,7 +200,7 @@ public class CTRUtil return NCSD; } - internal static bool writeROM(NCSD NCSD, string SAVE_PATH, + internal static bool writeROM(NCSD NCSD, string SAVE_PATH, bool trimmed = false, ProgressBar PB_Show = null, RichTextBox TB_Progress = null) { PB_Show = PB_Show ?? new ProgressBar(); @@ -269,14 +273,17 @@ public class CTRUtil } //NCSD Padding - OutFileStream.Seek((NCSD.header.OffsetSizeTable[NCSD.NCCH_Array.Count - 1].Offset * MEDIA_UNIT_SIZE) + (NCSD.header.OffsetSizeTable[NCSD.NCCH_Array.Count - 1].Size * MEDIA_UNIT_SIZE), SeekOrigin.Begin); - ulong TotalLen = NCSD.header.MediaSize * MEDIA_UNIT_SIZE; - byte[] Buffer = Enumerable.Repeat((byte)0xFF, 0x400000).ToArray(); - updateTB(TB_Progress, "Writing NCSD Padding..."); - while ((ulong)OutFileStream.Position < TotalLen) + if (!trimmed) { - int BUFFER_LEN = TotalLen - (ulong)OutFileStream.Position < 0x400000 ? (int)(TotalLen - (ulong)OutFileStream.Position) : 0x400000; - OutFileStream.Write(Buffer, 0, BUFFER_LEN); + OutFileStream.Seek((NCSD.header.OffsetSizeTable[NCSD.NCCH_Array.Count - 1].Offset * MEDIA_UNIT_SIZE) + (NCSD.header.OffsetSizeTable[NCSD.NCCH_Array.Count - 1].Size * MEDIA_UNIT_SIZE), SeekOrigin.Begin); + ulong TotalLen = NCSD.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); + } } } diff --git a/pk3DS.Core/CTR/Exheader.cs b/pk3DS.Core/CTR/Exheader.cs index 7a93359..44311ef 100644 --- a/pk3DS.Core/CTR/Exheader.cs +++ b/pk3DS.Core/CTR/Exheader.cs @@ -56,7 +56,17 @@ public string GetSerial() public bool isPokemon() { - return isORAS() || isXY(); + return isORAS() || isXY() || isUSUM() || isSuMo(); + } + + public bool isUSUM() + { + return (TitleID & 0xFFFFFFFF) >> 8 == 0x1B50 || (TitleID & 0xFFFFFFFF) >> 8 == 0x1B51; + } + + public bool isSuMo() + { + return (TitleID & 0xFFFFFFFF) >> 8 == 0x1648 || (TitleID & 0xFFFFFFFF) >> 8 == 0x175E; } public bool isORAS() @@ -76,6 +86,18 @@ public string GetPokemonSerial() string name; switch ((TitleID & 0xFFFFFFFF) >> 8) { + case 0x1B51: + name = "A2BA"; // Ultra Moon + break; + case 0x1B50: + name = "A2AA"; // Ultra Sun + break; + case 0x175E: // Moon + name = "BNEA"; + break; + case 0x1648: // Sun + name = "BNDA"; + break; case 0x11C5: //Alpha Sapphire name = "ECLA"; break; diff --git a/pk3DS.Core/CTR/NCCH.cs b/pk3DS.Core/CTR/NCCH.cs index 4327d13..9e1e616 100644 --- a/pk3DS.Core/CTR/NCCH.cs +++ b/pk3DS.Core/CTR/NCCH.cs @@ -1,4 +1,6 @@ using System; +using System.IO; +using System.Windows.Forms; namespace pk3DS.Core.CTR { @@ -73,6 +75,180 @@ public void BuildHeader() Array.Copy(ExefsHash, 0, Data, 0x1C0, 0x20); Array.Copy(RomfsHash, 0, Data, 0x1E0, 0x20); } + + public void BuildHeaderFromBytes(byte[] data) + { + Data = data; + Signature = new byte[0x100]; + Array.Copy(data, Signature, 0x100); + Magic = BitConverter.ToUInt32(data, 0x100); + Size = BitConverter.ToUInt32(data, 0x104); + TitleId = BitConverter.ToUInt64(data, 0x108); + MakerCode = BitConverter.ToUInt16(data, 0x110); + FormatVersion = BitConverter.ToUInt16(data, 0x112); + //4 Byte Padding + ProgramId = BitConverter.ToUInt64(data, 0x118); + //0x10 Byte Padding + LogoHash = new byte[0x20]; + Array.Copy(data, 0x130, LogoHash, 0x0, 0x20); + ProductCode = new byte[0x10]; + Array.Copy(data, 0x150, ProductCode, 0x0, 0x10); + ExheaderHash = new byte[0x20]; + Array.Copy(data, 0x160, ExheaderHash, 0x0, 0x20); + ExheaderSize = BitConverter.ToUInt32(data, 0x180); + //4 Byte Padding + Flags = new byte[0x8]; + Array.Copy(data, 0x188, Flags, 0, 0x8); + PlainRegionOffset = BitConverter.ToUInt32(data, 0x190); + PlainRegionSize = BitConverter.ToUInt32(data, 0x194); + LogoOffset = BitConverter.ToUInt32(data, 0x198); + LogoSize = BitConverter.ToUInt32(data, 0x19C); + ExefsOffset = BitConverter.ToUInt32(data, 0x1A0); + ExefsSize = BitConverter.ToUInt32(data, 0x1A4); + ExefsSuperBlockSize = BitConverter.ToUInt32(data, 0x1A8); + //4 Byte Padding + RomfsOffset = BitConverter.ToUInt32(data, 0x1B0); + RomfsSize = BitConverter.ToUInt32(data, 0x1B4); + RomfsSuperBlockSize = BitConverter.ToUInt32(data, 0x1B8); + //4 Byte Padding + ExefsHash = new byte[0x20]; + Array.Copy(data, 0x1C0, ExefsHash, 0x0, 0x20); + RomfsHash = new byte[0x20]; + Array.Copy(data, 0x1E0, RomfsHash, 0x0, 0x20); + } + } + + public void ExtractNCCHFromFile(string NCCH_PATH, string outputDirectory, RichTextBox TB_Progress = null, ProgressBar PB_Show = null) + { + if (!Directory.Exists(outputDirectory)) + Directory.CreateDirectory(outputDirectory); + + byte[] headerBytes = new byte[0x200]; + using (FileStream fs = new FileStream(NCCH_PATH, FileMode.Open, FileAccess.Read)) + { + fs.Read(headerBytes, 0, headerBytes.Length); + header = new Header(); + header.BuildHeaderFromBytes(headerBytes); + + logo = new byte[header.LogoSize * MEDIA_UNIT_SIZE]; + fs.Seek(Convert.ToInt32(header.LogoOffset * MEDIA_UNIT_SIZE), SeekOrigin.Begin); + fs.Read(logo, 0, logo.Length); + + plainregion = new byte[header.PlainRegionSize * MEDIA_UNIT_SIZE]; + fs.Seek(Convert.ToInt32(header.PlainRegionOffset * MEDIA_UNIT_SIZE), SeekOrigin.Begin); + fs.Read(plainregion, 0, plainregion.Length); + } + + ExtractExheader(NCCH_PATH, outputDirectory, TB_Progress); + ExtractExeFS(NCCH_PATH, outputDirectory, TB_Progress, PB_Show); + ExtractRomFS(NCCH_PATH, outputDirectory, TB_Progress, PB_Show); + } + + private void ExtractExheader(string NCCH_PATH, string outputDirectory, RichTextBox TB_Progress = null) + { + string exheaderpath = Path.Combine(outputDirectory, "exheader.bin"); + updateTB(TB_Progress, "Extracting exheader.bin from CXI..."); + byte[] exheaderbytes = new byte[header.ExheaderSize * 2]; + + using (FileStream fs = new FileStream(NCCH_PATH, FileMode.Open, FileAccess.Read)) + { + fs.Seek(Convert.ToInt32(0x200), SeekOrigin.Begin); + fs.Read(exheaderbytes, 0, exheaderbytes.Length); + } + + File.WriteAllBytes(exheaderpath, exheaderbytes); + exheader = new Exheader(exheaderpath); + } + + private void ExtractExeFS(string NCCH_PATH, string outputDirectory, RichTextBox TB_Progress = null, ProgressBar PB_Show = null) + { + string exefsbinpath = Path.Combine(outputDirectory, "exefs.bin"); + string exefspath = Path.Combine(outputDirectory, "exefs"); + updateTB(TB_Progress, "Extracting exefs.bin from CXI..."); + byte[] exefsbytes = new byte[header.ExefsSize * MEDIA_UNIT_SIZE]; + + using (FileStream fs = new FileStream(NCCH_PATH, FileMode.Open, FileAccess.Read)) + { + fs.Seek(Convert.ToInt32(header.ExefsOffset * MEDIA_UNIT_SIZE), SeekOrigin.Begin); + fs.Read(exefsbytes, 0, exefsbytes.Length); + } + + File.WriteAllBytes(exefsbinpath, exefsbytes); + ExeFS.get(exefsbinpath, exefspath); + File.Delete(exefsbinpath); + } + + private void ExtractRomFS(string NCCH_PATH, string outputDirectory, RichTextBox TB_Progress = null, ProgressBar PB_Show = null) + { + updateTB(TB_Progress, "Extracting romfs.bin from CXI..."); + string romfsbinpath = Path.Combine(outputDirectory, "romfs.bin"); + string romfspath = Path.Combine(outputDirectory, "romfs"); + byte[] romfsBytes = new byte[MEDIA_UNIT_SIZE]; + + using (FileStream ncchstream = new FileStream(NCCH_PATH, FileMode.Open, FileAccess.Read), + romfsstream = new FileStream(romfsbinpath, FileMode.Append, FileAccess.Write)) + { + ncchstream.Seek(Convert.ToInt32(header.RomfsOffset * MEDIA_UNIT_SIZE), SeekOrigin.Begin); + if (PB_Show.InvokeRequired) + PB_Show.Invoke((MethodInvoker)delegate { PB_Show.Minimum = 0; PB_Show.Step = 1; PB_Show.Value = 0; PB_Show.Maximum = Convert.ToInt32(header.RomfsSize); }); + else { PB_Show.Minimum = 0; PB_Show.Step = 1; PB_Show.Value = 0; PB_Show.Maximum = Convert.ToInt32(header.RomfsSize); } + for (int i = 0; i < header.RomfsSize; i++) + { + ncchstream.Read(romfsBytes, 0, romfsBytes.Length); + romfsstream.Write(romfsBytes, 0, romfsBytes.Length); + if (PB_Show.InvokeRequired) + PB_Show.Invoke((MethodInvoker)PB_Show.PerformStep); + else { PB_Show.PerformStep(); } + } + } + + RomFS romfs = new RomFS(romfsbinpath); + romfs.ExtractRomFS(romfspath, TB_Progress, PB_Show); + File.Delete(romfsbinpath); + } + + private void WriteHeaderToFile(string outputDirectory, RichTextBox TB_Progress) + { + updateTB(TB_Progress, "Extracting ncchheader.bin from CXI..."); + string headerParth = Path.Combine(outputDirectory, "ncchheader.bin"); + using (FileStream headerStream = new FileStream(headerParth, FileMode.OpenOrCreate, FileAccess.Write)) + { + headerStream.Write(this.header.Data, 0, this.header.Data.Length); + } + } + + private void WritePlainRegionAndLogo(string outputDirectory, RichTextBox TB_Progress) + { + string plainRegionPath = Path.Combine(outputDirectory, "plain.bin"); + string logoPath = Path.Combine(outputDirectory, "logo.bcma.lz"); + updateTB(TB_Progress, "Extracting plain.bin and logo.bcma.lz from CXI..."); + using (FileStream plainStream = new FileStream(plainRegionPath, FileMode.OpenOrCreate, FileAccess.Write), + logoStream = new FileStream(logoPath, FileMode.OpenOrCreate, FileAccess.Write)) + { + plainStream.Write(this.plainregion, 0, this.plainregion.Length); + logoStream.Write(this.logo, 0, this.logo.Length); + } + } + + 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 { } } } } diff --git a/pk3DS.Core/CTR/NCSD.cs b/pk3DS.Core/CTR/NCSD.cs index 0b4d4fa..49884fd 100644 --- a/pk3DS.Core/CTR/NCSD.cs +++ b/pk3DS.Core/CTR/NCSD.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Windows.Forms; namespace pk3DS.Core.CTR { @@ -56,9 +58,10 @@ public class NCCH_Meta public uint Size; } + private const ulong MEDIA_UNIT_SIZE = 0x200; + 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 @@ -101,6 +104,77 @@ public void BuildHeader() Array.Copy(Enumerable.Repeat((byte)0xFF, 0x2E00).ToArray(), 0, Data, 0x1200, 0x2E00); } + public Header CreateHeaderFromBytes(byte[] data) + { + Header header = new Header(); + Data = data; + header.Signature = new byte[0x100]; + Array.Copy(data, header.Signature, 0x100); + header.Magic = BitConverter.ToUInt32(data, 0x100); + header.MediaSize = BitConverter.ToUInt32(data, 0x104); + header.TitleId = BitConverter.ToUInt64(data, 0x108); + header.OffsetSizeTable = new NCCH_Meta[8]; + for (int i = 0; i < 8; i++) + { + header.OffsetSizeTable[i] = new NCCH_Meta(); + header.OffsetSizeTable[i].Offset = BitConverter.ToUInt32(data, 0x120 + (8 * i)); + header.OffsetSizeTable[i].Size = BitConverter.ToUInt32(data, 0x124 + (8 * i)); + } + header.flags = new byte[8]; + Array.Copy(data, 0x188, header.flags, 0, 8); + header.NCCHIdTable = new ulong[8]; + for (int i = 0; i < 8; i++) + { + header.NCCHIdTable[i] = BitConverter.ToUInt64(data, 0x190 + (8 * i)); + } + return header; + } + + public void ExtractFilesFromNCSD(string NCSD_PATH, string outputDirectory, RichTextBox TB_Progress = null, ProgressBar PB_Show = null) + { + if (!Directory.Exists(outputDirectory)) + Directory.CreateDirectory(outputDirectory); + + updateTB(TB_Progress, "Extracting game data (CXI) from .3DS file..."); + byte[] headerBytes = new byte[0x200]; + using (FileStream fs = new FileStream(NCSD_PATH, FileMode.Open, FileAccess.Read)) + { + fs.Read(headerBytes, 0, headerBytes.Length); + } + + header = CreateHeaderFromBytes(headerBytes); + string ncchPath = ExtractCXIfromNCSD(NCSD_PATH, outputDirectory, header.OffsetSizeTable[0].Size, PB_Show); + updateTB(TB_Progress, "CXI extracted, extracting files from CXI..."); + NCCH ncch = new NCCH(); + ncch.ExtractNCCHFromFile(ncchPath, outputDirectory, TB_Progress, PB_Show); + File.Delete(ncchPath); + } + + private static string ExtractCXIfromNCSD(string NCSD_PATH, string outputDirectory, uint ncchSize, ProgressBar PB_Show = null) + { + byte[] buffer = new byte[MEDIA_UNIT_SIZE * 10]; + string outputFile = Path.Combine(outputDirectory, "game.cxi"); + if (PB_Show.InvokeRequired) + PB_Show.Invoke((MethodInvoker)delegate { PB_Show.Minimum = 0; PB_Show.Step = 1; PB_Show.Value = 0; PB_Show.Maximum = Convert.ToInt32(ncchSize); }); + else { PB_Show.Minimum = 0; PB_Show.Step = 1; PB_Show.Value = 0; PB_Show.Maximum = Convert.ToInt32(ncchSize); } + + using (FileStream inputFileStream = new FileStream(NCSD_PATH, FileMode.Open, FileAccess.Read), + outputFileStream = new FileStream(outputFile, FileMode.Append, FileAccess.Write)) + { + inputFileStream.Seek(0x4000, SeekOrigin.Begin); + for (int i = 0; i < ncchSize; i++) + { + inputFileStream.Read(buffer, 0, buffer.Length); + outputFileStream.Write(buffer, 0, buffer.Length); + if (PB_Show.InvokeRequired) + PB_Show.Invoke((MethodInvoker)PB_Show.PerformStep); + else { PB_Show.PerformStep(); } + + } + } + return outputFile; + } + internal static ulong Align(ulong input, ulong alignsize) { ulong output = input; @@ -110,5 +184,26 @@ internal static ulong Align(ulong input, ulong alignsize) } return output; } + + 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 { } + } } } diff --git a/pk3DS.Core/CTR/RomFS.cs b/pk3DS.Core/CTR/RomFS.cs index 1751e75..54e9a46 100644 --- a/pk3DS.Core/CTR/RomFS.cs +++ b/pk3DS.Core/CTR/RomFS.cs @@ -64,6 +64,194 @@ internal static void updateTB(RichTextBox RTB, string progress) catch { } } + public void ExtractRomFS(string outputDirectory, RichTextBox TB_Progress = null, ProgressBar PB_Show = null) + { + byte[] ivfcHeaderBytes = new byte[0x5C]; + using (FileStream file = new FileStream(FileName, FileMode.Open, FileAccess.Read)) + { + file.Read(ivfcHeaderBytes, 0, ivfcHeaderBytes.Length); + } + IVFCInfo ivfc = GetIVFCInfoFromBytes(ivfcHeaderBytes); + + ulong romfsDataOffset = Align(ivfc.Levels[0].HashOffset + ivfc.MasterHashSize, ivfc.Levels[2].BlockSize); + byte[] romfsInfoHeaderBytes = new byte[0x28]; + using (FileStream file = new FileStream(FileName, FileMode.Open, FileAccess.Read)) + { + file.Seek(Convert.ToInt64(romfsDataOffset), SeekOrigin.Begin); + file.Read(romfsInfoHeaderBytes, 0, romfsInfoHeaderBytes.Length); + } + Romfs_InfoHeader infoHeader = GetRomfsInfoHeaderFromBytes(romfsInfoHeaderBytes, Convert.ToUInt32(romfsDataOffset)); + + byte[] directoryMetadataBlock = new byte[infoHeader.Sections[1].Size]; + byte[] fileMetadataBlock = new byte[infoHeader.Sections[3].Size]; + using (FileStream file = new FileStream(FileName, FileMode.Open, FileAccess.Read)) + { + file.Seek(Convert.ToInt64(infoHeader.Sections[1].Offset), SeekOrigin.Begin); + file.Read(directoryMetadataBlock, 0, directoryMetadataBlock.Length); + + file.Seek(Convert.ToInt64(infoHeader.Sections[3].Offset), SeekOrigin.Begin); + file.Read(fileMetadataBlock, 0, fileMetadataBlock.Length); + } + + if (!Directory.Exists(outputDirectory)) + { + Directory.CreateDirectory(outputDirectory); + } + + VisitDirectory(0, outputDirectory, directoryMetadataBlock, fileMetadataBlock, infoHeader.DataOffset, TB_Progress, PB_Show); + } + + private static IVFCInfo GetIVFCInfoFromBytes(byte[] headerBytes) + { + IVFCInfo ivfc = new IVFCInfo(); + uint magic1 = BitConverter.ToUInt32(headerBytes, 0x00); + uint magic2 = BitConverter.ToUInt32(headerBytes, 0x04); + if (magic1 != 0x43465649 || magic2 != 0x10000) + { + throw new ArgumentException("Bad IVFC Header"); + } + ivfc.MasterHashSize = BitConverter.ToUInt32(headerBytes, 0x08); + ivfc.Levels = new IVFCLevel[3]; + for (int i = 0; i < 3; i++) + { + ivfc.Levels[i] = new IVFCLevel(); + ivfc.Levels[i].HashOffset = BitConverter.ToUInt64(headerBytes, 0x0C + (i * 0x18)); + ivfc.Levels[i].DataLength = BitConverter.ToUInt64(headerBytes, 0x14 + (i * 0x18)); + ivfc.Levels[i].BlockSize = 1U << BitConverter.ToInt32(headerBytes, 0x1C + (i * 0x18)); + } + return ivfc; + } + + private static Romfs_InfoHeader GetRomfsInfoHeaderFromBytes(byte[] headerBytes, uint bodyOffset) + { + Romfs_InfoHeader infoHeader = new Romfs_InfoHeader(); + infoHeader.HeaderLength = BitConverter.ToUInt32(headerBytes, 0x00); + infoHeader.Sections = new Romfs_SectionHeader[4]; + for (int i = 0; i < 4; i++) + { + infoHeader.Sections[i] = new Romfs_SectionHeader(); + infoHeader.Sections[i].Offset = BitConverter.ToUInt32(headerBytes, 0x04 + (i * 0x08)) + bodyOffset; + infoHeader.Sections[i].Size = BitConverter.ToUInt32(headerBytes, 0x08 + (i * 0x08)); + } + infoHeader.DataOffset = BitConverter.ToUInt32(headerBytes, 0x24) + bodyOffset; + return infoHeader; + } + + private void VisitDirectory( + uint directoryOffset, + string rootPath, + byte[] directoryMetadataBlock, + byte[] fileMetadataBlock, + ulong dataOffset, + RichTextBox TB_Progress = null, + ProgressBar PB_Show = null) + { + Romfs_DirEntry entry = GetDirEntryFromMetadataBytes(directoryMetadataBlock, Convert.ToInt32(directoryOffset)); + + string currentPath = Path.Combine(rootPath, entry.Name); + Directory.CreateDirectory(currentPath); + + if (entry.FileOffset != 0xFFFFFFFF) + { + VisitFile(entry.FileOffset, currentPath, fileMetadataBlock, dataOffset, TB_Progress, PB_Show); + } + + if (entry.ChildOffset != 0xFFFFFFFF) + { + VisitDirectory(entry.ChildOffset, currentPath, directoryMetadataBlock, fileMetadataBlock, dataOffset, TB_Progress, PB_Show); + } + + if (entry.SiblingOffset != 0xFFFFFFFF) + { + VisitDirectory(entry.SiblingOffset, rootPath, directoryMetadataBlock, fileMetadataBlock, dataOffset, TB_Progress, PB_Show); + } + + } + + private void VisitFile( + uint fileOffset, + string rootPath, + byte[] fileMetadataBlock, + ulong dataOffset, + RichTextBox TB_Progress = null, + ProgressBar PB_Show = null) + { + Romfs_FileEntry entry = GetFileEntryFromMetadataBytes(fileMetadataBlock, Convert.ToInt32(fileOffset)); + string currentPath = Path.Combine(rootPath, entry.Name); + UpdateTB_Progress(string.Format("Extracting {0} from romfs", currentPath), TB_Progress); + ExtractFileFromRomFS(entry, currentPath, dataOffset, PB_Show); + + if (entry.SiblingOffset != 0xFFFFFFFF) + { + VisitFile(entry.SiblingOffset, rootPath, fileMetadataBlock, dataOffset, TB_Progress, PB_Show); + } + } + + + private static Romfs_DirEntry GetDirEntryFromMetadataBytes(byte[] directoryMetadataBlock, int directoryOffset) + { + Romfs_DirEntry entry = new Romfs_DirEntry(); + entry.ParentOffset = BitConverter.ToUInt32(directoryMetadataBlock, directoryOffset); + entry.SiblingOffset = BitConverter.ToUInt32(directoryMetadataBlock, directoryOffset + 0x04); + entry.ChildOffset = BitConverter.ToUInt32(directoryMetadataBlock, directoryOffset + 0x08); + entry.FileOffset = BitConverter.ToUInt32(directoryMetadataBlock, directoryOffset + 0x0C); + uint nameLength = BitConverter.ToUInt32(directoryMetadataBlock, directoryOffset + 0x14); + entry.Name = ""; + if (nameLength != 0xFFFFFFFF) + { + byte[] fileNameBytes = new byte[nameLength]; + Array.Copy(directoryMetadataBlock, directoryOffset + 0x18, fileNameBytes, 0, fileNameBytes.Length); + entry.Name = Encoding.Unicode.GetString(fileNameBytes); + } + return entry; + } + + private static Romfs_FileEntry GetFileEntryFromMetadataBytes(byte[] fileMetadataBlock, int fileOffset) + { + Romfs_FileEntry entry = new Romfs_FileEntry(); + entry.ParentDirOffset = BitConverter.ToUInt32(fileMetadataBlock, fileOffset); + entry.SiblingOffset = BitConverter.ToUInt32(fileMetadataBlock, fileOffset + 0x04); + entry.DataOffset = BitConverter.ToUInt64(fileMetadataBlock, fileOffset + 0x08); + entry.DataSize = BitConverter.ToUInt64(fileMetadataBlock, fileOffset + 0x10); + entry.NameSize = BitConverter.ToUInt32(fileMetadataBlock, fileOffset + 0x1C); + entry.Name = ""; + if (entry.NameSize != 0xFFFFFFFF) + { + byte[] fileNameBytes = new byte[entry.NameSize]; + Array.Copy(fileMetadataBlock, fileOffset + 0x20, fileNameBytes, 0, fileNameBytes.Length); + entry.Name = Encoding.Unicode.GetString(fileNameBytes); + } + return entry; + } + + private void ExtractFileFromRomFS(Romfs_FileEntry entry, string path, ulong dataOffset, ProgressBar PB_Show = null) + { + ulong offsetInRomFS = dataOffset + entry.DataOffset; + byte[] buffer = new byte[0x2000]; + + using (FileStream romfsFileStream = new FileStream(FileName, FileMode.Open, FileAccess.Read), + outputFileStream = new FileStream(path, FileMode.Append, FileAccess.Write)) + { + romfsFileStream.Seek(Convert.ToInt64(offsetInRomFS), SeekOrigin.Begin); + int remainingSize = Convert.ToInt32(entry.DataSize); + if (PB_Show.InvokeRequired) + PB_Show.Invoke((MethodInvoker)delegate { PB_Show.Minimum = 0; PB_Show.Step = 1; PB_Show.Value = 0; PB_Show.Maximum = remainingSize / buffer.Length; }); + else { PB_Show.Minimum = 0; PB_Show.Step = 1; PB_Show.Value = 0; PB_Show.Maximum = remainingSize / buffer.Length; } + + while (remainingSize > 0) + { + int sizeToReadWrite = buffer.Length > remainingSize ? remainingSize : buffer.Length; + romfsFileStream.Read(buffer, 0, sizeToReadWrite); + outputFileStream.Write(buffer, 0, sizeToReadWrite); + remainingSize -= sizeToReadWrite; + if (PB_Show.InvokeRequired) + PB_Show.Invoke((MethodInvoker)PB_Show.PerformStep); + else { PB_Show.PerformStep(); } + } + } + + } + public static void BuildRomFS(string infile, string outfile, RichTextBox TB_Progress = null, ProgressBar PB_Show = null) { OutFile = outfile; @@ -283,7 +471,17 @@ internal static string ByteArrayToString(IEnumerable input) internal static void UpdateTB_Progress(string text, RichTextBox TB_Progress = null) { - TB_Progress.Text += text + Environment.NewLine; + if (TB_Progress.InvokeRequired) + { + TB_Progress.Invoke((MethodInvoker)delegate + { + TB_Progress.Text += text + Environment.NewLine; + }); + } + else + { + TB_Progress.Text += text + Environment.NewLine; + } } internal static void BuildRomFSHeader(MemoryStream romfs_stream, RomfsFile[] Entries, string DIR) @@ -736,6 +934,7 @@ public static ulong GetDataBlockLength(RomfsFile[] files, ulong PreData) public class IVFCInfo { + public uint MasterHashSize; public IVFCLevel[] Levels; } diff --git a/pk3DS.Core/Resources/romdata/byte/PlainRegion/SuMo.bin b/pk3DS.Core/Resources/romdata/byte/PlainRegion/SuMo.bin new file mode 100644 index 0000000..9e426b3 Binary files /dev/null and b/pk3DS.Core/Resources/romdata/byte/PlainRegion/SuMo.bin differ diff --git a/pk3DS.Core/Resources/romdata/byte/PlainRegion/USUM.bin b/pk3DS.Core/Resources/romdata/byte/PlainRegion/USUM.bin new file mode 100644 index 0000000..7e122a4 Binary files /dev/null and b/pk3DS.Core/Resources/romdata/byte/PlainRegion/USUM.bin differ diff --git a/pk3DS.Core/Resources/romdata/txt/3dsgames.txt b/pk3DS.Core/Resources/romdata/txt/3dsgames.txt index c715534..c39217b 100644 --- a/pk3DS.Core/Resources/romdata/txt/3dsgames.txt +++ b/pk3DS.Core/Resources/romdata/txt/3dsgames.txt @@ -3573,4 +3573,8 @@ 000400000011BB00 JXQE Johnny Dynamite 0004000000158C00 JT7E 3D Thunder Blade 0004000000158200 JT7P 3D Thunder Blade -000400000016AC00 JWXE Samurai Defender \ No newline at end of file +000400000016AC00 JWXE Samurai Defender +0004000000164800 BNDA Pokémon™ Sun +0004000000175E00 BNEA Pokémon™ Moon +00040000001B5000 A2AA Pokémon™ Ultra Sun +00040000001B5100 A2BA Pokémon™ Ultra Moon \ No newline at end of file diff --git a/pk3DS/Main.Designer.cs b/pk3DS/Main.Designer.cs index 89d23b3..8357de3 100644 --- a/pk3DS/Main.Designer.cs +++ b/pk3DS/Main.Designer.cs @@ -48,10 +48,13 @@ private void InitializeComponent() this.Menu_BLZ = new System.Windows.Forms.ToolStripMenuItem(); this.Menu_LZ11 = new System.Windows.Forms.ToolStripMenuItem(); this.Menu_Shuffler = new System.Windows.Forms.ToolStripMenuItem(); + this.Menu_Extract = new System.Windows.Forms.ToolStripMenuItem(); + this.Menu_Extract_CXI = new System.Windows.Forms.ToolStripMenuItem(); this.Menu_Rebuild = new System.Windows.Forms.ToolStripMenuItem(); this.Menu_RomFS = new System.Windows.Forms.ToolStripMenuItem(); this.Menu_ExeFS = new System.Windows.Forms.ToolStripMenuItem(); this.Menu_CRO = new System.Windows.Forms.ToolStripMenuItem(); + this.Menu_Trimmed3DS = new System.Windows.Forms.ToolStripMenuItem(); this.Menu_3DS = new System.Windows.Forms.ToolStripMenuItem(); this.Menu_Patch = new System.Windows.Forms.ToolStripMenuItem(); this.Menu_SMDH = new System.Windows.Forms.ToolStripMenuItem(); @@ -92,6 +95,7 @@ private void InitializeComponent() this.B_Static = new System.Windows.Forms.Button(); this.Tab_Output = new System.Windows.Forms.TabPage(); this.L_Status = new System.Windows.Forms.Label(); + this.Menu_Extract_3DS = new System.Windows.Forms.ToolStripMenuItem(); this.menuStrip1.SuspendLayout(); this.TC_RomFS.SuspendLayout(); this.Tab_RomFS.SuspendLayout(); @@ -102,19 +106,19 @@ private void InitializeComponent() this.FLP_CRO.SuspendLayout(); this.Tab_Output.SuspendLayout(); this.SuspendLayout(); - // + // // TB_Path - // - this.TB_Path.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + // + this.TB_Path.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.TB_Path.Location = new System.Drawing.Point(158, 3); this.TB_Path.Name = "TB_Path"; this.TB_Path.ReadOnly = true; this.TB_Path.Size = new System.Drawing.Size(214, 20); this.TB_Path.TabIndex = 1; - // + // // L_Game - // + // this.L_Game.AutoSize = true; this.L_Game.ForeColor = System.Drawing.Color.Red; this.L_Game.Location = new System.Drawing.Point(157, 23); @@ -122,18 +126,18 @@ private void InitializeComponent() this.L_Game.Size = new System.Drawing.Size(91, 13); this.L_Game.TabIndex = 2; this.L_Game.Text = "No Game Loaded"; - // + // // pBar1 - // - this.pBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + // + this.pBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.pBar1.Location = new System.Drawing.Point(12, 225); this.pBar1.Name = "pBar1"; this.pBar1.Size = new System.Drawing.Size(360, 14); this.pBar1.TabIndex = 6; - // + // // B_OPower - // + // this.B_OPower.Location = new System.Drawing.Point(215, 32); this.B_OPower.Name = "B_OPower"; this.B_OPower.Size = new System.Drawing.Size(100, 23); @@ -141,9 +145,9 @@ private void InitializeComponent() this.B_OPower.Text = "O-Powers"; this.B_OPower.UseVisualStyleBackColor = true; this.B_OPower.Click += new System.EventHandler(this.B_OPower_Click); - // + // // B_Pickup - // + // this.B_Pickup.Location = new System.Drawing.Point(109, 32); this.B_Pickup.Name = "B_Pickup"; this.B_Pickup.Size = new System.Drawing.Size(100, 23); @@ -151,9 +155,9 @@ private void InitializeComponent() this.B_Pickup.Text = "Pickup Items"; this.B_Pickup.UseVisualStyleBackColor = true; this.B_Pickup.Click += new System.EventHandler(this.B_Pickup_Click); - // + // // B_Mart - // + // this.B_Mart.Location = new System.Drawing.Point(3, 32); this.B_Mart.Name = "B_Mart"; this.B_Mart.Size = new System.Drawing.Size(100, 23); @@ -161,9 +165,9 @@ private void InitializeComponent() this.B_Mart.Text = "Poké Mart"; this.B_Mart.UseVisualStyleBackColor = true; this.B_Mart.Click += new System.EventHandler(this.B_Mart_Click); - // + // // B_MoveTutor - // + // this.B_MoveTutor.Location = new System.Drawing.Point(3, 3); this.B_MoveTutor.Name = "B_MoveTutor"; this.B_MoveTutor.Size = new System.Drawing.Size(100, 23); @@ -171,9 +175,9 @@ private void InitializeComponent() this.B_MoveTutor.Text = "Move Tutors"; this.B_MoveTutor.UseVisualStyleBackColor = true; this.B_MoveTutor.Click += new System.EventHandler(this.B_MoveTutor_Click); - // + // // B_TMHM - // + // this.B_TMHM.Location = new System.Drawing.Point(109, 3); this.B_TMHM.Name = "B_TMHM"; this.B_TMHM.Size = new System.Drawing.Size(100, 23); @@ -181,9 +185,9 @@ private void InitializeComponent() this.B_TMHM.Text = "TMs/HMs"; this.B_TMHM.UseVisualStyleBackColor = true; this.B_TMHM.Click += new System.EventHandler(this.B_TMHM_Click); - // + // // RTB_Status - // + // this.RTB_Status.Dock = System.Windows.Forms.DockStyle.Fill; this.RTB_Status.Location = new System.Drawing.Point(0, 0); this.RTB_Status.Name = "RTB_Status"; @@ -191,9 +195,9 @@ private void InitializeComponent() this.RTB_Status.Size = new System.Drawing.Size(352, 154); this.RTB_Status.TabIndex = 7; this.RTB_Status.Text = ""; - // + // // menuStrip1 - // + // this.menuStrip1.BackColor = System.Drawing.Color.Transparent; this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.Menu_File, @@ -204,54 +208,55 @@ private void InitializeComponent() this.menuStrip1.Size = new System.Drawing.Size(384, 24); this.menuStrip1.TabIndex = 9; this.menuStrip1.Text = "menuStrip1"; - // + // // Menu_File - // + // this.Menu_File.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.Menu_Open, this.Menu_Exit}); this.Menu_File.Name = "Menu_File"; this.Menu_File.Size = new System.Drawing.Size(37, 20); this.Menu_File.Text = "File"; - // + // // Menu_Open - // + // this.Menu_Open.Name = "Menu_Open"; this.Menu_Open.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O))); this.Menu_Open.Size = new System.Drawing.Size(155, 22); this.Menu_Open.Text = "&Open..."; this.Menu_Open.Click += new System.EventHandler(this.B_Open_Click); - // + // // Menu_Exit - // + // this.Menu_Exit.Name = "Menu_Exit"; this.Menu_Exit.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Q))); this.Menu_Exit.Size = new System.Drawing.Size(155, 22); this.Menu_Exit.Text = "&Exit"; this.Menu_Exit.Click += new System.EventHandler(this.Menu_Exit_Click); - // + // // Menu_Tools - // + // this.Menu_Tools.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.Menu_Restore, this.Menu_Misc, + this.Menu_Extract, this.Menu_Rebuild, this.Menu_SMDH, this.randomizationToolStripMenuItem}); this.Menu_Tools.Name = "Menu_Tools"; - this.Menu_Tools.Size = new System.Drawing.Size(47, 20); + this.Menu_Tools.Size = new System.Drawing.Size(46, 20); this.Menu_Tools.Text = "Tools"; - // + // // Menu_Restore - // + // this.Menu_Restore.Enabled = false; this.Menu_Restore.Name = "Menu_Restore"; this.Menu_Restore.Size = new System.Drawing.Size(184, 22); this.Menu_Restore.Text = "Restore Original Files"; this.Menu_Restore.Click += new System.EventHandler(this.L_Game_Click); - // + // // Menu_Misc - // + // this.Menu_Misc.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.unPackBCLIMToolStripMenuItem, this.Menu_BLZ, @@ -260,115 +265,140 @@ private void InitializeComponent() this.Menu_Misc.Name = "Menu_Misc"; this.Menu_Misc.Size = new System.Drawing.Size(184, 22); this.Menu_Misc.Text = "Misc Tools"; - // + // // unPackBCLIMToolStripMenuItem - // + // this.unPackBCLIMToolStripMenuItem.Name = "unPackBCLIMToolStripMenuItem"; this.unPackBCLIMToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.T))); this.unPackBCLIMToolStripMenuItem.Size = new System.Drawing.Size(216, 22); this.unPackBCLIMToolStripMenuItem.Text = "(un)Pack + BCLIM"; this.unPackBCLIMToolStripMenuItem.Click += new System.EventHandler(this.L_SubTools_Click); - // + // // Menu_BLZ - // + // this.Menu_BLZ.Name = "Menu_BLZ"; this.Menu_BLZ.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.B))); this.Menu_BLZ.Size = new System.Drawing.Size(216, 22); this.Menu_BLZ.Text = "(de)Compress BLZ"; this.Menu_BLZ.Click += new System.EventHandler(this.Menu_BLZ_Click); - // + // // Menu_LZ11 - // + // this.Menu_LZ11.Name = "Menu_LZ11"; this.Menu_LZ11.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.L))); this.Menu_LZ11.Size = new System.Drawing.Size(216, 22); this.Menu_LZ11.Text = "(de)Compress LZ11"; this.Menu_LZ11.Click += new System.EventHandler(this.Menu_LZ11_Click); - // + // // Menu_Shuffler - // + // this.Menu_Shuffler.Enabled = false; this.Menu_Shuffler.Name = "Menu_Shuffler"; this.Menu_Shuffler.Size = new System.Drawing.Size(216, 22); this.Menu_Shuffler.Text = "GARC Shuffler"; this.Menu_Shuffler.Click += new System.EventHandler(this.Menu_Shuffler_Click); - // + // + // Menu_Extract + // + this.Menu_Extract.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.Menu_Extract_CXI, + this.Menu_Extract_3DS}); + this.Menu_Extract.Name = "Menu_Extract"; + this.Menu_Extract.Size = new System.Drawing.Size(184, 22); + this.Menu_Extract.Text = "Extract..."; + // + // Menu_Extract_CXI + // + this.Menu_Extract_CXI.Name = "Menu_Extract_CXI"; + this.Menu_Extract_CXI.Size = new System.Drawing.Size(180, 22); + this.Menu_Extract_CXI.Text = ".CXI"; + this.Menu_Extract_CXI.Click += new System.EventHandler(this.B_ExtractCXI_Click); + // // Menu_Rebuild - // + // this.Menu_Rebuild.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.Menu_RomFS, this.Menu_ExeFS, this.Menu_CRO, + this.Menu_Trimmed3DS, this.Menu_3DS, this.Menu_Patch}); this.Menu_Rebuild.Name = "Menu_Rebuild"; this.Menu_Rebuild.Size = new System.Drawing.Size(184, 22); this.Menu_Rebuild.Text = "Rebuild..."; - // + // // Menu_RomFS - // + // this.Menu_RomFS.Enabled = false; this.Menu_RomFS.Name = "Menu_RomFS"; - this.Menu_RomFS.Size = new System.Drawing.Size(111, 22); + this.Menu_RomFS.Size = new System.Drawing.Size(180, 22); this.Menu_RomFS.Text = "RomFS"; this.Menu_RomFS.Click += new System.EventHandler(this.rebuildRomFS); - // + // // Menu_ExeFS - // + // this.Menu_ExeFS.Enabled = false; this.Menu_ExeFS.Name = "Menu_ExeFS"; - this.Menu_ExeFS.Size = new System.Drawing.Size(111, 22); + this.Menu_ExeFS.Size = new System.Drawing.Size(180, 22); this.Menu_ExeFS.Text = "ExeFS"; this.Menu_ExeFS.Click += new System.EventHandler(this.rebuildExeFS); - // + // // Menu_CRO - // + // this.Menu_CRO.Enabled = false; this.Menu_CRO.Name = "Menu_CRO"; - this.Menu_CRO.Size = new System.Drawing.Size(111, 22); + this.Menu_CRO.Size = new System.Drawing.Size(180, 22); this.Menu_CRO.Text = "CRO"; this.Menu_CRO.Click += new System.EventHandler(this.patchCRO_CRR); - // + // + // Menu_Trimmed3DS + // + this.Menu_Trimmed3DS.Enabled = false; + this.Menu_Trimmed3DS.Name = "Menu_Trimmed3DS"; + this.Menu_Trimmed3DS.Size = new System.Drawing.Size(180, 22); + this.Menu_Trimmed3DS.Text = "Trimmed .3DS"; + this.Menu_Trimmed3DS.Click += new System.EventHandler(this.B_RebuildTrimmed3DS_Click); + // // Menu_3DS - // + // this.Menu_3DS.Enabled = false; this.Menu_3DS.Name = "Menu_3DS"; - this.Menu_3DS.Size = new System.Drawing.Size(111, 22); + this.Menu_3DS.Size = new System.Drawing.Size(180, 22); this.Menu_3DS.Text = ".3DS"; this.Menu_3DS.Click += new System.EventHandler(this.B_Rebuild3DS_Click); - // + // // Menu_Patch - // + // this.Menu_Patch.Enabled = false; this.Menu_Patch.Name = "Menu_Patch"; - this.Menu_Patch.Size = new System.Drawing.Size(111, 22); + this.Menu_Patch.Size = new System.Drawing.Size(180, 22); this.Menu_Patch.Text = "Patch"; this.Menu_Patch.Click += new System.EventHandler(this.B_Patch_Click); - // + // // Menu_SMDH - // + // this.Menu_SMDH.Name = "Menu_SMDH"; this.Menu_SMDH.Size = new System.Drawing.Size(184, 22); this.Menu_SMDH.Text = "SMDH Editor (Icon)"; this.Menu_SMDH.Click += new System.EventHandler(this.Menu_SMDH_Click); - // + // // randomizationToolStripMenuItem - // + // this.randomizationToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.setInt32SeedToolStripMenuItem}); this.randomizationToolStripMenuItem.Name = "randomizationToolStripMenuItem"; this.randomizationToolStripMenuItem.Size = new System.Drawing.Size(184, 22); this.randomizationToolStripMenuItem.Text = "Randomization"; - // + // // setInt32SeedToolStripMenuItem - // + // this.setInt32SeedToolStripMenuItem.Name = "setInt32SeedToolStripMenuItem"; this.setInt32SeedToolStripMenuItem.Size = new System.Drawing.Size(146, 22); this.setInt32SeedToolStripMenuItem.Text = "Set int32 seed"; this.setInt32SeedToolStripMenuItem.Click += new System.EventHandler(this.setInt32SeedToolStripMenuItem_Click); - // + // // Menu_Options - // + // this.Menu_Options.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.Menu_Language, this.Menu_About, @@ -376,17 +406,17 @@ private void InitializeComponent() this.Menu_Options.Name = "Menu_Options"; this.Menu_Options.Size = new System.Drawing.Size(61, 20); this.Menu_Options.Text = "Options"; - // + // // Menu_Language - // + // this.Menu_Language.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.CB_Lang}); this.Menu_Language.Name = "Menu_Language"; this.Menu_Language.Size = new System.Drawing.Size(146, 22); this.Menu_Language.Text = "Language"; - // + // // CB_Lang - // + // this.CB_Lang.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.CB_Lang.Items.AddRange(new object[] { "カタカナ", @@ -402,27 +432,27 @@ private void InitializeComponent() this.CB_Lang.Name = "CB_Lang"; this.CB_Lang.Size = new System.Drawing.Size(121, 23); this.CB_Lang.SelectedIndexChanged += new System.EventHandler(this.ChangeLanguage); - // + // // Menu_About - // + // this.Menu_About.Name = "Menu_About"; this.Menu_About.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.B))); this.Menu_About.ShowShortcutKeys = false; this.Menu_About.Size = new System.Drawing.Size(146, 22); this.Menu_About.Text = "A&bout pk3DS"; this.Menu_About.Click += new System.EventHandler(this.L_About_Click); - // + // // Menu_GARCs - // + // this.Menu_GARCs.Name = "Menu_GARCs"; this.Menu_GARCs.Size = new System.Drawing.Size(146, 22); this.Menu_GARCs.Text = "About GARCs"; this.Menu_GARCs.Click += new System.EventHandler(this.L_GARCInfo_Click); - // + // // TC_RomFS - // - this.TC_RomFS.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) + // + this.TC_RomFS.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.TC_RomFS.Controls.Add(this.Tab_RomFS); this.TC_RomFS.Controls.Add(this.Tab_ExeFS); @@ -433,9 +463,9 @@ private void InitializeComponent() this.TC_RomFS.SelectedIndex = 0; this.TC_RomFS.Size = new System.Drawing.Size(360, 180); this.TC_RomFS.TabIndex = 11; - // + // // Tab_RomFS - // + // this.Tab_RomFS.Controls.Add(this.FLP_RomFS); this.Tab_RomFS.Location = new System.Drawing.Point(4, 22); this.Tab_RomFS.Name = "Tab_RomFS"; @@ -444,9 +474,9 @@ private void InitializeComponent() this.Tab_RomFS.TabIndex = 0; this.Tab_RomFS.Text = "RomFS"; this.Tab_RomFS.UseVisualStyleBackColor = true; - // + // // FLP_RomFS - // + // this.FLP_RomFS.AutoScroll = true; this.FLP_RomFS.Controls.Add(this.B_GameText); this.FLP_RomFS.Controls.Add(this.B_StoryText); @@ -468,9 +498,9 @@ private void InitializeComponent() this.FLP_RomFS.Name = "FLP_RomFS"; this.FLP_RomFS.Size = new System.Drawing.Size(346, 148); this.FLP_RomFS.TabIndex = 13; - // + // // B_GameText - // + // this.B_GameText.Location = new System.Drawing.Point(3, 3); this.B_GameText.Name = "B_GameText"; this.B_GameText.Size = new System.Drawing.Size(100, 23); @@ -478,9 +508,9 @@ private void InitializeComponent() this.B_GameText.Text = "Game Text"; this.B_GameText.UseVisualStyleBackColor = true; this.B_GameText.Click += new System.EventHandler(this.B_GameText_Click); - // + // // B_StoryText - // + // this.B_StoryText.Location = new System.Drawing.Point(109, 3); this.B_StoryText.Name = "B_StoryText"; this.B_StoryText.Size = new System.Drawing.Size(100, 23); @@ -488,9 +518,9 @@ private void InitializeComponent() this.B_StoryText.Text = "Story Text"; this.B_StoryText.UseVisualStyleBackColor = true; this.B_StoryText.Click += new System.EventHandler(this.B_StoryText_Click); - // + // // B_Personal - // + // this.B_Personal.Location = new System.Drawing.Point(215, 3); this.B_Personal.Name = "B_Personal"; this.B_Personal.Size = new System.Drawing.Size(100, 23); @@ -498,9 +528,9 @@ private void InitializeComponent() this.B_Personal.Text = "Personal Stats"; this.B_Personal.UseVisualStyleBackColor = true; this.B_Personal.Click += new System.EventHandler(this.B_Personal_Click); - // + // // B_Evolution - // + // this.B_Evolution.Location = new System.Drawing.Point(3, 32); this.B_Evolution.Name = "B_Evolution"; this.B_Evolution.Size = new System.Drawing.Size(100, 23); @@ -508,9 +538,9 @@ private void InitializeComponent() this.B_Evolution.Text = "Evolutions"; this.B_Evolution.UseVisualStyleBackColor = true; this.B_Evolution.Click += new System.EventHandler(this.B_Evolution_Click); - // + // // B_LevelUp - // + // this.B_LevelUp.Location = new System.Drawing.Point(109, 32); this.B_LevelUp.Name = "B_LevelUp"; this.B_LevelUp.Size = new System.Drawing.Size(100, 23); @@ -518,9 +548,9 @@ private void InitializeComponent() this.B_LevelUp.Text = "Level Up Moves"; this.B_LevelUp.UseVisualStyleBackColor = true; this.B_LevelUp.Click += new System.EventHandler(this.B_LevelUp_Click); - // + // // B_Wild - // + // this.B_Wild.Location = new System.Drawing.Point(215, 32); this.B_Wild.Name = "B_Wild"; this.B_Wild.Size = new System.Drawing.Size(100, 23); @@ -528,9 +558,9 @@ private void InitializeComponent() this.B_Wild.Text = "Wild Encounters"; this.B_Wild.UseVisualStyleBackColor = true; this.B_Wild.Click += new System.EventHandler(this.B_Wild_Click); - // + // // B_MegaEvo - // + // this.B_MegaEvo.Location = new System.Drawing.Point(3, 61); this.B_MegaEvo.Name = "B_MegaEvo"; this.B_MegaEvo.Size = new System.Drawing.Size(100, 23); @@ -538,9 +568,9 @@ private void InitializeComponent() this.B_MegaEvo.Text = "Mega Evolutions"; this.B_MegaEvo.UseVisualStyleBackColor = true; this.B_MegaEvo.Click += new System.EventHandler(this.B_MegaEvo_Click); - // + // // B_EggMove - // + // this.B_EggMove.Location = new System.Drawing.Point(109, 61); this.B_EggMove.Name = "B_EggMove"; this.B_EggMove.Size = new System.Drawing.Size(100, 23); @@ -548,9 +578,9 @@ private void InitializeComponent() this.B_EggMove.Text = "Egg Moves"; this.B_EggMove.UseVisualStyleBackColor = true; this.B_EggMove.Click += new System.EventHandler(this.B_EggMove_Click); - // + // // B_Trainer - // + // this.B_Trainer.Location = new System.Drawing.Point(215, 61); this.B_Trainer.Name = "B_Trainer"; this.B_Trainer.Size = new System.Drawing.Size(100, 23); @@ -558,9 +588,9 @@ private void InitializeComponent() this.B_Trainer.Text = "Trainers"; this.B_Trainer.UseVisualStyleBackColor = true; this.B_Trainer.Click += new System.EventHandler(this.B_Trainer_Click); - // + // // B_Maison - // + // this.B_Maison.Location = new System.Drawing.Point(3, 90); this.B_Maison.Name = "B_Maison"; this.B_Maison.Size = new System.Drawing.Size(100, 23); @@ -568,9 +598,9 @@ private void InitializeComponent() this.B_Maison.Text = "Battle Maison"; this.B_Maison.UseVisualStyleBackColor = true; this.B_Maison.Click += new System.EventHandler(this.B_Maison_Click); - // + // // B_Royal - // + // this.B_Royal.Location = new System.Drawing.Point(109, 90); this.B_Royal.Name = "B_Royal"; this.B_Royal.Size = new System.Drawing.Size(100, 23); @@ -578,9 +608,9 @@ private void InitializeComponent() this.B_Royal.Text = "Battle Royal/Tree"; this.B_Royal.UseVisualStyleBackColor = true; this.B_Royal.Click += new System.EventHandler(this.B_Maison_Click); - // + // // B_Item - // + // this.B_Item.Location = new System.Drawing.Point(215, 90); this.B_Item.Name = "B_Item"; this.B_Item.Size = new System.Drawing.Size(100, 23); @@ -588,9 +618,9 @@ private void InitializeComponent() this.B_Item.Text = "Item Stats"; this.B_Item.UseVisualStyleBackColor = true; this.B_Item.Click += new System.EventHandler(this.B_Item_Click); - // + // // B_Move - // + // this.B_Move.Location = new System.Drawing.Point(3, 119); this.B_Move.Name = "B_Move"; this.B_Move.Size = new System.Drawing.Size(100, 23); @@ -598,9 +628,9 @@ private void InitializeComponent() this.B_Move.Text = "Move Stats"; this.B_Move.UseVisualStyleBackColor = true; this.B_Move.Click += new System.EventHandler(this.B_Move_Click); - // + // // B_TitleScreen - // + // this.B_TitleScreen.Location = new System.Drawing.Point(109, 119); this.B_TitleScreen.Name = "B_TitleScreen"; this.B_TitleScreen.Size = new System.Drawing.Size(100, 23); @@ -608,9 +638,9 @@ private void InitializeComponent() this.B_TitleScreen.Text = "Title Screen"; this.B_TitleScreen.UseVisualStyleBackColor = true; this.B_TitleScreen.Click += new System.EventHandler(this.B_TitleScreen_Click); - // + // // B_OWSE - // + // this.B_OWSE.Location = new System.Drawing.Point(215, 119); this.B_OWSE.Name = "B_OWSE"; this.B_OWSE.Size = new System.Drawing.Size(100, 23); @@ -618,9 +648,9 @@ private void InitializeComponent() this.B_OWSE.Text = "OWSE [Dev]"; this.B_OWSE.UseVisualStyleBackColor = true; this.B_OWSE.Click += new System.EventHandler(this.B_OWSE_Click); - // + // // Tab_ExeFS - // + // this.Tab_ExeFS.Controls.Add(this.FLP_ExeFS); this.Tab_ExeFS.Location = new System.Drawing.Point(4, 22); this.Tab_ExeFS.Name = "Tab_ExeFS"; @@ -629,9 +659,9 @@ private void InitializeComponent() this.Tab_ExeFS.TabIndex = 1; this.Tab_ExeFS.Text = "ExeFS"; this.Tab_ExeFS.UseVisualStyleBackColor = true; - // + // // FLP_ExeFS - // + // this.FLP_ExeFS.AutoScroll = true; this.FLP_ExeFS.Controls.Add(this.B_MoveTutor); this.FLP_ExeFS.Controls.Add(this.B_TMHM); @@ -645,9 +675,9 @@ private void InitializeComponent() this.FLP_ExeFS.Name = "FLP_ExeFS"; this.FLP_ExeFS.Size = new System.Drawing.Size(346, 148); this.FLP_ExeFS.TabIndex = 11; - // + // // B_TM - // + // this.B_TM.Location = new System.Drawing.Point(215, 3); this.B_TM.Name = "B_TM"; this.B_TM.Size = new System.Drawing.Size(100, 23); @@ -655,9 +685,9 @@ private void InitializeComponent() this.B_TM.Text = "TMs"; this.B_TM.UseVisualStyleBackColor = true; this.B_TM.Click += new System.EventHandler(this.B_TMHM_Click); - // + // // B_ShinyRate - // + // this.B_ShinyRate.Location = new System.Drawing.Point(3, 61); this.B_ShinyRate.Name = "B_ShinyRate"; this.B_ShinyRate.Size = new System.Drawing.Size(100, 23); @@ -665,9 +695,9 @@ private void InitializeComponent() this.B_ShinyRate.Text = "Shiny Rate"; this.B_ShinyRate.UseVisualStyleBackColor = true; this.B_ShinyRate.Click += new System.EventHandler(this.B_ShinyRate_Click); - // + // // Tab_CRO - // + // this.Tab_CRO.Controls.Add(this.FLP_CRO); this.Tab_CRO.Location = new System.Drawing.Point(4, 22); this.Tab_CRO.Name = "Tab_CRO"; @@ -675,9 +705,9 @@ private void InitializeComponent() this.Tab_CRO.TabIndex = 2; this.Tab_CRO.Text = "CRO"; this.Tab_CRO.UseVisualStyleBackColor = true; - // + // // FLP_CRO - // + // this.FLP_CRO.Controls.Add(this.B_TypeChart); this.FLP_CRO.Controls.Add(this.B_Starter); this.FLP_CRO.Controls.Add(this.B_Gift); @@ -688,9 +718,9 @@ private void InitializeComponent() this.FLP_CRO.Padding = new System.Windows.Forms.Padding(3); this.FLP_CRO.Size = new System.Drawing.Size(352, 154); this.FLP_CRO.TabIndex = 1; - // + // // B_TypeChart - // + // this.B_TypeChart.Location = new System.Drawing.Point(6, 6); this.B_TypeChart.Name = "B_TypeChart"; this.B_TypeChart.Size = new System.Drawing.Size(100, 23); @@ -698,9 +728,9 @@ private void InitializeComponent() this.B_TypeChart.Text = "Type Chart"; this.B_TypeChart.UseVisualStyleBackColor = true; this.B_TypeChart.Click += new System.EventHandler(this.B_TypeChart_Click); - // + // // B_Starter - // + // this.B_Starter.Location = new System.Drawing.Point(112, 6); this.B_Starter.Name = "B_Starter"; this.B_Starter.Size = new System.Drawing.Size(100, 23); @@ -708,9 +738,9 @@ private void InitializeComponent() this.B_Starter.Text = "Starter Pokémon"; this.B_Starter.UseVisualStyleBackColor = true; this.B_Starter.Click += new System.EventHandler(this.B_Starter_Click); - // + // // B_Gift - // + // this.B_Gift.Location = new System.Drawing.Point(218, 6); this.B_Gift.Name = "B_Gift"; this.B_Gift.Size = new System.Drawing.Size(100, 23); @@ -718,9 +748,9 @@ private void InitializeComponent() this.B_Gift.Text = "Gift Pokémon"; this.B_Gift.UseVisualStyleBackColor = true; this.B_Gift.Click += new System.EventHandler(this.B_Gift_Click); - // + // // B_Static - // + // this.B_Static.Location = new System.Drawing.Point(6, 35); this.B_Static.Name = "B_Static"; this.B_Static.Size = new System.Drawing.Size(100, 23); @@ -728,9 +758,9 @@ private void InitializeComponent() this.B_Static.Text = "Static Encounters"; this.B_Static.UseVisualStyleBackColor = true; this.B_Static.Click += new System.EventHandler(this.B_Static_Click); - // + // // Tab_Output - // + // this.Tab_Output.Controls.Add(this.RTB_Status); this.Tab_Output.Location = new System.Drawing.Point(4, 22); this.Tab_Output.Name = "Tab_Output"; @@ -738,17 +768,24 @@ private void InitializeComponent() this.Tab_Output.TabIndex = 3; this.Tab_Output.Text = "Output"; this.Tab_Output.UseVisualStyleBackColor = true; - // + // // L_Status - // + // this.L_Status.Location = new System.Drawing.Point(203, 37); this.L_Status.Name = "L_Status"; this.L_Status.Size = new System.Drawing.Size(163, 20); this.L_Status.TabIndex = 14; this.L_Status.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // + // + // Menu_Extract_3DS + // + this.Menu_Extract_3DS.Name = "Menu_Extract_3DS"; + this.Menu_Extract_3DS.Size = new System.Drawing.Size(180, 22); + this.Menu_Extract_3DS.Text = ".3DS"; + this.Menu_Extract_3DS.Click += new System.EventHandler(this.B_Extract3DS_Click); + // // Main - // + // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(384, 252); @@ -845,5 +882,9 @@ private void InitializeComponent() private System.Windows.Forms.Button B_TM; private System.Windows.Forms.ToolStripMenuItem randomizationToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem setInt32SeedToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem Menu_Extract; + private System.Windows.Forms.ToolStripMenuItem Menu_Extract_CXI; + private System.Windows.Forms.ToolStripMenuItem Menu_Trimmed3DS; + private System.Windows.Forms.ToolStripMenuItem Menu_Extract_3DS; } } \ No newline at end of file diff --git a/pk3DS/Main.cs b/pk3DS/Main.cs index 327e24a..fcaec0a 100644 --- a/pk3DS/Main.cs +++ b/pk3DS/Main.cs @@ -234,6 +234,8 @@ private void openQuick(string path) Menu_Patch.Enabled = RomFSPath != null && ExeFSPath != null; Menu_3DS.Enabled = ExHeaderPath != null && RomFSPath != null && ExeFSPath != null; + Menu_Trimmed3DS.Enabled = + ExHeaderPath != null && RomFSPath != null && ExeFSPath != null; // Change L_Game if RomFS and ExeFS exists to a better descriptor SMDH = ExeFSPath != null ? File.Exists(Path.Combine(ExeFSPath, "icon.bin")) ? new SMDH(Path.Combine(ExeFSPath, "icon.bin")) : null : null; @@ -249,6 +251,84 @@ private void openQuick(string path) } } + private void B_ExtractCXI_Click(object sender, EventArgs e) + { + if (WinFormsUtil.Prompt( + MessageBoxButtons.OKCancel, + "Extracting a CXI requires multiple GB of disc space and takes some time to complete.", + "If you want to continue, press OK to select your CXI and then select your output directory. For best results, make sure the output directory is an empty directory.") == DialogResult.OK) + { + string inputCXI; + OpenFileDialog ofd = new OpenFileDialog(); + ofd.Title = "Select CXI"; + ofd.Filter = "CXI files (*.cxi)|*.cxi"; + if (ofd.ShowDialog() == DialogResult.OK) + inputCXI = ofd.FileName; + else + return; + + FolderBrowserDialog fbd = new FolderBrowserDialog(); + DialogResult result = fbd.ShowDialog(); + if (result == DialogResult.OK) + extractNCCH(inputCXI, fbd.SelectedPath); + } + + } + + private void B_Extract3DS_Click(object sender, EventArgs e) + { + if (WinFormsUtil.Prompt( + MessageBoxButtons.OKCancel, + "Extracting a 3DS file requires multiple GB of disc space and takes some time to complete.", + "If you want to continue, press OK to select your CXI and then select your output directory. For best results, make sure the output directory is an empty directory.") == DialogResult.OK) + { + string input3DS; + OpenFileDialog ofd = new OpenFileDialog(); + ofd.Title = "Select 3DS"; + ofd.Filter = "3DS files (*.3ds)|*.3ds"; + if (ofd.ShowDialog() == DialogResult.OK) + input3DS = ofd.FileName; + else + return; + + FolderBrowserDialog fbd = new FolderBrowserDialog(); + DialogResult result = fbd.ShowDialog(); + if (result == DialogResult.OK) + extractNCSD(input3DS, fbd.SelectedPath); + } + } + + private void extractNCCH(string ncchPath, string outputDirectory) + { + if (!File.Exists(ncchPath)) + return; + + NCCH ncch = new NCCH(); + + new Thread(() => + { + threads++; + ncch.ExtractNCCHFromFile(ncchPath, outputDirectory, RTB_Status, pBar1); + threads--; + WinFormsUtil.Prompt(MessageBoxButtons.OK, "Extraction complete!"); + }).Start(); + } + + private void extractNCSD(string ncsdPath, string outputDirectory) + { + if (!File.Exists(ncsdPath)) + return; + + NCSD ncsd = new NCSD(); + new Thread(() => + { + threads++; + ncsd.ExtractFilesFromNCSD(ncsdPath, outputDirectory, RTB_Status, pBar1); + threads--; + WinFormsUtil.Prompt(MessageBoxButtons.OK, "Extraction complete!"); + }).Start(); + } + private void ToggleSubEditors() { // Hide all buttons @@ -1140,16 +1220,9 @@ private void B_Static_Click(object sender, EventArgs e) new StaticEncounterEditor6().ShowDialog(); } - // 3DS Building - private void B_Rebuild3DS_Click(object sender, EventArgs e) + // CXI Building + private void B_RebuildTrimmed3DS_Click(object sender, EventArgs e) { - // Ensure that the romfs paths are valid - string[] files = Directory.GetFiles(TB_Path.Text, "*", SearchOption.AllDirectories); - if (!Config.IsRebuildable(files.Length)) - { - WinFormsUtil.Error("RomFS file count does not match the default game file count."); - return; - } if (threadActive()) return; @@ -1166,8 +1239,33 @@ private void B_Rebuild3DS_Click(object sender, EventArgs e) { threads++; Exheader exh = new Exheader(ExHeaderPath); - CTRUtil.buildROM(true, "Nintendo", ExeFSPath, RomFSPath, ExHeaderPath, exh.GetSerial(), path, pBar1, - RTB_Status); + CTRUtil.buildROM(true, "Nintendo", ExeFSPath, RomFSPath, ExHeaderPath, exh.GetSerial(), path, + true, pBar1, RTB_Status); + threads--; + }).Start(); + } + + // 3DS Building + private void B_Rebuild3DS_Click(object sender, EventArgs e) + { + if (threadActive()) + return; + + SaveFileDialog sfd = new SaveFileDialog + { + FileName = "newROM.3ds", + Filter = "Binary File|*.*" + }; + if (sfd.ShowDialog() != DialogResult.OK) + return; + string path = sfd.FileName; + + new Thread(() => + { + threads++; + Exheader exh = new Exheader(ExHeaderPath); + CTRUtil.buildROM(true, "Nintendo", ExeFSPath, RomFSPath, ExHeaderPath, exh.GetSerial(), path, + false, pBar1, RTB_Status); threads--; }).Start(); }