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

github.com/ClusterM/nes-containers.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2020-11-29 14:45:30 +0300
committerAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2020-11-29 14:45:30 +0300
commit08cc02895c9b4c6ee73947ce1f5a4c3dedc9713b (patch)
tree9e2ccf317b2ca19ef0489bbf7fa43eb5b0b8e5e4
parent448bda7a1feac448d04eaac8ca2997d482eae249 (diff)
More FDS stuff
-rw-r--r--FdsBlockDiskInfo.cs (renamed from FdsDiskInfoBlock.cs)110
-rw-r--r--FdsBlockFileAmount.cs45
-rw-r--r--FdsBlockFileData.cs (renamed from FdsFileDataBlock.cs)26
-rw-r--r--FdsBlockFileHeader.cs (renamed from FdsFileHeaderBlock.cs)26
-rw-r--r--FdsDiskSide.cs157
-rw-r--r--FdsFile.cs12
-rw-r--r--FdsFileAmountBlock.cs37
-rw-r--r--FdsGame.cs68
-rw-r--r--IFdsBlock.cs1
-rw-r--r--NesContainers.csproj10
10 files changed, 399 insertions, 93 deletions
diff --git a/FdsDiskInfoBlock.cs b/FdsBlockDiskInfo.cs
index 3861ecd..c208b6b 100644
--- a/FdsDiskInfoBlock.cs
+++ b/FdsBlockDiskInfo.cs
@@ -9,58 +9,87 @@ using System.Threading.Tasks;
namespace com.clusterrr.Famicom.Containers
{
[StructLayout(LayoutKind.Sequential, Size = 58, Pack = 1, CharSet = CharSet.Ansi)]
- public class FdsDiskInfoBlock : IFdsBlock
+ public class FdsBlockDiskInfo : IFdsBlock
{
- public enum DiskSizes
+ public enum DiskSides
{
- SideA = 0,
- SideB = 1,
+ A = 0,
+ B = 1,
}
public enum DiskTypes
{
FMS = 0, // Normal
FSC = 1, // With shutter
}
+ public enum Country
+ {
+ Japan = 0x49
+ }
[MarshalAs(UnmanagedType.U1)]
// Raw byte: 0x01
private byte blockType = 1;
+ /// <summary>
+ /// True if block type ID is valid
+ /// </summary>
+ public bool IsValid { get => blockType == 1; }
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)]
- // Literal ASCII string: *NINTENDO-HVC*
- char[] diskVerification;
- public string DiskVerification { get => new string(diskVerification).Trim(new char[] { '\0', ' ' }); set => diskVerification = value.PadRight(14).ToCharArray(); }
+ char[] diskVerification = "*NINTENDO-HVC*".ToCharArray();
+ /// <summary>
+ /// Literal ASCII string: *NINTENDO-HVC*
+ /// </summary>
+ public string DiskVerification { get => new string(diskVerification).Trim(new char[] { '\0', ' ' }); /*set => diskVerification = value.PadRight(14).ToCharArray(0, value.Length > 14 ? 14 : value.Length);*/ }
[MarshalAs(UnmanagedType.U1)]
private byte manufacturerCode;
+ /// <summary>
+ /// Manufacturer code. $00 = Unlicensed, $01 = Nintendo
+ /// </summary>
public byte ManufacturerCode { get => manufacturerCode; set => manufacturerCode = value; }
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
- // 3-letter ASCII code per game (e.g. ZEL for The Legend of Zelda)
char[] gameName;
- public string GameName { get => new string(gameName).Trim(new char[] { '\0', ' ' }); set => gameName = value.PadRight(3).ToCharArray(); }
+ /// <summary>
+ /// 3-letter ASCII code per game (e.g. ZEL for The Legend of Zelda)
+ /// </summary>
+ public string GameName { get => new string(gameName).Trim(new char[] { '\0', ' ' }); set => gameName = value.PadRight(3).ToCharArray(0, value.Length > 3 ? 3 : value.Length); }
[MarshalAs(UnmanagedType.U1)]
char gameType;
+ /// <summary>
+ /// $20 = " " — Normal disk
+ /// $45 = "E" — Event(e.g.Japanese national DiskFax tournaments)
+ /// $52 = "R" — Reduction in price via advertising
+ /// </summary>
public char GameType { get => gameType; set => gameType = value; }
[MarshalAs(UnmanagedType.U1)]
byte gameVersion;
+ /// <summary>
+ /// Game version/revision number. Starts at $00, increments per revision
+ /// </summary>
public byte GameVersion { get => gameVersion; set => gameVersion = value; }
[MarshalAs(UnmanagedType.U1)]
- // $00 = Side A, $01 = Side B. Single-sided disks use $00
byte diskSide;
- public DiskSizes DiskSide { get => (DiskSizes)diskSide; set => diskSide = (byte)value; }
+ /// <summary>
+ /// Side number. Single-sided disks use A
+ /// </summary>
+ public DiskSides DiskSide { get => (DiskSides)diskSide; set => diskSide = (byte)value; }
[MarshalAs(UnmanagedType.U1)]
- // First disk is $00, second is $01, etc.
byte diskNumber;
+ /// <summary>
+ /// Disk number. First disk is $00, second is $01, etc.
+ /// </summary>
public byte DiskNumber { get => diskNumber; set => diskNumber = value; }
[MarshalAs(UnmanagedType.U1)]
- // $00 = FMC ("normal card"), $01 = FSC ("card with shutter"). May correlate with FMC and FSC product codes
byte diskType;
+ /// <summary>
+ /// Disk type. $00 = FMC ("normal card"), $01 = FSC ("card with shutter"). May correlate with FMC and FSC product codes
+ /// </summary>
public DiskTypes DiskType { get => (DiskTypes)diskType; set => diskType = (byte)value; }
[MarshalAs(UnmanagedType.U1)]
@@ -68,8 +97,11 @@ namespace com.clusterrr.Famicom.Containers
// Speculative: $00 = yellow disk, $01 = blue or gold disk, $FE = white disk, $FF = blue disk
byte unknown01 = 0x00;
[MarshalAs(UnmanagedType.U1)]
- // Refers to the file code/file number to load upon boot/start-up
byte bootFile;
+ /// <summary>
+ /// Boot read file code. Refers to the file code/file number to load upon boot/start-up
+ /// </summary>
+ public byte BootFile { get => bootFile; set => bootFile = value; }
[MarshalAs(UnmanagedType.U1)]
byte unknown02 = 0xFF;
[MarshalAs(UnmanagedType.U1)]
@@ -83,6 +115,9 @@ namespace com.clusterrr.Famicom.Containers
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
byte[] manufacturingDate;
+ /// <summary>
+ /// Manufacturing date
+ /// </summary>
public DateTime ManufacturingDate
{
get
@@ -113,8 +148,11 @@ namespace com.clusterrr.Famicom.Containers
[MarshalAs(UnmanagedType.U1)]
// $49 = Japan
- byte countryCode = 0x49;
- public byte CountryCode { get => countryCode; set => countryCode = value; }
+ byte countryCode = (byte)Country.Japan;
+ /// <summary>
+ /// Country code. $49 = Japan
+ /// </summary>
+ public Country CountryCode { get => (Country)countryCode; set => countryCode = (byte)value; }
[MarshalAs(UnmanagedType.U1)]
// Raw byte: $61. Speculative: Region code?
@@ -131,9 +169,11 @@ namespace com.clusterrr.Famicom.Containers
byte unknown10 = 0x02;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
- // It's speculated this refers to the date the disk was formatted and rewritten by something like a Disk Writer kiosk.
- // In the case of an original (non-copied) disk, this should be the same as Manufacturing date.
byte[] rewrittenDate;
+ /// <summary>
+ /// "Rewritten disk" date. It's speculated this refers to the date the disk was formatted and rewritten by something like a Disk Writer kiosk.
+ /// In the case of an original (non-copied) disk, this should be the same as Manufacturing date
+ /// </summary>
public DateTime RewrittenDate
{
get
@@ -170,6 +210,9 @@ namespace com.clusterrr.Famicom.Containers
[MarshalAs(UnmanagedType.U2)]
ushort diskWriterSerialNumber;
+ /// <summary>
+ /// Disk Writer serial number
+ /// </summary>
public ushort DiskWriterSerialNumber { get => diskWriterSerialNumber; set => diskWriterSerialNumber = value; }
[MarshalAs(UnmanagedType.U1)]
@@ -177,8 +220,10 @@ namespace com.clusterrr.Famicom.Containers
byte unknown13 = 0x07;
[MarshalAs(UnmanagedType.U1)]
- // Value stored in BCD format. $00 = Original (no copies).
byte diskRewriteCount = 0x00;
+ /// <summary>
+ /// Disk rewrite count. $00 = Original (no copies)
+ /// </summary>
public byte DiskRewriteCount
{
get
@@ -192,28 +237,39 @@ namespace com.clusterrr.Famicom.Containers
}
[MarshalAs(UnmanagedType.U1)]
- // $00 = Side A, $01 = Side B
byte actualDiskSide = 0x00;
- public DiskSizes ActualDiskSide { get => (DiskSizes)actualDiskSide; set => actualDiskSide = (byte)value; }
+ /// <summary>
+ /// Actual disk side
+ /// </summary>
+ public DiskSides ActualDiskSide { get => (DiskSides)actualDiskSide; set => actualDiskSide = (byte)value; }
[MarshalAs(UnmanagedType.U1)]
byte unknown14 = 0x00;
[MarshalAs(UnmanagedType.U1)]
byte price = 0x00;
+ /// <summary>
+ /// Price code
+ /// </summary>
public byte Price { get => price; set => price = value; }
[MarshalAs(UnmanagedType.U1)]
- private bool crcOk;
+ private bool crcOk = true;
+ /// <summary>
+ /// Set by dumper. True when checksum is ok
+ /// </summary>
public bool CrcOk { get => crcOk; set => crcOk = value; }
[MarshalAs(UnmanagedType.U1)]
- private bool endOfHeadMeet;
+ private bool endOfHeadMeet = false;
+ /// <summary>
+ /// Set by dumper. True when "end of head" flag was meet during dumping
+ /// </summary>
public bool EndOfHeadMeet { get => endOfHeadMeet; set => endOfHeadMeet = value; }
- public static FdsDiskInfoBlock FromBytes(byte[] rawData, int position = 0)
+ public static FdsBlockDiskInfo FromBytes(byte[] rawData, int position = 0)
{
- int rawsize = Marshal.SizeOf(typeof(FdsDiskInfoBlock));
+ int rawsize = Marshal.SizeOf(typeof(FdsBlockDiskInfo));
if (rawsize > rawData.Length - position)
{
if (rawsize <= rawData.Length - position + 2)
@@ -230,10 +286,8 @@ namespace com.clusterrr.Famicom.Containers
}
IntPtr buffer = Marshal.AllocHGlobal(rawsize);
Marshal.Copy(rawData, position, buffer, rawsize);
- FdsDiskInfoBlock retobj = (FdsDiskInfoBlock)Marshal.PtrToStructure(buffer, typeof(FdsDiskInfoBlock));
+ FdsBlockDiskInfo retobj = (FdsBlockDiskInfo)Marshal.PtrToStructure(buffer, typeof(FdsBlockDiskInfo));
Marshal.FreeHGlobal(buffer);
- if (retobj.blockType != 1)
- throw new InvalidDataException("Invalid block type");
return retobj;
}
diff --git a/FdsBlockFileAmount.cs b/FdsBlockFileAmount.cs
new file mode 100644
index 0000000..8858151
--- /dev/null
+++ b/FdsBlockFileAmount.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace com.clusterrr.Famicom.Containers
+{
+ public class FdsBlockFileAmount : IFdsBlock
+ {
+ private byte blockType = 2;
+ /// <summary>
+ /// True if block type ID is valid
+ /// </summary>
+ public bool IsValid { get => blockType == 2; }
+
+ private byte fileAmount;
+ public byte FileAmount { get => fileAmount; set => fileAmount = value; }
+
+ /// <summary>
+ /// Set by dumper. True when checksum is ok
+ /// </summary>
+ public bool CrcOk { get; set; } = true;
+
+ /// <summary>
+ /// Set by dumper. True when "end of head" flag was meet during dumping
+ /// </summary>
+ public bool EndOfHeadMeet { get; set; } = false;
+
+ public static FdsBlockFileAmount FromBytes(byte[] rawData, int position = 0)
+ {
+ var retobj = new FdsBlockFileAmount();
+ retobj.blockType = rawData[position];
+ retobj.fileAmount = rawData[position + 1];
+ return retobj;
+ }
+
+ public byte[] ToBytes()
+ {
+ return new byte[] { blockType, fileAmount };
+ }
+ }
+}
diff --git a/FdsFileDataBlock.cs b/FdsBlockFileData.cs
index 5d54937..51345bd 100644
--- a/FdsFileDataBlock.cs
+++ b/FdsBlockFileData.cs
@@ -8,9 +8,13 @@ using System.Threading.Tasks;
namespace com.clusterrr.Famicom.Containers
{
- public class FdsFileDataBlock : IFdsBlock
+ public class FdsBlockFileData : IFdsBlock
{
private byte blockType = 4;
+ /// <summary>
+ /// True if block type ID is valid
+ /// </summary>
+ public bool IsValid { get => blockType == 4; }
private byte[] data = new byte[0];
public IEnumerable<byte> Data
@@ -19,18 +23,22 @@ namespace com.clusterrr.Famicom.Containers
set => data = value.ToArray();
}
- public bool CrcOk { get; set; }
+ /// <summary>
+ /// Set by dumper. True when checksum is ok
+ /// </summary>
+ public bool CrcOk { get; set; } = true;
- public bool EndOfHeadMeet { get; set; }
+ /// <summary>
+ /// Set by dumper. True when "end of head" flag was meet during dumping
+ /// </summary>
+ public bool EndOfHeadMeet { get; set; } = false;
- public static FdsFileDataBlock FromBytes(byte[] rawData, int position = 0, int size = -1)
+ public static FdsBlockFileData FromBytes(byte[] rawData, int position = 0, int size = -1)
{
- var retobj = new FdsFileDataBlock();
+ var retobj = new FdsBlockFileData();
retobj.blockType = rawData[position];
- if (retobj.blockType != 4)
- throw new InvalidDataException("Invalid block type");
- retobj.data = new byte[size < 0 ? rawData.Length - position : size];
- Array.Copy(rawData, position, retobj.data, 0, retobj.data.Length);
+ retobj.data = new byte[size < 0 ? rawData.Length - position - 1 : size - 1];
+ Array.Copy(rawData, position + 1, retobj.data, 0, retobj.data.Length);
return retobj;
}
diff --git a/FdsFileHeaderBlock.cs b/FdsBlockFileHeader.cs
index 0c5926e..99c301c 100644
--- a/FdsFileHeaderBlock.cs
+++ b/FdsBlockFileHeader.cs
@@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace com.clusterrr.Famicom.Containers
{
[StructLayout(LayoutKind.Sequential, Size = 18, Pack = 1, CharSet = CharSet.Ansi)]
- public class FdsFileHeaderBlock : IFdsBlock
+ public class FdsBlockFileHeader : IFdsBlock
{
public enum Kind
{
@@ -20,6 +20,10 @@ namespace com.clusterrr.Famicom.Containers
[MarshalAs(UnmanagedType.U1)]
private byte blockType = 3;
+ /// <summary>
+ /// True if block type ID is valid
+ /// </summary>
+ public bool IsValid { get => blockType == 3; }
[MarshalAs(UnmanagedType.U1)]
private byte fileNumber;
@@ -32,7 +36,7 @@ namespace com.clusterrr.Famicom.Containers
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
private char[] fileName;
- public string FileName { get => new string(fileName).Trim(new char[] { '\0', ' ' }); set => fileName = value.PadRight(0).ToCharArray(); }
+ public string FileName { get => new string(fileName).Trim(new char[] { '\0', ' ' }); set => fileName = value.PadRight(0).ToCharArray(0, value.Length > 8 ? 8 : value.Length); }
[MarshalAs(UnmanagedType.U2)]
// the destination address when loading
@@ -48,16 +52,22 @@ namespace com.clusterrr.Famicom.Containers
public Kind FileKind { get => (Kind)fileKind; set => fileKind = (byte)value; }
[MarshalAs(UnmanagedType.U1)]
- private bool crcOk;
+ private bool crcOk = true;
+ /// <summary>
+ /// Set by dumper. True when checksum is ok
+ /// </summary>
public bool CrcOk { get => crcOk; set => crcOk = value; }
[MarshalAs(UnmanagedType.U1)]
- private bool endOfHeadMeet;
+ private bool endOfHeadMeet = false;
+ /// <summary>
+ /// Set by dumper. True when "end of head" flag was meet during dumping
+ /// </summary>
public bool EndOfHeadMeet { get => endOfHeadMeet; set => endOfHeadMeet = value; }
- public static FdsFileHeaderBlock FromBytes(byte[] rawData, int position = 0)
+ public static FdsBlockFileHeader FromBytes(byte[] rawData, int position = 0)
{
- int rawsize = Marshal.SizeOf(typeof(FdsFileHeaderBlock));
+ int rawsize = Marshal.SizeOf(typeof(FdsBlockFileHeader));
if (rawsize > rawData.Length - position)
{
if (rawsize <= rawData.Length - position + 2)
@@ -74,10 +84,8 @@ namespace com.clusterrr.Famicom.Containers
}
IntPtr buffer = Marshal.AllocHGlobal(rawsize);
Marshal.Copy(rawData, position, buffer, rawsize);
- FdsFileHeaderBlock retobj = (FdsFileHeaderBlock)Marshal.PtrToStructure(buffer, typeof(FdsFileHeaderBlock));
+ FdsBlockFileHeader retobj = (FdsBlockFileHeader)Marshal.PtrToStructure(buffer, typeof(FdsBlockFileHeader));
Marshal.FreeHGlobal(buffer);
- if (retobj.blockType != 3)
- throw new InvalidDataException("Invalid block type");
return retobj;
}
diff --git a/FdsDiskSide.cs b/FdsDiskSide.cs
new file mode 100644
index 0000000..9e9dca1
--- /dev/null
+++ b/FdsDiskSide.cs
@@ -0,0 +1,157 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace com.clusterrr.Famicom.Containers
+{
+ public class FdsDiskSide
+ {
+ FdsBlockDiskInfo diskInfoBlock;
+
+ public string DiskVerification { get => diskInfoBlock.DiskVerification; }
+ /// <summary>
+ /// Manufacturer code. $00 = Unlicensed, $01 = Nintendo
+ /// </summary>
+ public byte ManufacturerCode { get => diskInfoBlock.ManufacturerCode; set => diskInfoBlock.ManufacturerCode = value; }
+ /// <summary>
+ /// 3-letter ASCII code per game (e.g. ZEL for The Legend of Zelda)
+ /// </summary>
+ public string GameName { get => diskInfoBlock.GameName; set => diskInfoBlock.GameName = value; }
+ /// <summary>
+ /// $20 = " " — Normal disk
+ /// $45 = "E" — Event(e.g.Japanese national DiskFax tournaments)
+ /// $52 = "R" — Reduction in price via advertising
+ /// </summary>
+ public char GameType { get => diskInfoBlock.GameType; set => diskInfoBlock.GameType = value; }
+ /// <summary>
+ /// Game version/revision number. Starts at $00, increments per revision
+ /// </summary>
+ public byte GameVersion { get => diskInfoBlock.GameVersion; set => diskInfoBlock.GameVersion = value; }
+ /// <summary>
+ /// Side number. Single-sided disks use A
+ /// </summary>
+ public FdsBlockDiskInfo.DiskSides DiskSide { get => diskInfoBlock.DiskSide; set => diskInfoBlock.DiskSide = value; }
+ /// <summary>
+ /// Disk number. First disk is $00, second is $01, etc.
+ /// </summary>
+ public byte DiskNumber { get => diskInfoBlock.DiskNumber; set => diskInfoBlock.DiskNumber = value; }
+ /// <summary>
+ /// Disk type. $00 = FMC ("normal card"), $01 = FSC ("card with shutter"). May correlate with FMC and FSC product codes
+ /// </summary>
+ public FdsBlockDiskInfo.DiskTypes DiskType { get => diskInfoBlock.DiskType; set => diskInfoBlock.DiskType = value; }
+ /// <summary>
+ /// Boot read file code. Refers to the file code/file number to load upon boot/start-up
+ /// </summary>
+ public byte BootFile { get => diskInfoBlock.BootFile; set => diskInfoBlock.BootFile = value; }
+ /// <summary>
+ /// Manufacturing date
+ /// </summary>
+ public DateTime ManufacturingDate { get => diskInfoBlock.ManufacturingDate; set => diskInfoBlock.ManufacturingDate = value; }
+ /// <summary>
+ /// Country code. $49 = Japan
+ /// </summary>
+ public FdsBlockDiskInfo.Country CountryCode { get => diskInfoBlock.CountryCode; set => diskInfoBlock.CountryCode = value; }
+ /// <summary>
+ /// "Rewritten disk" date. It's speculated this refers to the date the disk was formatted and rewritten by something like a Disk Writer kiosk.
+ /// In the case of an original (non-copied) disk, this should be the same as Manufacturing date
+ /// </summary>
+ public DateTime RewrittenDate { get => diskInfoBlock.RewrittenDate; set => diskInfoBlock.RewrittenDate = value; }
+ /// <summary>
+ /// Disk Writer serial number
+ /// </summary>
+ public ushort DiskWriterSerialNumber { get => diskInfoBlock.DiskWriterSerialNumber; set => diskInfoBlock.DiskWriterSerialNumber = value; }
+ /// <summary>
+ /// Disk rewrite count. $00 = Original (no copies)
+ /// </summary>
+ public byte DiskRewriteCount { get => diskInfoBlock.DiskRewriteCount; set => diskInfoBlock.DiskRewriteCount = value; }
+ /// <summary>
+ /// Actual disk side
+ /// </summary>
+ public FdsBlockDiskInfo.DiskSides ActualDiskSide { get => diskInfoBlock.ActualDiskSide; set => diskInfoBlock.ActualDiskSide = value; }
+ /// <summary>
+ /// Price code
+ /// </summary>
+ public byte Price { get => diskInfoBlock.Price; set => diskInfoBlock.Price = value; }
+
+ FdsBlockFileAmount fileAmountBlock;
+ /// <summary>
+ /// Non-hidden file amount
+ /// </summary>
+ public byte FileAmount { get => fileAmountBlock.FileAmount; set => fileAmountBlock.FileAmount = value; }
+
+ IList<FdsFile> files;
+
+ /// <summary>
+ /// Files on disk
+ /// </summary>
+ public IList<FdsFile> Files { get => files; }
+
+ public FdsDiskSide()
+ {
+ diskInfoBlock = new FdsBlockDiskInfo();
+ fileAmountBlock = new FdsBlockFileAmount();
+ files = new List<FdsFile>();
+ }
+
+ public FdsDiskSide(FdsBlockDiskInfo diskInfoBlock, FdsBlockFileAmount fileAmountBlock, IEnumerable<FdsFile> files)
+ {
+ this.diskInfoBlock = diskInfoBlock;
+ this.fileAmountBlock = fileAmountBlock;
+ this.files = files.ToList();
+ //if (this.fileAmountBlock.FileAmount > this.files.Count)
+ // throw new ArgumentOutOfRangeException("visibleFileAmount", "visibleFileAmount must be less or equal number of files");
+ }
+
+ public FdsDiskSide(IEnumerable<IFdsBlock> blocks)
+ {
+ this.diskInfoBlock = (FdsBlockDiskInfo)blocks.First();
+ this.fileAmountBlock = (FdsBlockFileAmount)blocks.Skip(1).First();
+ files = new List<FdsFile>();
+ var fileBlocks = blocks.Skip(2).ToArray();
+ for (int i = 0; i < fileBlocks.Length / 2; i++)
+ {
+ files.Add(new FdsFile((FdsBlockFileHeader)fileBlocks[i * 2], (FdsBlockFileData)fileBlocks[i * 2 + 1]));
+ }
+ }
+
+ public FdsDiskSide(byte[] data) : this()
+ {
+ int pos = 0;
+ this.diskInfoBlock = FdsBlockDiskInfo.FromBytes(data.Take(56).ToArray());
+ pos += 56;
+ this.fileAmountBlock = FdsBlockFileAmount.FromBytes(data.Skip(pos).Take(2).ToArray());
+ pos += 2;
+ while (pos < data.Length)
+ {
+ var fileHeaderBlock = FdsBlockFileHeader.FromBytes(data.Skip(pos).Take(16).ToArray());
+ if (!fileHeaderBlock.IsValid)
+ break;
+ pos += 16;
+ var fileDataBlock = FdsBlockFileData.FromBytes(data.Skip(pos).Take(fileHeaderBlock.FileSize + 1).ToArray());
+ if (!fileDataBlock.IsValid)
+ break;
+ pos += fileHeaderBlock.FileSize + 1;
+ files.Add(new FdsFile(fileHeaderBlock, fileDataBlock));
+ }
+ }
+
+ public void FixFileNumbers()
+ {
+ for (var i = 0; i < files.Count; i++)
+ files[i].FileNumber = (byte)i;
+ }
+
+ public static FdsDiskSide FromBytes(byte[] data)
+ {
+ return new FdsDiskSide(data);
+ }
+
+ public byte[] ToBytes()
+ {
+ var data = Enumerable.Concat(Enumerable.Concat(diskInfoBlock.ToBytes(), fileAmountBlock.ToBytes()), files.SelectMany(f => f.ToBytes())).ToArray();
+ return Enumerable.Concat(data, new byte[65500 - data.Count()]).ToArray();
+ }
+ }
+}
diff --git a/FdsFile.cs b/FdsFile.cs
index ada4a2d..127e31e 100644
--- a/FdsFile.cs
+++ b/FdsFile.cs
@@ -8,15 +8,15 @@ namespace com.clusterrr.Famicom.Containers
{
public class FdsFile
{
- private FdsFileHeaderBlock headerBlock;
- private FdsFileDataBlock dataBlock;
+ private FdsBlockFileHeader headerBlock;
+ private FdsBlockFileData dataBlock;
public byte FileNumber { get => headerBlock.FileNumber; set => headerBlock.FileNumber = value; }
public byte FileIndicateCode { get => headerBlock.FileIndicateCode; set => headerBlock.FileIndicateCode = value; }
public string FileName { get => headerBlock.FileName; set => headerBlock.FileName = value; }
public ushort FileAddress { get => headerBlock.FileAddress; set => headerBlock.FileAddress = value; }
public ushort FileSize { get => (ushort)dataBlock.Data.Count(); }
- public FdsFileHeaderBlock.Kind FileKind { get => headerBlock.FileKind; set => headerBlock.FileKind = value; }
+ public FdsBlockFileHeader.Kind FileKind { get => headerBlock.FileKind; set => headerBlock.FileKind = value; }
public IEnumerable<byte> Data
{
get => dataBlock.Data;
@@ -27,7 +27,7 @@ namespace com.clusterrr.Famicom.Containers
}
}
- public FdsFile(FdsFileHeaderBlock headerBlock, FdsFileDataBlock dataBlock)
+ public FdsFile(FdsBlockFileHeader headerBlock, FdsBlockFileData dataBlock)
{
this.headerBlock = headerBlock;
this.dataBlock = dataBlock;
@@ -36,8 +36,8 @@ namespace com.clusterrr.Famicom.Containers
public FdsFile()
{
- this.headerBlock = new FdsFileHeaderBlock();
- this.dataBlock = new FdsFileDataBlock();
+ this.headerBlock = new FdsBlockFileHeader();
+ this.dataBlock = new FdsBlockFileData();
headerBlock.FileSize = (ushort)dataBlock.Data.Count();
}
diff --git a/FdsFileAmountBlock.cs b/FdsFileAmountBlock.cs
deleted file mode 100644
index a428f1d..0000000
--- a/FdsFileAmountBlock.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace com.clusterrr.Famicom.Containers
-{
- public class FdsFileAmountBlock : IFdsBlock
- {
- private byte blockType = 2;
-
- private byte fileAmount;
- public byte FileAmount { get => fileAmount; set => value = fileAmount; }
-
- public bool CrcOk { get; set; }
-
- public bool EndOfHeadMeet { get; set; }
-
- public static FdsFileAmountBlock FromBytes(byte[] rawData, int position = 0)
- {
- var retobj = new FdsFileAmountBlock();
- retobj.blockType = rawData[position];
- if (retobj.blockType != 2)
- throw new InvalidDataException("Invalid block type");
- retobj.fileAmount = rawData[position + 1];
- return retobj;
- }
-
- public byte[] ToBytes()
- {
- return new byte[] { blockType, fileAmount };
- }
- }
-}
diff --git a/FdsGame.cs b/FdsGame.cs
new file mode 100644
index 0000000..3396494
--- /dev/null
+++ b/FdsGame.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace com.clusterrr.Famicom.Containers
+{
+ public class FdsGame
+ {
+ IList<FdsDiskSide> sides;
+ /// <summary>
+ /// Disk Side Images
+ /// </summary>
+ public IList<FdsDiskSide> Sides { get => sides; set => sides = value; }
+
+ public FdsGame()
+ {
+ sides = new List<FdsDiskSide>();
+ }
+
+ public FdsGame(string filename) : this(File.ReadAllBytes(filename))
+ {
+ }
+
+ public FdsGame(byte[] data) : this()
+ {
+ if (data[0] == (byte)'F' && data[1] == (byte)'D' && data[2] == (byte)'S')
+ data = data.Skip(16).ToArray(); // skip header
+ for (int i = 0; i < data.Length; i += 65500)
+ {
+ var sideData = data.Skip(i).Take(66500).ToArray();
+ sides.Add(FdsDiskSide.FromBytes(sideData));
+ }
+ }
+
+ public FdsGame(IEnumerable<FdsDiskSide> sides)
+ {
+ this.sides = new List<FdsDiskSide>(sides);
+ }
+
+ public static FdsGame FromBytes(byte[] data)
+ {
+ return new FdsGame(data);
+ }
+
+ public byte[] ToBytes(bool useHeader = false)
+ {
+ var data = sides.SelectMany(s => s.ToBytes());
+ if (useHeader)
+ {
+ var header = new byte[16];
+ header[0] = (byte)'F';
+ header[1] = (byte)'D';
+ header[2] = (byte)'S';
+ header[3] = (byte)sides.Count();
+ data = Enumerable.Concat(header, data);
+ }
+ return data.ToArray();
+ }
+
+ public void Save(string filename, bool useHeader = false)
+ {
+ File.WriteAllBytes(filename, ToBytes(useHeader));
+ }
+ }
+}
diff --git a/IFdsBlock.cs b/IFdsBlock.cs
index 08143d3..e8ec78d 100644
--- a/IFdsBlock.cs
+++ b/IFdsBlock.cs
@@ -9,6 +9,7 @@ namespace com.clusterrr.Famicom.Containers
{
public interface IFdsBlock
{
+ bool IsValid { get; }
bool CrcOk { get; set; }
bool EndOfHeadMeet { get; set; }
byte[] ToBytes();
diff --git a/NesContainers.csproj b/NesContainers.csproj
index 3cb3ff4..089048c 100644
--- a/NesContainers.csproj
+++ b/NesContainers.csproj
@@ -44,11 +44,13 @@
<ItemGroup>
<Compile Include="Crc32Calculator.cs" />
<Compile Include="FdsFile.cs" />
+ <Compile Include="FdsDiskSide.cs" />
+ <Compile Include="FdsGame.cs" />
<Compile Include="IFdsBlock.cs" />
- <Compile Include="FdsDiskInfoBlock.cs" />
- <Compile Include="FdsFileAmountBlock.cs" />
- <Compile Include="FdsFileDataBlock.cs" />
- <Compile Include="FdsFileHeaderBlock.cs" />
+ <Compile Include="FdsBlockDiskInfo.cs" />
+ <Compile Include="FdsBlockFileAmount.cs" />
+ <Compile Include="FdsBlockFileData.cs" />
+ <Compile Include="FdsBlockFileHeader.cs" />
<Compile Include="NesFile.cs" />
<Compile Include="NesHeaderFixer.cs" />
<Compile Include="UnifFile.cs" />