using System; using System.Collections.Generic; using System.Linq; using System.Text; using LibEveryFileExplorer.Files; using System.Drawing; using System.IO; using _3DS.UI; using LibEveryFileExplorer.IO; namespace _3DS { public class ThemeFile : FileFormat, IViewable, IWriteable { public byte[] Write() { MemoryStream m = new MemoryStream(); EndianBinaryWriter er = new EndianBinaryWriter(m, Endianness.LittleEndian); er.BaseStream.Position = 0xD0;//just after header - normal start of data for theme files header.topScreenTextureOffset = (uint)er.BaseStream.Position; er.Write(topScreenTexture, 0, topScreenTexture.Length); header.bottomScreenSolidOrTextureOffset = (uint)er.BaseStream.Position; er.Write(bottomScreenTexture, 0, bottomScreenTexture.Length); header.openedFolderTextureOffset = (uint)er.BaseStream.Position; er.Write(openFolderTexture, 0, openFolderTexture.Length); header.closedFolderTextureOffset = (uint)er.BaseStream.Position; er.Write(closedFolderTexture, 0, closedFolderTexture.Length); header.iconBorder48pxOffset = (uint)er.BaseStream.Position; er.Write(iconBorder48pxTexture, 0, iconBorder48pxTexture.Length); header.iconBorder24pxOffset = (uint)er.BaseStream.Position; er.Write(iconBorder24pxTexture, 0, iconBorder24pxTexture.Length); header.Write(er); return m.ToArray(); } public string GetSaveDefaultFileFilter() { return "System Menu Theme (body_LZ.bin)|body_LZ.bin"; } public Color topSolidColor; public Color bottomSolidColor; public ThemeFile(byte[] Data) { EndianBinaryReader er = new EndianBinaryReader(new MemoryStream(Data), Endianness.LittleEndian); try { header = new ThemeHeader(er); topHeight = 256; bottomHeight = 256; switch (header.topScreenDrawType) { case 1://solid color er.BaseStream.Position = header.topScreenSolidColorDataOffset;//7 bytes here, first 4 is rgba, other 3 unknown (possible overlay colour ??) Byte r = er.ReadByte(); Byte g = er.ReadByte(); Byte b = er.ReadByte(); Byte a1 = er.ReadByte();//alpha makes the colours appear lighter because the 3ds does blending topSolidColor = Color.FromArgb(a1, r, g, b); break; case 2://wierd 64x64 overlay mode topWidth = 64; topHeight = 64; topClampWidth = 64; topClampHeight = 64; break; case 3://texture switch (header.topScreenFrameType) { case 0://tex1 case 3://tex1 topWidth = 1024; topClampWidth = 1008; topClampHeight = 240; break; case 1://tex0 topWidth = 512; topClampWidth = 412; topClampHeight = 240; break; } break; } switch (header.bottomScreenDrawType) { case 1://solid color er.BaseStream.Position = header.bottomScreenSolidOrTextureOffset;//7 bytes here, first 4 is rgba, other 3 unknown (possible overlay colour ??) Byte r = er.ReadByte(); Byte g = er.ReadByte(); Byte b = er.ReadByte(); Byte a1 = er.ReadByte();//alpha makes the colours appear lighter because the 3ds does blending bottomSolidColor = Color.FromArgb(a1, r, g, b); break; case 3://texture switch (header.bottomScreenFrameType) { case 0://tex4 case 3://tex4 bottomWidth = 1024; bottomClampWidth = 1008; bottomClampHeight = 240; break; case 1://tex2 bottomWidth = 512; bottomClampWidth = 320; bottomClampHeight = 240; break; case 4://tex3 //both animated.? case 2://tex3 bottomWidth = 1024; bottomClampWidth = 960; bottomClampHeight = 240; break; } break; } er.BaseStream.Position = header.topScreenTextureOffset; if (header.topScreenDrawType == 2)// in draw type 2 theyre are 2 alpha images, one at topscreentextureoffset and the other at the colour offset, they need to be drawn onto the gradient background { topOverlayAlphaTexture = er.ReadBytes(alphaTextureWidth * alphaTextureHeight); er.BaseStream.Position = header.topScreenAdditionalTextureOffset; topBackgroundAlphaTexture = er.ReadBytes(alphaTextureWidth * alphaTextureHeight); //TODO figure out colour data for gradients etc er.BaseStream.Position = header.topScreenSolidColorDataOffset; Byte type = er.ReadByte(); Byte r1 = er.ReadByte(); Byte g1 = er.ReadByte(); Byte b1 = er.ReadByte(); Byte r2 = er.ReadByte(); Byte g2 = er.ReadByte(); Byte b2 = er.ReadByte(); Console.WriteLine("type? " + type); Console.WriteLine("color " + r1 + " " + g1 + " " + b1); Console.WriteLine("color " + r2 + " " + g2 + " " + b2); } else topScreenTexture = er.ReadBytes((topHeight * topWidth) * 2); er.BaseStream.Position = header.bottomScreenSolidOrTextureOffset; bottomScreenTexture = er.ReadBytes((bottomHeight * bottomWidth) * 2); er.BaseStream.Position = header.openedFolderTextureOffset; openFolderTexture = er.ReadBytes((folderWidth * folderHeight) * 4); er.BaseStream.Position = header.closedFolderTextureOffset; closedFolderTexture = er.ReadBytes((folderWidth * folderHeight) * 4); er.BaseStream.Position = header.iconBorder48pxOffset; iconBorder48pxTexture = er.ReadBytes((iconBorder48pxWidth * iconBorder48pxHeight) * 4); er.BaseStream.Position = header.iconBorder24pxOffset; iconBorder24pxTexture = er.ReadBytes((iconBorder24pxWidth * iconBorder24pxHeight) * 4); er.BaseStream.Position = header.audioSectionOffset; audioSection = new AudioSection(er); } finally { er.Close(); } } public Image testImage; public int iconBorder48pxWidth = 64; public int iconBorder48pxHeight = 128; public byte[] iconBorder48pxTexture; public Bitmap GetIconBorder48px(bool clamp) { if (clamp) return GPU.Textures.ToBitmap(iconBorder48pxTexture, 36, 72, GPU.Textures.ImageFormat.RGB8, false); return GPU.Textures.ToBitmap(iconBorder48pxTexture, iconBorder48pxWidth, iconBorder48pxHeight, GPU.Textures.ImageFormat.RGB8, true); } public int iconBorder24pxWidth = 32; public int iconBorder24pxHeight = 64; public byte[] iconBorder24pxTexture; public Bitmap GetIconBorder24px(bool clamp) { if (clamp) return GPU.Textures.ToBitmap(iconBorder24pxTexture, 25, 50, GPU.Textures.ImageFormat.RGB8, false); return GPU.Textures.ToBitmap(iconBorder24pxTexture, iconBorder24pxWidth, iconBorder24pxHeight, GPU.Textures.ImageFormat.RGB8, true); } public int folderWidth = 128; public int folderHeight = 64; public byte[] openFolderTexture; public byte[] closedFolderTexture; public Bitmap GetOpenFolderTexture(bool clamp) { if (clamp) return GPU.Textures.ToBitmap(openFolderTexture, 82, 64, GPU.Textures.ImageFormat.RGB8, false); return GPU.Textures.ToBitmap(openFolderTexture, folderWidth, folderHeight, GPU.Textures.ImageFormat.RGB8, true); } public Bitmap GetClosedFolderTexture(bool clamp) { if (clamp) return GPU.Textures.ToBitmap(closedFolderTexture, 74, 64, GPU.Textures.ImageFormat.RGB8, false); return GPU.Textures.ToBitmap(closedFolderTexture, folderWidth, folderHeight, GPU.Textures.ImageFormat.RGB8, true); } public int bottomWidth; public int bottomHeight; public byte[] bottomScreenTexture; public int bottomClampWidth; public int bottomClampHeight; public Bitmap GetBottomTexture(bool clamp) { if (clamp) return GPU.Textures.ToBitmap(bottomScreenTexture, bottomClampWidth, bottomClampHeight, GPU.Textures.ImageFormat.RGB565, false); return GPU.Textures.ToBitmap(bottomScreenTexture, bottomWidth, bottomHeight, GPU.Textures.ImageFormat.RGB565, true); } public int topWidth; public int topHeight; public byte[] topScreenTexture; public int topClampWidth; public int topClampHeight; public Bitmap GetTopTexture(bool clamp) { if (clamp) return GPU.Textures.ToBitmap(topScreenTexture, topClampWidth, topClampHeight, GPU.Textures.ImageFormat.RGB565, false); return GPU.Textures.ToBitmap(topScreenTexture, topWidth, topHeight, GPU.Textures.ImageFormat.RGB565, true); } public byte[] topOverlayAlphaTexture; public byte[] topBackgroundAlphaTexture; public int alphaTextureWidth = 64; public int alphaTextureHeight = 64; public Bitmap GetOverlayAlphaTexture()///Overlayed in a moving pattern ontop of the TopAlphaTexture { return GPU.Textures.ToBitmap(topOverlayAlphaTexture, alphaTextureWidth, alphaTextureHeight, GPU.Textures.ImageFormat.A8, true); } public Bitmap GetBackgroundAlphaTexture()//Static texture background of top screen, does not move. { return GPU.Textures.ToBitmap(topBackgroundAlphaTexture, alphaTextureWidth, alphaTextureHeight, GPU.Textures.ImageFormat.A8, true); } public System.Windows.Forms.Form GetDialog() { return new ThemeViewer(this); } public AudioSection audioSection; public class AudioSection { public AudioSection(EndianBinaryReader er) { Console.WriteLine("audio section at " + er.BaseStream.Position); version = er.ReadUInt32(); flag = er.ReadUInt32(); //start of cwav cwavSize = er.ReadUInt32(); er.ReadUInt32();//2nd flag? Console.WriteLine("cwavSize " + cwavSize); //cwav Console.WriteLine("cwav at " + er.BaseStream.Position); } UInt32 version; UInt32 flag; byte[] cwav6ExtraData; UInt32 cwavSize; byte[] cwav4ExtraData; } public ThemeHeader header; public class ThemeHeader { public ThemeHeader(EndianBinaryReader er) { //offset 0x0 version = er.ReadUInt32(); unknownByte1 = er.ReadByte(); backgroundMusicEnabled = er.ReadByte() == 1; unknownByte2 = er.ReadByte(); unknownByte3 = er.ReadByte(); unknownInt2 = er.ReadUInt32(); //offset 0xC topScreenDrawType = er.ReadUInt32();// 0 = none, 1 = solid colour, 2 = extension of val1, 3 = texture topScreenFrameType = er.ReadUInt32();//0 = solid colour(only when draw type is 1 else uses tex 0), 1 = texture0, 3 = texture1 topScreenSolidColorDataOffset = er.ReadUInt32(); topScreenTextureOffset = er.ReadUInt32(); topScreenAdditionalTextureOffset = er.ReadUInt32();//used with draw type val2, optional when using draw type val2 System.Console.WriteLine("topScreenDrawType " + topScreenDrawType); System.Console.WriteLine("topScreenFrameType " + topScreenFrameType); System.Console.WriteLine("topScreenAdditionalTextureOffset " + topScreenAdditionalTextureOffset); System.Console.WriteLine("topScreenSolidColorDataOffset " + topScreenSolidColorDataOffset); //offset 0x20 bottomScreenDrawType = er.ReadUInt32(); bottomScreenFrameType = er.ReadUInt32(); bottomScreenSolidOrTextureOffset = er.ReadUInt32(); System.Console.WriteLine("bottomScreenDrawType " + bottomScreenDrawType); System.Console.WriteLine("bottomScreenFrameType " + bottomScreenFrameType); System.Console.WriteLine("bottomScreenSolidOrTextureOffset " + bottomScreenSolidOrTextureOffset); //offset 0x2C useUnknownBlock0 = er.ReadUInt32() == 1; unknownBlock0Offset = er.ReadUInt32();//0xC length //offset 0x34 useUnknownBlock1 = er.ReadUInt32() == 1; unknownBlock1Offset = er.ReadUInt32();//0xC length //offset 0x3C useFolderTextures = er.ReadUInt32() == 1; closedFolderTextureOffset = er.ReadUInt32();//texture 6 openedFolderTextureOffset = er.ReadUInt32();//texture 7 //offset 0x48 useUnknownBlock2 = er.ReadUInt32() == 1; unknownBlock2Offset = er.ReadUInt32();//0xD length //offset 0x50 useIconBorders = er.ReadUInt32() == 1; iconBorder48pxOffset = er.ReadUInt32(); iconBorder24pxOffset = er.ReadUInt32(); //offset 0x4C useUnknownBlock3 = er.ReadUInt32() == 1; unknownBlock3Offset = er.ReadUInt32();//0xD length useUnknownBlock4 = er.ReadUInt32() == 1; unknownBlock4Offset = er.ReadUInt32();//0x9 length useUnknownBlock5and6 = er.ReadUInt32() == 1; unknownBlock5Offset = er.ReadUInt32();//0x20 length unknownBlock6Offset = er.ReadUInt32();//0x20 length useUnknownBlock7 = er.ReadUInt32() == 1; unknownBlock7Offset = er.ReadUInt32();//0xD length useUnknownBlock8 = er.ReadUInt32() == 1; unknownBlock8Offset = er.ReadUInt32();//0xD length useUnknownBlock9 = er.ReadUInt32() == 1; unknownBlock9Offset = er.ReadUInt32();//0x9 length useUnknownBlock10 = er.ReadUInt32() == 1; unknownBlock10Offset = er.ReadUInt32();//0xD length useUnknownBlock11 = er.ReadUInt32() == 1; unknownBlock11Offset = er.ReadUInt32();//0x20 length useUnknownBlock12 = er.ReadUInt32() == 1; unknownBlock12Offset = er.ReadUInt32();//0x15 length useUnknownBlock13 = er.ReadUInt32() == 1; unknownBlock13Offset = er.ReadUInt32();//0xC length useUnknownBlock14 = er.ReadUInt32() == 1; unknownBlock14Offset = er.ReadUInt32();//0x6 length useAudioSection = er.ReadUInt32() == 1; audioSectionSize = er.ReadUInt32(); audioSectionOffset = er.ReadUInt32(); System.Console.WriteLine("useFolderTextures " + useFolderTextures); System.Console.WriteLine("useIconBorders " + useIconBorders); System.Console.WriteLine("useAudioSection " + useAudioSection); System.Console.WriteLine("audioSectionSize " + audioSectionSize); System.Console.WriteLine("audioSectionOffset " + audioSectionOffset); } public void Write(EndianBinaryWriter er) { er.BaseStream.Position = 0; //write header data er.Write(version); er.Write(unknownByte1); er.Write((byte)(backgroundMusicEnabled ? 1:0)); er.Write(unknownByte2); er.Write(unknownByte3); er.Write(unknownInt2); er.Write(topScreenDrawType); er.Write(topScreenFrameType); er.Write(topScreenSolidColorDataOffset); er.Write(topScreenTextureOffset); er.Write(topScreenAdditionalTextureOffset); er.Write(bottomScreenDrawType); er.Write(bottomScreenFrameType); er.Write(bottomScreenSolidOrTextureOffset); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write(useFolderTextures ? 1u : 0u);//TODO write 0 instead or somethign when this is disabled? er.Write(closedFolderTextureOffset); er.Write(openedFolderTextureOffset); er.Write((UInt32)0); er.Write((UInt32)0); er.Write(useIconBorders ? 1u : 0u);//TODO write 0 instead or somethign when this is disabled? er.Write(iconBorder48pxOffset); er.Write(iconBorder24pxOffset); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); er.Write((UInt32)0); } public UInt32 version; public Byte unknownByte1; public bool backgroundMusicEnabled; public Byte unknownByte2; public Byte unknownByte3; public UInt32 unknownInt2; public UInt32 topScreenDrawType; public UInt32 topScreenFrameType; public UInt32 topScreenSolidColorDataOffset; public UInt32 topScreenTextureOffset; public UInt32 topScreenAdditionalTextureOffset; public UInt32 bottomScreenDrawType; public UInt32 bottomScreenFrameType; public UInt32 bottomScreenSolidOrTextureOffset; public bool useUnknownBlock0; public UInt32 unknownBlock0Offset; public bool useUnknownBlock1; public UInt32 unknownBlock1Offset; public bool useFolderTextures;//6 and 7 public UInt32 closedFolderTextureOffset; public UInt32 openedFolderTextureOffset; public bool useUnknownBlock2; public UInt32 unknownBlock2Offset; public bool useIconBorders; public UInt32 iconBorder48pxOffset; public UInt32 iconBorder24pxOffset; public bool useUnknownBlock3; public UInt32 unknownBlock3Offset; public bool useUnknownBlock4; public UInt32 unknownBlock4Offset; public bool useUnknownBlock5and6; public UInt32 unknownBlock5Offset; public UInt32 unknownBlock6Offset; public bool useUnknownBlock7; public UInt32 unknownBlock7Offset; public bool useUnknownBlock8; public UInt32 unknownBlock8Offset; public bool useUnknownBlock9; public UInt32 unknownBlock9Offset; public bool useUnknownBlock10; public UInt32 unknownBlock10Offset; public bool useUnknownBlock11; public UInt32 unknownBlock11Offset; public bool useUnknownBlock12; public UInt32 unknownBlock12Offset; public bool useUnknownBlock13; public UInt32 unknownBlock13Offset; public bool useUnknownBlock14; public UInt32 unknownBlock14Offset; public Boolean useAudioSection; public UInt32 audioSectionSize; public UInt32 audioSectionOffset; } public class ThemeIdentifier : FileFormatIdentifier { public override string GetCategory() { return "3DS Themes"; } public override string GetFileDescription() { return "System Theme body_LZ.bin"; } public override string GetFileFilter() { return "System Menu Theme (body_LZ.bin)|body_LZ.bin"; } public override Bitmap GetIcon() { return null; } public override FormatMatch IsFormat(EFEFile File) { if (File.Name.Equals("body_LZ.bin")) return FormatMatch.Content; else return FormatMatch.No; } } } }