mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-09-22 14:55:11 +00:00
Add untested FDS <-> QD conversion
This commit is contained in:
84
SabreTools.Serialization/CrossModel/FDS.Deserializer.cs
Normal file
84
SabreTools.Serialization/CrossModel/FDS.Deserializer.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Data.Models.NES;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
namespace SabreTools.Serialization.CrossModel
|
||||
{
|
||||
/// <see href="https://gist.github.com/einstein95/6545066905680466cdf200c4cc8ca4f0"/>
|
||||
public partial class FamicomDiskSystem : ICrossModel<FDS, QD>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Debug { get; set; } = false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public FDS? Deserialize(QD? obj)
|
||||
{
|
||||
// Ignore invalid data
|
||||
if (obj?.Data is null || obj.Data.Length == 0)
|
||||
return null;
|
||||
|
||||
// Ignore incomplete data
|
||||
if ((obj.Data.Length % 65536) != 0)
|
||||
return null;
|
||||
|
||||
// Determine the number of sides
|
||||
byte sides = (byte)(obj.Data.Length / 65536);
|
||||
if (sides < 1)
|
||||
return null;
|
||||
|
||||
// Create the FDS for output
|
||||
var fds = new FDS
|
||||
{
|
||||
Header = new FDSHeader
|
||||
{
|
||||
IdentificationString = Constants.FDSSignatureBytes,
|
||||
DiskSides = sides,
|
||||
}
|
||||
};
|
||||
|
||||
// Loop over the sides and convert
|
||||
List<byte> converted = [];
|
||||
for (int i = 0; i < sides; i++)
|
||||
{
|
||||
// Convert the data to manipulate
|
||||
List<byte> data = [.. obj.Data];
|
||||
|
||||
// Delete block 01 checksum
|
||||
data.RemoveRange(0x38, 2);
|
||||
|
||||
// Delete block 02 checksum
|
||||
int pos = 0x3A;
|
||||
data.RemoveRange(pos, 2);
|
||||
|
||||
// Loop while there are more files
|
||||
while (data[pos] == 3)
|
||||
{
|
||||
// Get the filesize
|
||||
ushort filesize = (ushort)((data[pos + 0x0E] << 8) | data[pos + 0x0D]);
|
||||
|
||||
// Delete block 03 checksum
|
||||
data.RemoveRange(pos + 0x10, 2);
|
||||
|
||||
// Skip the file data
|
||||
pos = pos + 0x10 + 1 + filesize;
|
||||
|
||||
// Delete block 04 checksum
|
||||
data.RemoveRange(pos, 2);
|
||||
}
|
||||
|
||||
// Ensure the data is correctly sized
|
||||
if (data.Count > 65500)
|
||||
data = data.GetRange(0, 65500);
|
||||
else if (data.Count < 65500)
|
||||
data.AddRange(new byte[65500 - data.Count]);
|
||||
|
||||
// Add the data to the output
|
||||
converted.AddRange(data);
|
||||
}
|
||||
|
||||
// Assign the converted data and return
|
||||
fds.Data = [.. converted];
|
||||
return fds;
|
||||
}
|
||||
}
|
||||
}
|
||||
131
SabreTools.Serialization/CrossModel/FDS.Serializer.cs
Normal file
131
SabreTools.Serialization/CrossModel/FDS.Serializer.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Data.Models.NES;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
namespace SabreTools.Serialization.CrossModel
|
||||
{
|
||||
/// <see href="https://gist.github.com/einstein95/6545066905680466cdf200c4cc8ca4f0"/>
|
||||
public partial class FamicomDiskSystem : ICrossModel<FDS, QD>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public QD? Serialize(FDS? obj)
|
||||
=> Serialize(obj, false);
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <param name="nul">True to insert null checksums on conversion, false otherwise</param>
|
||||
public QD? Serialize(FDS? obj, bool nul)
|
||||
{
|
||||
// Ignore invalid data
|
||||
if (obj?.Data is null || obj.Data.Length == 0)
|
||||
return null;
|
||||
|
||||
// Ignore incomplete data
|
||||
if ((obj.Data.Length % 65500) != 0)
|
||||
return null;
|
||||
|
||||
// Determine the number of sides
|
||||
byte sides = obj.Header != null
|
||||
? obj.Header.DiskSides
|
||||
: (byte)(obj.Data.Length / 65500);
|
||||
if (sides < 1)
|
||||
return null;
|
||||
|
||||
// Create the QD for output
|
||||
var qd = new QD();
|
||||
|
||||
// Loop over the sides and convert
|
||||
List<byte> converted = [];
|
||||
for (int i = 0; i < sides; i++)
|
||||
{
|
||||
// Convert the data to manipulate
|
||||
List<byte> data = [.. obj.Data];
|
||||
|
||||
// If the data is unexpected
|
||||
if (data[0] != 0x01)
|
||||
break;
|
||||
|
||||
// Insert block 01 checksum
|
||||
InsertCrc(data, 0, 0x38, nul);
|
||||
|
||||
// Insert block 02 checksum
|
||||
int pos = 0x3A;
|
||||
InsertCrc(data, pos, pos + 2, nul);
|
||||
|
||||
// Loop while there are more files
|
||||
pos = 0x3E;
|
||||
while (data[pos] == 3)
|
||||
{
|
||||
// Get the filesize
|
||||
ushort filesize = (ushort)((data[pos + 0x0E] << 8) | data[pos + 0x0D]);
|
||||
|
||||
// Insert block 03 checksum
|
||||
InsertCrc(data, pos, pos + 0x10, nul);
|
||||
|
||||
// Skip the file data
|
||||
pos += 0x10 + 2;
|
||||
|
||||
// Insert block 04 checksum
|
||||
InsertCrc(data, pos, pos + 1 + filesize, nul);
|
||||
pos += 1 + filesize + 2;
|
||||
}
|
||||
|
||||
// Ensure the data is correctly sized
|
||||
if (data.Count > 65536)
|
||||
data = data.GetRange(0, 65536);
|
||||
else if (data.Count < 65536)
|
||||
data.AddRange(new byte[65536 - data.Count]);
|
||||
|
||||
// Add the data to the output
|
||||
converted.AddRange(data);
|
||||
}
|
||||
|
||||
// Assign the converted data and return
|
||||
qd.Data = [.. converted];
|
||||
return qd;
|
||||
}
|
||||
|
||||
private void InsertCrc(List<byte> data, int start, int end, bool nul)
|
||||
{
|
||||
if (!nul)
|
||||
{
|
||||
byte[] temp = new byte[end - start];
|
||||
data.CopyTo(start, temp, 0, temp.Length);
|
||||
ushort crc = FdsCrc(temp);
|
||||
data.Insert(end + 0, (byte)(crc & 0xFF));
|
||||
data.Insert(end + 1, (byte)(crc >> 0x08));
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Insert(end + 0, 0);
|
||||
data.Insert(end + 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace with CRC-16/KERMIT from Hashing
|
||||
private ushort FdsCrc(byte[] data)
|
||||
{
|
||||
// http://forums.nesdev.com/viewtopic.php?p=194867
|
||||
// Do not include any existing checksum, not even the blank checksums 00 00 or FF FF.
|
||||
// The formula will automatically count 2 0x00 bytes without the programmer adding them manually.
|
||||
// Also, do not include the gap terminator (0x80) in the data.
|
||||
// If you wish to do so, change sum to 0x0000.
|
||||
ushort s = 0x8000;
|
||||
byte[] padded = [.. data, 0x00, 0x00];
|
||||
foreach (byte b in padded)
|
||||
{
|
||||
s |= (ushort)(b << 16);
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((s & 1) != 0)
|
||||
{
|
||||
unchecked { s ^= (ushort)(0x8408 << 1); }
|
||||
}
|
||||
|
||||
s >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user