Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/hakchi2.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/Apps
diff options
context:
space:
mode:
authorAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2017-10-05 23:42:19 +0300
committerAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2017-10-05 23:42:19 +0300
commitad222ad525a69ff39a0c7a6120eb45e6bae3ce70 (patch)
treecf91cbf0cc629809bca3ab52f1bae0ea3fd5039d /Apps
parent100852f221b8f1b83c978d29c0cd56f54d7ffab8 (diff)
Finally SNES patcher
Diffstat (limited to 'Apps')
-rw-r--r--Apps/AppTypeCollection.cs2
-rw-r--r--Apps/FdsGame.cs2
-rw-r--r--Apps/NesGame.cs81
-rw-r--r--Apps/NesMiniApplication.cs77
-rw-r--r--Apps/SnesGame.cs322
5 files changed, 363 insertions, 121 deletions
diff --git a/Apps/AppTypeCollection.cs b/Apps/AppTypeCollection.cs
index 1ad25fab..112d3031 100644
--- a/Apps/AppTypeCollection.cs
+++ b/Apps/AppTypeCollection.cs
@@ -51,7 +51,7 @@ namespace com.clusterrr.hakchi_gui
{
Class = typeof(SnesGame),
Extensions = new string[] { ".sfc", ".smc", ".sfrom" },
- DefaultApps = new string[] { "/bin/snes", "/usr/bin/clover-canoe-shvc", },
+ DefaultApps = new string[] { "/bin/snes", "/bin/clover-canoe-shvc-wr", "/usr/bin/clover-canoe-shvc" },
Prefix = 'U',
DefaultCover = Resources.blank_snes_us
},
diff --git a/Apps/FdsGame.cs b/Apps/FdsGame.cs
index 271391b1..5f862cd8 100644
--- a/Apps/FdsGame.cs
+++ b/Apps/FdsGame.cs
@@ -40,6 +40,8 @@ namespace com.clusterrr.hakchi_gui
}
if (ConfigIni.ConsoleType == MainForm.ConsoleType.NES || ConfigIni.ConsoleType == MainForm.ConsoleType.Famicom)
application = "/bin/clover-kachikachi-wr";
+ else
+ application = "/bin/nes";
args = DefaultArgs;
return true;
}
diff --git a/Apps/NesGame.cs b/Apps/NesGame.cs
index eb9e699c..6fedd280 100644
--- a/Apps/NesGame.cs
+++ b/Apps/NesGame.cs
@@ -113,85 +113,6 @@ namespace com.clusterrr.hakchi_gui
return true;
}
- /*
- public static NesMiniApplication Import(string nesFileName, string sourceFileName, bool? ignoreMapper, ref bool? needPatch, NeedPatchDelegate needPatchCallback = null, Form parentForm = null, byte[] rawRomData = null)
- {
- NesFile nesFile;
- try
- {
- if (rawRomData != null)
- nesFile = new NesFile(rawRomData);
- else
- nesFile = new NesFile(nesFileName);
- }
- catch
- {
- return NesMiniApplication.Import(nesFileName, sourceFileName, rawRomData);
- }
- nesFile.CorrectRom();
- var crc32 = nesFile.CRC32;
- var code = GenerateCode(crc32, Prefix);
- var gamePath = Path.Combine(GamesDirectory, code);
- var nesPath = Path.Combine(gamePath, code + ".nes");
- var patchesDirectory = Path.Combine(Program.BaseDirectoryExternal, "patches");
- Directory.CreateDirectory(patchesDirectory);
- Directory.CreateDirectory(gamePath);
- var patches = Directory.GetFiles(patchesDirectory, string.Format("{0:X8}*.ips", crc32), SearchOption.AllDirectories);
- if (patches.Length > 0 && needPatch != false)
- {
- if (needPatch == true || ((needPatchCallback != null) && needPatchCallback(parentForm, Path.GetFileName(nesFileName))))
- {
- needPatch = true;
- var patch = patches[0];
- if (rawRomData == null)
- rawRomData = File.ReadAllBytes(nesFileName);
- Debug.WriteLine(string.Format("Patching {0}", nesFileName));
- IpsPatcher.Patch(patch, ref rawRomData);
- nesFile = new NesFile(rawRomData);
- }
- else needPatch = false;
- }
-
- if (!supportedMappers.Contains(nesFile.Mapper) && (ignoreMapper != true))
- {
- Directory.Delete(gamePath, true);
- if (ignoreMapper != false)
- throw new UnsupportedMapperException(nesFile);
- else
- {
- Debug.WriteLine(string.Format("Game {0} has mapper #{1}, skipped", nesFileName, nesFile.Mapper));
- return null;
- }
- }
- if ((nesFile.Mirroring == NesFile.MirroringType.FourScreenVram) && (ignoreMapper != true))
- {
- Directory.Delete(gamePath, true);
- if (ignoreMapper != false)
- throw new UnsupportedFourScreenException(nesFile);
- else
- {
- Debug.WriteLine(string.Format("Game {0} has four-screen mirroring, skipped", nesFileName, nesFile.Mapper));
- return null;
- }
- }
- // TODO: Make trainer check. I think that the NES Mini doesn't support it.
-
- nesFile.Save(nesPath);
- var game = new NesGame(gamePath, true);
-
- game.Name = Path.GetFileNameWithoutExtension(nesFileName);
- if (game.Name.Contains("(J)")) game.region = "Japan";
- game.TryAutofill(crc32);
- game.Name = Regex.Replace(game.Name, @" ?\(.*?\)", string.Empty).Trim();
- game.Name = Regex.Replace(game.Name, @" ?\[.*?\]", string.Empty).Trim();
- game.Name = game.Name.Replace("_", " ").Replace(" ", " ");
- game.FindCover(nesFileName, sourceFileName, (game.region == "Japan") ? Resources.blank_jp : Resources.blank_nes, crc32);
- game.Args = DefaultArgs;
- game.Save();
- return game;
- }
- */
-
public bool TryAutofill(uint crc32)
{
CachedGameInfo gameinfo;
@@ -228,7 +149,7 @@ namespace com.clusterrr.hakchi_gui
if (!string.IsNullOrEmpty(GameGenie))
{
var codes = GameGenie.Split(new char[] { ',', '\t', ' ', ';' }, StringSplitOptions.RemoveEmptyEntries);
- var nesFiles = Directory.GetFiles(this.Path, "*.nes", SearchOption.TopDirectoryOnly);
+ var nesFiles = Directory.GetFiles(this.GamePath, "*.nes", SearchOption.TopDirectoryOnly);
foreach (var f in nesFiles)
{
var nesFile = new NesFile(f);
diff --git a/Apps/NesMiniApplication.cs b/Apps/NesMiniApplication.cs
index 437253ce..c1ee4eb2 100644
--- a/Apps/NesMiniApplication.cs
+++ b/Apps/NesMiniApplication.cs
@@ -65,7 +65,7 @@ namespace com.clusterrr.hakchi_gui
get { return "game"; }
}
- public readonly string Path;
+ public readonly string GamePath;
public readonly string ConfigPath;
public readonly string IconPath;
public readonly string SmallIconPath;
@@ -201,10 +201,18 @@ namespace com.clusterrr.hakchi_gui
FindPatch(ref rawRomData, inputFileName, crc32);
var code = GenerateCode(crc32, prefix);
- var gamePath = System.IO.Path.Combine(GamesDirectory, code);
- var romPath = System.IO.Path.Combine(gamePath, outputFileName);
+ var gamePath = Path.Combine(GamesDirectory, code);
+ var romPath = Path.Combine(gamePath, outputFileName);
if (Directory.Exists(gamePath))
- Directory.Delete(gamePath, true);
+ {
+ var files = Directory.GetFiles(gamePath, "*.*", SearchOption.AllDirectories);
+ foreach (var f in files)
+ try
+ {
+ File.Delete(f);
+ }
+ catch { }
+ }
Directory.CreateDirectory(gamePath);
File.WriteAllBytes(romPath, rawRomData);
var game = new NesMiniApplication(gamePath, true);
@@ -240,7 +248,7 @@ namespace com.clusterrr.hakchi_gui
protected NesMiniApplication()
{
- Path = null;
+ GamePath = null;
ConfigPath = null;
Players = 1;
Simultaneous = false;
@@ -251,7 +259,7 @@ namespace com.clusterrr.hakchi_gui
protected NesMiniApplication(string path, bool ignoreEmptyConfig = false)
{
- Path = path;
+ GamePath = path;
code = System.IO.Path.GetFileName(path);
Name = Code;
ConfigPath = System.IO.Path.Combine(path, Code + ".desktop");
@@ -305,27 +313,24 @@ namespace com.clusterrr.hakchi_gui
if (!hasUnsavedChanges) return false;
Debug.WriteLine(string.Format("Saving application \"{0}\" as {1}", Name, Code));
Name = Regex.Replace(Name, @"'(\d)", @"`$1"); // Apostrophe + any number in game name crashes whole system. What. The. Fuck?
- File.WriteAllText(ConfigPath, string.Format(
- "[Desktop Entry]\n" +
- "Type=Application\n" +
- "Exec={1}\n" +
- "Path=/var/lib/clover/profiles/0/{0}\n" +
- "Name={2}\n" +
- "Icon=/usr/share/games/nes/kachikachi/{0}/{0}.png\n\n" +
- "[X-CLOVER Game]\n" +
- "Code={0}\n" +
- "TestID=777\n" +
- "ID=0\n" +
- "Players={3}\n" +
- "Simultaneous={7}\n" +
- "ReleaseDate={4}\n" +
- "SaveCount=0\n" +
- "SortRawTitle={5}\n" +
- "SortRawPublisher={6}\n" +
- "Copyright=hakchi2 ©2017 Alexey 'Cluster' Avdyukhin\n",
- Code, command, Name ?? Code, Players, ReleaseDate ?? DefaultReleaseDate,
- (Name ?? Code).ToLower(), (Publisher ?? DefaultPublisher).ToUpper(),
- Simultaneous ? 1 : 0));
+ File.WriteAllText(ConfigPath,
+ $"[Desktop Entry]\n" +
+ $"Type=Application\n" +
+ $"Exec={command}\n" +
+ $"Path=/var/lib/clover/profiles/0/{Code}\n" +
+ $"Name={Name ?? Code}\n" +
+ $"Icon={GamesCloverPath}/{Code}/{Code}.png\n\n" +
+ $"[X-CLOVER Game]\n" +
+ $"Code={Code}\n" +
+ $"TestID=777\n" +
+ $"ID=0\n" +
+ $"Players={Players}\n" +
+ $"Simultaneous={(Simultaneous ? 1 : 0)}\n" +
+ $"ReleaseDate={ReleaseDate ?? DefaultReleaseDate}\n" +
+ $"SaveCount=0\n" +
+ $"SortRawTitle={(Name ?? Code).ToLower()}\n" +
+ $"SortRawPublisher={(Publisher ?? DefaultPublisher).ToUpper()}\n" +
+ $"Copyright=hakchi2 ©2017 Alexey 'Cluster' Avdyukhin\n");
hasUnsavedChanges = false;
return true;
}
@@ -481,7 +486,7 @@ namespace com.clusterrr.hakchi_gui
public NesMiniApplication CopyTo(string path)
{
var targetDir = System.IO.Path.Combine(path, code);
- DirectoryCopy(Path, targetDir, true);
+ DirectoryCopy(GamePath, targetDir, true);
return FromDirectory(targetDir);
}
@@ -528,7 +533,7 @@ namespace com.clusterrr.hakchi_gui
public long Size(string path = null)
{
if (path == null)
- path = Path;
+ path = GamePath;
long size = 0;
// Get the subdirectories for the specified directory.
DirectoryInfo dir = new DirectoryInfo(path);
@@ -600,15 +605,15 @@ namespace com.clusterrr.hakchi_gui
public string[] CompressPossible()
{
var result = new List<string>();
- var exec = Regex.Replace(Command, "['\\\"]", " ") + " ";
- var files = Directory.GetFiles(Path, "*.*", SearchOption.TopDirectoryOnly);
+ var exec = Regex.Replace(Command, "['/\\\"]", " ") + " ";
+ var files = Directory.GetFiles(GamePath, "*.*", SearchOption.TopDirectoryOnly);
foreach (var file in files)
{
if (System.IO.Path.GetExtension(file).ToLower() == ".7z")
continue;
if (System.IO.Path.GetExtension(file).ToLower() == ".zip")
continue;
- if (exec.Contains(System.IO.Path.GetFileName(file) + " "))
+ if (exec.Contains(" " + System.IO.Path.GetFileName(file) + " "))
result.Add(file);
}
return result.ToArray();
@@ -617,11 +622,11 @@ namespace com.clusterrr.hakchi_gui
public string[] DecompressPossible()
{
var result = new List<string>();
- var exec = Regex.Replace(Command, "['\\\"]", " ") + " ";
- var files = Directory.GetFiles(Path, "*.7z", SearchOption.TopDirectoryOnly);
+ var exec = Regex.Replace(Command, "['/\\\"]", " ") + " ";
+ var files = Directory.GetFiles(GamePath, "*.7z", SearchOption.TopDirectoryOnly);
foreach (var file in files)
{
- if (exec.Contains(System.IO.Path.GetFileName(file) + " "))
+ if (exec.Contains(" " + System.IO.Path.GetFileName(file) + " "))
result.Add(file);
}
return result.ToArray();
@@ -650,7 +655,7 @@ namespace com.clusterrr.hakchi_gui
using (var szExtractor = new SevenZipExtractor(filename))
{
Debug.WriteLine("Decompressing " + filename);
- szExtractor.ExtractArchive(Path);
+ szExtractor.ExtractArchive(GamePath);
foreach (var f in szExtractor.ArchiveFileNames)
Command = Command.Replace(System.IO.Path.GetFileName(filename), f);
}
diff --git a/Apps/SnesGame.cs b/Apps/SnesGame.cs
index 49e3d365..e780badd 100644
--- a/Apps/SnesGame.cs
+++ b/Apps/SnesGame.cs
@@ -1,12 +1,59 @@
#pragma warning disable 0108
using com.clusterrr.hakchi_gui.Properties;
+using System;
+using System.Collections.Generic;
using System.Drawing;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
using System.Windows.Forms;
namespace com.clusterrr.hakchi_gui
{
public class SnesGame : NesMiniApplication
{
+ const string DefaultArgs = "--volume 100 -rollback-snapshot-period 600";
+ static List<byte> SfxTypes = new List<byte>() { 0x13, 0x14, 0x15, 0x1a };
+ static List<byte> Dsp1Types = new List<byte>() { 0x03, 0x05 };
+ static List<byte> SA1Types = new List<byte>() { 0x34, 0x35 };
+ static Dictionary<string, ushort> knownPresets = new Dictionary<string, ushort>()
+ {
+ { "SUPER MARIOWORLD", 0x1011 },
+ { "F-ZERO", 0x1018 },
+ { "THE LEGEND OF ZELDA", 0x101D },
+ { "SUPER MARIO KART", 0x10BD },
+ { "Super Metroid", 0x1040 },
+ { "EARTH BOUND", 0x1070 },
+ { "Kirby's Dream Course", 0x1058 },
+ { "DONKEY KONG COUNTRY", 0x1077 },
+ { "KIRBY SUPER DELUXE", 0x109F },
+ { "Super Punch-Out!!", 0x10A9 },
+ { "MEGAMAN X", 0x1109 },
+ { "SUPER GHOULS'N GHOST", 0x1003 },
+ { "Street Fighter2 Turb", 0x1065 },
+ { "SUPER MARIO RPG", 0x109E },
+ { "Secret of MANA", 0x10B0 },
+ { "FINAL FANTASY 3", 0x10DC },
+ { "SUPER CASTLEVANIA 4", 0x1030 },
+ { "CONTRA3 THE ALIEN WA", 0x1036 },
+ { "STAR FOX", 0x123B },
+ { "YOSHI'S ISLAND", 0x123D },
+ { "STARFOX2", 0x1245 },
+ { "FINAL FIGHT", 0x100E },
+ { "DIDDY'S KONG QUEST", 0x105D },
+ { "KIRBY'S DREAM LAND 3", 0x10A2 },
+ { "BREATH OF FIRE 2", 0x1068 },
+ { "FINAL FIGHT 2", 0x10E1 },
+ { "MEGAMAN X2", 0x1117 },
+ { "FINAL FIGHT 3", 0x10E3 },
+ { "GENGHIS KHAN 2", 0x10C4 },
+ { "CASTLEVANIA DRACULA", 0x1131 },
+ { "STREET FIGHTER ALPHA", 0x10DF },
+ { "MEGAMAN 7", 0x113A },
+ { "MEGAMAN X3", 0x113D },
+ { "Breath of Fire", 0x1144 },
+ };
+
public override string GoogleSuffix
{
get
@@ -20,13 +67,280 @@ namespace com.clusterrr.hakchi_gui
{
}
- public static NesMiniApplication Import(string fileName, string sourceFile = null, byte[] rawRomData = null)
+ public static bool Patch(string inputFileName, ref byte[] rawRomData, ref char prefix, ref string application, ref string outputFileName, ref string args, ref Image cover, ref uint crc32)
{
- if (ConfigIni.ConsoleType != MainForm.ConsoleType.SNES && ConfigIni.ConsoleType != MainForm.ConsoleType.SuperFamicom)
- return null;
+ FindPatch(ref rawRomData, inputFileName, crc32);
+ if (inputFileName.Contains("(E)") || inputFileName.Contains("(J)"))
+ cover = Resources.blank_snes_eu_jp;
+ if (ConfigIni.ConsoleType == MainForm.ConsoleType.SNES || ConfigIni.ConsoleType == MainForm.ConsoleType.SuperFamicom)
+ {
+ application = "/bin/clover-canoe-shvc-wr -rom";
+ args = DefaultArgs;
+ var ext = Path.GetExtension(inputFileName);
+ if (ext.ToLower() != ".sfrom") // Need to patch for canoe
+ {
+ if ((ext.ToLower() == ".smc") && ((rawRomData.Length % 1024) != 0))
+ {
+ var stripped = new byte[rawRomData.Length - 512];
+ Array.Copy(rawRomData, 512, stripped, 0, stripped.Length);
+ rawRomData = stripped;
+ }
+ MakeSfrom(ref rawRomData);
+ outputFileName = Path.GetFileNameWithoutExtension(outputFileName) + ".sfrom";
+ }
+ }
+ else
+ {
+ application = "/bin/snes";
+ }
+
+ return true;
+ }
+
+ private static void MakeSfrom(ref byte[] rawRomData)
+ {
+ var romHeaderLoRom = SnesRomHeader.Read(rawRomData, 0x7FC0);
+ var romHeaderHiRom = SnesRomHeader.Read(rawRomData, 0xFFC0);
+ SnesRomHeader romHeader;
+ bool loRom = true;
+ bool hiRom = true;
+ if (romHeaderLoRom.GameTitle.Length == 0)
+ loRom = false;
+ foreach (char c in romHeaderLoRom.GameTitle)
+ if (c < 31 || c > 127) loRom = false;
+ if (romHeaderHiRom.GameTitle.Length == 0)
+ hiRom = false;
+ foreach (char c in romHeaderHiRom.GameTitle)
+ if (c < 31 || c > 127) hiRom = false;
+ SnesRomType romType;
+ if (loRom && !hiRom)
+ {
+ romType = SnesRomType.LoRom;
+ romHeader = romHeaderLoRom;
+ }
+ else if (!loRom && hiRom)
+ {
+ romType = SnesRomType.HiRom;
+ romHeader = romHeaderHiRom;
+ }
+ else if ((romHeaderLoRom.RomMakeup & 1) == 0)
+ {
+ romType = SnesRomType.LoRom;
+ romHeader = romHeaderLoRom;
+ }
+ else
+ {
+ romType = SnesRomType.HiRom;
+ romHeader = romHeaderHiRom;
+ }
+
+ string gameTitle = romHeader.GameTitle.Trim();
+ ushort presetId = 0; // 0x1011;
+ ushort chip = 0;
+ if (SfxTypes.Contains(romHeader.RomType)) // Super FX chip
+ chip = 0x0C;
+ if (!knownPresets.TryGetValue(gameTitle, out presetId)) // Known codes
+ {
+ if (Dsp1Types.Contains(romHeader.RomType))
+ presetId = 0x10BD; // ID from Mario Kard, DSP1
+ if (SA1Types.Contains(romHeader.RomType))
+ presetId = 0x109C; // ID from Super Mario RPG, SA1
+ }
- return null;
+ var sfromHeader1 = new SfromHeader1((uint)rawRomData.Length);
+ var sfromHeader2 = new SfromHeader2((uint)rawRomData.Length, presetId, romType, chip);
+ var sfromHeader1Raw = sfromHeader1.GetBytes();
+ var sfromHeader2Raw = sfromHeader2.GetBytes();
+ var result = new byte[sfromHeader1Raw.Length + rawRomData.Length + sfromHeader2Raw.Length];
+ Array.Copy(sfromHeader1Raw, 0, result, 0, sfromHeader1Raw.Length);
+ Array.Copy(rawRomData, 0, result, sfromHeader1Raw.Length, rawRomData.Length);
+ Array.Copy(sfromHeader2Raw, 0, result, sfromHeader1Raw.Length + rawRomData.Length, sfromHeader2Raw.Length);
+ rawRomData = result;
}
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct SnesRomHeader
+ {
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]
+ public string GameTitle;
+ [MarshalAs(UnmanagedType.U1)] // $xFD5
+ public byte RomMakeup;
+ [MarshalAs(UnmanagedType.U1)] // $xFD6
+ public byte RomType;
+ [MarshalAs(UnmanagedType.U1)] // $xFD7
+ public byte RomSize;
+ [MarshalAs(UnmanagedType.U1)] // $xFD8
+ public byte SramSize;
+ [MarshalAs(UnmanagedType.U2)] // $xFD9
+ public ushort LicenseId;
+ [MarshalAs(UnmanagedType.U1)] // $xFDB
+ public byte Version;
+ [MarshalAs(UnmanagedType.U2)] // $xFDC
+ public ushort ChecksumComplement;
+ [MarshalAs(UnmanagedType.U2)] // $xFDE
+ public ushort Checksum;
+
+ public byte[] GetBytes()
+ {
+ int size = Marshal.SizeOf(this);
+ byte[] arr = new byte[size];
+
+ IntPtr ptr = Marshal.AllocHGlobal(size);
+ Marshal.StructureToPtr(this, ptr, true);
+ Marshal.Copy(ptr, arr, 0, size);
+ Marshal.FreeHGlobal(ptr);
+ return arr;
+ }
+
+ public static SnesRomHeader Read(byte[] buffer, int pos)
+ {
+ var size = Marshal.SizeOf(typeof(SnesRomHeader));
+ IntPtr ptr = Marshal.AllocHGlobal(size);
+ Marshal.Copy(buffer, pos, ptr, size);
+ var r = (SnesRomHeader)Marshal.PtrToStructure(ptr, typeof(SnesRomHeader));
+ Marshal.FreeHGlobal(ptr);
+ return r;
+ }
+ }
+
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct SfromHeader1
+ {
+ [MarshalAs(UnmanagedType.U4)]
+ public uint Uknown1_0x00000100;
+ [MarshalAs(UnmanagedType.U4)]
+ public uint FileSize;
+ [MarshalAs(UnmanagedType.U4)]
+ public uint Uknown2_0x00000030;
+ [MarshalAs(UnmanagedType.U4)]
+ public uint RomEnd;
+ [MarshalAs(UnmanagedType.U4)]
+ public uint FooterStart;
+ [MarshalAs(UnmanagedType.U4)]
+ public uint Header2;
+ [MarshalAs(UnmanagedType.U4)]
+ public uint FileSize2;
+ [MarshalAs(UnmanagedType.U4)]
+ public uint Uknown3_0x00000000;
+ [MarshalAs(UnmanagedType.U4)]
+ public uint Flags;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
+ public byte[] VCGameID;
+ [MarshalAs(UnmanagedType.U4)]
+ public uint Uknown4_0x00000000;
+
+ public SfromHeader1(uint romSize)
+ {
+ Uknown1_0x00000100 = 0x00000100;
+ FileSize = (uint)(48 + romSize + 38);
+ Uknown2_0x00000030 = 0x00000030;
+ RomEnd = (uint)(48 + romSize);
+ FooterStart = FileSize;
+ Header2 = RomEnd;
+ FileSize2 = FileSize;
+ Uknown3_0x00000000 = 0;
+ Flags = FileSize - 11;
+ VCGameID = new byte[8];
+ var VCGameID_s = Encoding.ASCII.GetBytes("WUP-XXXX");
+ Array.Copy(VCGameID_s, VCGameID, VCGameID_s.Length);
+ Uknown4_0x00000000 = 0;
+ }
+
+ public byte[] GetBytes()
+ {
+ int size = Marshal.SizeOf(this);
+ byte[] arr = new byte[size];
+
+ IntPtr ptr = Marshal.AllocHGlobal(size);
+ Marshal.StructureToPtr(this, ptr, true);
+ Marshal.Copy(ptr, arr, 0, size);
+ Marshal.FreeHGlobal(ptr);
+ return arr;
+ }
+
+ public static SfromHeader1 Read(byte[] buffer, int pos)
+ {
+ var size = Marshal.SizeOf(typeof(SfromHeader1));
+ IntPtr ptr = Marshal.AllocHGlobal(size);
+ Marshal.Copy(buffer, pos, ptr, size);
+ var r = (SfromHeader1)Marshal.PtrToStructure(ptr, typeof(SfromHeader1));
+ Marshal.FreeHGlobal(ptr);
+ return r;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ private struct SfromHeader2
+ {
+ [MarshalAs(UnmanagedType.U1)] // 0x00
+ public byte FPS;
+ [MarshalAs(UnmanagedType.U4)] // 0x01
+ public uint RomSize;
+ [MarshalAs(UnmanagedType.U4)] // 0x05
+ public uint PcmSize;
+ [MarshalAs(UnmanagedType.U4)] // 0x09
+ public uint FooterSize;
+ [MarshalAs(UnmanagedType.U2)] // 0x0D
+ public ushort PresetID;
+ [MarshalAs(UnmanagedType.U1)] // 0x0F
+ public byte Mostly2;
+ [MarshalAs(UnmanagedType.U1)] // 0x10
+ public byte Volume;
+ [MarshalAs(UnmanagedType.U1)] // 0x11
+ public byte RomType;
+ [MarshalAs(UnmanagedType.U4)] // 0x12
+ public uint Chip;
+ [MarshalAs(UnmanagedType.U4)] // 0x16
+ public uint Unknown1_0x00000000;
+ [MarshalAs(UnmanagedType.U4)] // 0x1A
+ public uint Unknown2_0x00000100;
+ [MarshalAs(UnmanagedType.U4)] // 0x1E
+ public uint Unknown3_0x00000100;
+ [MarshalAs(UnmanagedType.U4)]
+ public uint Unknown4_0x00000000;
+
+ public SfromHeader2(uint romSize, ushort presetId, SnesRomType romType, uint chip)
+ {
+ FPS = 60;
+ RomSize = romSize;
+ PcmSize = 0;
+ FooterSize = 0;
+ PresetID = presetId;
+ Mostly2 = 2;
+ Volume = 100;
+ RomType = (byte)romType;
+ Chip = chip;
+ Unknown1_0x00000000 = 0x00000000;
+ Unknown2_0x00000100 = 0x00000100;
+ Unknown3_0x00000100 = 0x00000100;
+ Unknown4_0x00000000 = 0x00000000;
+ }
+
+ public byte[] GetBytes()
+ {
+ int size = Marshal.SizeOf(this);
+ byte[] arr = new byte[size];
+
+ IntPtr ptr = Marshal.AllocHGlobal(size);
+ Marshal.StructureToPtr(this, ptr, true);
+ Marshal.Copy(ptr, arr, 0, size);
+ Marshal.FreeHGlobal(ptr);
+ return arr;
+ }
+
+ public static SfromHeader2 Read(byte[] buffer, int pos)
+ {
+ var size = Marshal.SizeOf(typeof(SfromHeader2));
+ IntPtr ptr = Marshal.AllocHGlobal(size);
+ Marshal.Copy(buffer, pos, ptr, size);
+ var r = (SfromHeader2)Marshal.PtrToStructure(ptr, typeof(SfromHeader2));
+ Marshal.FreeHGlobal(ptr);
+ return r;
+ }
+ }
+
+ private enum SnesRomType { LoRom = 0x14, HiRom = 0x15 };
}
}