mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-02-04 05:36:12 +00:00
Create a new DataSource helper class
This commit is contained in:
@@ -132,7 +132,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
int compressedSize = file.CompressedSize;
|
||||
|
||||
// Some files can lack the length prefix
|
||||
if (compressedSize > GetEndOfFile())
|
||||
if (compressedSize > GetEndOffset())
|
||||
{
|
||||
offset -= 4;
|
||||
compressedSize = file.UncompressedSize;
|
||||
|
||||
@@ -314,7 +314,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
// Try to get the sector data offset
|
||||
int sectorDataOffset = (int)FATSectorToFileOffset(sectorChain[i]);
|
||||
if (sectorDataOffset < 0 || sectorDataOffset >= GetEndOfFile())
|
||||
if (sectorDataOffset < 0 || sectorDataOffset >= GetEndOffset())
|
||||
return null;
|
||||
|
||||
// Try to read the sector data
|
||||
|
||||
203
SabreTools.Serialization/Wrappers/DataSource.cs
Normal file
203
SabreTools.Serialization/Wrappers/DataSource.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Extensions;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the data source backing the wrapper
|
||||
/// </summary>
|
||||
public class DataSource
|
||||
{
|
||||
#region Instance Variables
|
||||
|
||||
/// <summary>
|
||||
/// Source of the original data
|
||||
/// </summary>
|
||||
private readonly DataSourceType _dataSourceType = DataSourceType.UNKNOWN;
|
||||
|
||||
/// <summary>
|
||||
/// Lock object for reading from the source
|
||||
/// </summary>
|
||||
private readonly object _streamDataLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initial position of the data source
|
||||
/// </summary>
|
||||
/// <remarks>Populated for both <see cref="DataSourceType.ByteArray"/> and <see cref="DataSourceType.Stream"/></remarks>
|
||||
protected long _initialPosition = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Source byte array data
|
||||
/// </summary>
|
||||
/// <remarks>This is only populated if <see cref="_dataSourceType"/> is <see cref="DataSourceType.ByteArray"/></remarks>
|
||||
protected byte[]? _byteArrayData = null;
|
||||
|
||||
/// <summary>
|
||||
/// Source Stream data
|
||||
/// </summary>
|
||||
/// <remarks>This is only populated if <see cref="_dataSourceType"/> is <see cref="DataSourceType.Stream"/></remarks>
|
||||
protected Stream? _streamData = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new DataSource from a Stream
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public DataSource(Stream data)
|
||||
{
|
||||
_dataSourceType = DataSourceType.Stream;
|
||||
_initialPosition = data.Position;
|
||||
_streamData = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new DataSource from a byte array
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="offset"></param>
|
||||
public DataSource(byte[] data, int offset)
|
||||
{
|
||||
_dataSourceType = DataSourceType.ByteArray;
|
||||
_initialPosition = offset;
|
||||
_byteArrayData = data;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data
|
||||
|
||||
/// <summary>
|
||||
/// Get the ending offset of the source
|
||||
/// </summary>
|
||||
/// <returns>Value greater than 0 for a valid end of file, -1 on error</returns>
|
||||
public long GetEndOffset()
|
||||
{
|
||||
// Validate the data souece
|
||||
if (!IsValid())
|
||||
return -1;
|
||||
|
||||
// Return the effective endpoint
|
||||
return _dataSourceType switch
|
||||
{
|
||||
DataSourceType.ByteArray => _byteArrayData!.Length - _initialPosition,
|
||||
DataSourceType.Stream => _streamData!.Length - _initialPosition,
|
||||
_ => -1,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the usable length of the underlying data
|
||||
/// </summary>
|
||||
/// <returns>The usable length on success, -1 on error</returns>
|
||||
public long GetLength()
|
||||
{
|
||||
return _dataSourceType switch
|
||||
{
|
||||
DataSourceType.ByteArray => _byteArrayData!.Length - _initialPosition,
|
||||
DataSourceType.Stream => _streamData!.Length - _initialPosition,
|
||||
|
||||
// Everything else is invalid
|
||||
_ => -1,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate the backing data source
|
||||
/// </summary>
|
||||
/// <returns>True if the data source is valid, false otherwise</returns>
|
||||
public bool IsValid()
|
||||
{
|
||||
return _dataSourceType switch
|
||||
{
|
||||
// Byte array data requires both a valid array and offset
|
||||
DataSourceType.ByteArray => _byteArrayData != null && _initialPosition >= 0,
|
||||
|
||||
// Stream data requires both a valid stream
|
||||
DataSourceType.Stream => _streamData != null && _initialPosition >= 0 && _streamData.CanRead && _streamData.CanSeek,
|
||||
|
||||
// Everything else is invalid
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a data segment is valid in the data source
|
||||
/// </summary>
|
||||
/// <param name="position">Position in the source</param>
|
||||
/// <param name="length">Length of the data to check</param>
|
||||
/// <returns>True if the positional data is valid, false otherwise</returns>
|
||||
public bool SegmentValid(int position, int length)
|
||||
{
|
||||
// Validate the data souece
|
||||
if (!IsValid())
|
||||
return false;
|
||||
|
||||
// If we have an invalid position
|
||||
if (position < 0 || position >= GetEndOffset())
|
||||
return false;
|
||||
|
||||
return _dataSourceType switch
|
||||
{
|
||||
DataSourceType.ByteArray => _initialPosition + position + length <= _byteArrayData!.Length,
|
||||
DataSourceType.Stream => _initialPosition + position + length <= _streamData!.Length,
|
||||
|
||||
// Everything else is invalid
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read data from the source
|
||||
/// </summary>
|
||||
/// <param name="position">Position in the source to read from</param>
|
||||
/// <param name="length">Length of the requested data</param>
|
||||
/// <returns>Byte array containing the requested data, null on error</returns>
|
||||
public byte[]? Read(int position, int length)
|
||||
{
|
||||
// Validate the data source
|
||||
if (!IsValid())
|
||||
return null;
|
||||
|
||||
// Validate the requested segment
|
||||
if (!SegmentValid(position, length))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Read and return the data
|
||||
byte[]? sectionData = null;
|
||||
switch (_dataSourceType)
|
||||
{
|
||||
case DataSourceType.ByteArray:
|
||||
sectionData = new byte[length];
|
||||
Array.Copy(_byteArrayData!, _initialPosition + position, sectionData, 0, length);
|
||||
break;
|
||||
|
||||
case DataSourceType.Stream:
|
||||
lock (_streamDataLock)
|
||||
{
|
||||
long currentLocation = _streamData!.Position;
|
||||
_streamData.Seek(_initialPosition + position, SeekOrigin.Begin);
|
||||
sectionData = _streamData.ReadBytes(length);
|
||||
_streamData.Seek(currentLocation, SeekOrigin.Begin);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sectionData;
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Absorb the error
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// <summary>
|
||||
/// Location that the data originated from
|
||||
/// </summary>
|
||||
public enum DataSource
|
||||
public enum DataSourceType
|
||||
{
|
||||
/// <summary>
|
||||
/// Unknown origin / testing
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return -1;
|
||||
|
||||
// Get the end of the file, if possible
|
||||
long endOfFile = GetEndOfFile();
|
||||
long endOfFile = GetEndOffset();
|
||||
if (endOfFile == -1)
|
||||
return -1;
|
||||
|
||||
@@ -268,7 +268,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return -1;
|
||||
|
||||
// Get the end of the file, if possible
|
||||
long endOfFile = GetEndOfFile();
|
||||
long endOfFile = GetEndOffset();
|
||||
if (endOfFile == -1)
|
||||
return -1;
|
||||
|
||||
@@ -309,7 +309,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
// If we have an unset length, read the whole source
|
||||
if (length == -1)
|
||||
length = GetEndOfFile();
|
||||
length = GetEndOffset();
|
||||
|
||||
return ReadFromDataSource(rangeStart, (int)length);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return _overlayAddress.Value;
|
||||
|
||||
// Get the end of the file, if possible
|
||||
long endOfFile = GetEndOfFile();
|
||||
long endOfFile = GetEndOffset();
|
||||
if (endOfFile == -1)
|
||||
return -1;
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return _overlayData;
|
||||
|
||||
// Get the end of the file, if possible
|
||||
long endOfFile = GetEndOfFile();
|
||||
long endOfFile = GetEndOffset();
|
||||
if (endOfFile == -1)
|
||||
return null;
|
||||
|
||||
@@ -190,7 +190,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return _overlayStrings;
|
||||
|
||||
// Get the end of the file, if possible
|
||||
long endOfFile = GetEndOfFile();
|
||||
long endOfFile = GetEndOffset();
|
||||
if (endOfFile == -1)
|
||||
return null;
|
||||
|
||||
@@ -396,7 +396,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return null;
|
||||
|
||||
// Get the end of the file, if possible
|
||||
long endOfFile = GetEndOfFile();
|
||||
long endOfFile = GetEndOffset();
|
||||
if (endOfFile == -1)
|
||||
return null;
|
||||
|
||||
@@ -474,7 +474,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
public int GetResourceOffset(int id)
|
||||
{
|
||||
// Get the end of the file, if possible
|
||||
long endOfFile = GetEndOfFile();
|
||||
long endOfFile = GetEndOffset();
|
||||
if (endOfFile == -1)
|
||||
return -1;
|
||||
|
||||
@@ -570,7 +570,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return -1;
|
||||
|
||||
// Get the end of the file, if possible
|
||||
long endOfFile = GetEndOfFile();
|
||||
long endOfFile = GetEndOffset();
|
||||
if (endOfFile == -1)
|
||||
return -1;
|
||||
|
||||
@@ -607,7 +607,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
// If we have an unset length, read the whole source
|
||||
if (length == -1)
|
||||
length = GetEndOfFile();
|
||||
length = GetEndOffset();
|
||||
|
||||
return ReadFromDataSource(rangeStart, (int)length);
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return _overlayAddress.Value;
|
||||
|
||||
// Get the end of the file, if possible
|
||||
long endOfFile = GetEndOfFile();
|
||||
long endOfFile = GetEndOffset();
|
||||
if (endOfFile == -1)
|
||||
return -1;
|
||||
|
||||
@@ -279,7 +279,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return _overlayData;
|
||||
|
||||
// Get the end of the file, if possible
|
||||
long endOfFile = GetEndOfFile();
|
||||
long endOfFile = GetEndOffset();
|
||||
if (endOfFile == -1)
|
||||
return null;
|
||||
|
||||
@@ -357,7 +357,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return _overlayStrings;
|
||||
|
||||
// Get the end of the file, if possible
|
||||
long endOfFile = GetEndOfFile();
|
||||
long endOfFile = GetEndOffset();
|
||||
if (endOfFile == -1)
|
||||
return null;
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
// Read the entire compressed data
|
||||
int compressedDataOffset = (int)CompressedDataOffset;
|
||||
long compressedDataLength = GetEndOfFile() - compressedDataOffset;
|
||||
long compressedDataLength = GetEndOffset() - compressedDataOffset;
|
||||
var compressedData = ReadFromDataSource(compressedDataOffset, (int)compressedDataLength);
|
||||
|
||||
// Print a debug reminder
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
@@ -22,20 +21,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// <summary>
|
||||
/// Length of the underlying data
|
||||
/// </summary>
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _dataSource switch
|
||||
{
|
||||
DataSource.ByteArray => _byteArrayData!.Length - _initialPosition,
|
||||
DataSource.Stream => _streamData!.Length - _initialPosition,
|
||||
|
||||
// Everything else is invalid
|
||||
_ => -1,
|
||||
};
|
||||
}
|
||||
}
|
||||
public long Length => _dataSource.GetLength();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -44,30 +30,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// <summary>
|
||||
/// Source of the original data
|
||||
/// </summary>
|
||||
protected DataSource _dataSource = DataSource.UNKNOWN;
|
||||
|
||||
/// <summary>
|
||||
/// Lock object for reading from the source
|
||||
/// </summary>
|
||||
private readonly object _streamDataLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initial position of the data source
|
||||
/// </summary>
|
||||
/// <remarks>Populated for both <see cref="DataSource.ByteArray"/> and <see cref="DataSource.Stream"/></remarks>
|
||||
protected long _initialPosition = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Source byte array data
|
||||
/// </summary>
|
||||
/// <remarks>This is only populated if <see cref="_dataSource"/> is <see cref="DataSource.ByteArray"/></remarks>
|
||||
protected byte[]? _byteArrayData = null;
|
||||
|
||||
/// <summary>
|
||||
/// Source Stream data
|
||||
/// </summary>
|
||||
/// <remarks>This is only populated if <see cref="_dataSource"/> is <see cref="DataSource.Stream"/></remarks>
|
||||
protected Stream? _streamData = null;
|
||||
private readonly DataSource _dataSource;
|
||||
|
||||
#if NETCOREAPP
|
||||
/// <summary>
|
||||
@@ -107,9 +70,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
|
||||
Model = model;
|
||||
_dataSource = DataSource.ByteArray;
|
||||
_initialPosition = offset;
|
||||
_byteArrayData = data;
|
||||
_dataSource = new DataSource(data, offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -125,9 +86,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
throw new ArgumentOutOfRangeException(nameof(data));
|
||||
|
||||
Model = model;
|
||||
_dataSource = DataSource.Stream;
|
||||
_initialPosition = data.Position;
|
||||
_streamData = data;
|
||||
_dataSource = new DataSource(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -135,49 +94,10 @@ namespace SabreTools.Serialization.Wrappers
|
||||
#region Data
|
||||
|
||||
/// <summary>
|
||||
/// Validate the backing data source
|
||||
/// Get the ending offset of the source
|
||||
/// </summary>
|
||||
/// <returns>True if the data source is valid, false otherwise</returns>
|
||||
public bool DataSourceIsValid()
|
||||
{
|
||||
return _dataSource switch
|
||||
{
|
||||
// Byte array data requires both a valid array and offset
|
||||
DataSource.ByteArray => _byteArrayData != null && _initialPosition >= 0,
|
||||
|
||||
// Stream data requires both a valid stream
|
||||
DataSource.Stream => _streamData != null && _initialPosition >= 0 && _streamData.CanRead && _streamData.CanSeek,
|
||||
|
||||
// Everything else is invalid
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a data segment is valid in the data source
|
||||
/// </summary>
|
||||
/// <param name="position">Position in the source</param>
|
||||
/// <param name="length">Length of the data to check</param>
|
||||
/// <returns>True if the positional data is valid, false otherwise</returns>
|
||||
public bool SegmentValid(int position, int length)
|
||||
{
|
||||
// Validate the data souece
|
||||
if (!DataSourceIsValid())
|
||||
return false;
|
||||
|
||||
// If we have an invalid position
|
||||
if (position < 0 || position >= GetEndOfFile())
|
||||
return false;
|
||||
|
||||
return _dataSource switch
|
||||
{
|
||||
DataSource.ByteArray => _initialPosition + position + length <= _byteArrayData!.Length,
|
||||
DataSource.Stream => _initialPosition + position + length <= _streamData!.Length,
|
||||
|
||||
// Everything else is invalid
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
/// <returns>Value greater than 0 for a valid end of file, -1 on error</returns>
|
||||
public long GetEndOffset() => _dataSource.GetEndOffset();
|
||||
|
||||
/// <summary>
|
||||
/// Read data from the source
|
||||
@@ -186,46 +106,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// <param name="length">Length of the requested data</param>
|
||||
/// <returns>Byte array containing the requested data, null on error</returns>
|
||||
public byte[]? ReadFromDataSource(int position, int length)
|
||||
{
|
||||
// Validate the data source
|
||||
if (!DataSourceIsValid())
|
||||
return null;
|
||||
|
||||
// Validate the requested segment
|
||||
if (!SegmentValid(position, length))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Read and return the data
|
||||
byte[]? sectionData = null;
|
||||
switch (_dataSource)
|
||||
{
|
||||
case DataSource.ByteArray:
|
||||
sectionData = new byte[length];
|
||||
Array.Copy(_byteArrayData!, _initialPosition + position, sectionData, 0, length);
|
||||
break;
|
||||
|
||||
case DataSource.Stream:
|
||||
lock (_streamDataLock)
|
||||
{
|
||||
long currentLocation = _streamData!.Position;
|
||||
_streamData.Seek(_initialPosition + position, SeekOrigin.Begin);
|
||||
sectionData = _streamData.ReadBytes(length);
|
||||
_streamData.Seek(currentLocation, SeekOrigin.Begin);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sectionData;
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Absorb the error
|
||||
return null;
|
||||
}
|
||||
}
|
||||
=> _dataSource.Read(position, length);
|
||||
|
||||
/// <summary>
|
||||
/// Read string data from the source
|
||||
@@ -260,25 +141,6 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return sourceStrings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the ending offset of the source
|
||||
/// </summary>
|
||||
/// <returns>Value greater than 0 for a valid end of file, -1 on error</returns>
|
||||
public long GetEndOfFile()
|
||||
{
|
||||
// Validate the data souece
|
||||
if (!DataSourceIsValid())
|
||||
return -1;
|
||||
|
||||
// Return the effective endpoint
|
||||
return _dataSource switch
|
||||
{
|
||||
DataSource.ByteArray => _byteArrayData!.Length - _initialPosition,
|
||||
DataSource.Stream => _streamData!.Length - _initialPosition,
|
||||
_ => -1,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read string data from the source with an encoding
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user