Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions SabreTools.Data.Models/Steam2Installer/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace SabreTools.Data.Models.Steam2Installer
{
public static class Constants
{
public static readonly byte[] SimSignatureBytes = [0x1F, 0x4C, 0xD0, 0x3F];

/// <remarks>All other values in the structure are little endian, assuming this is too</remarks>
public const uint SimSignatureUInt32 = 0x3FD04C1F;
}
}
71 changes: 71 additions & 0 deletions SabreTools.Data.Models/Steam2Installer/FileEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System.Runtime.InteropServices;

namespace SabreTools.Data.Models.Steam2Installer
{

public sealed class FileEntry
{
/// <summary>
/// Offset of the filename in the string bytes array
/// </summary>
public uint NameOffset { get; set; }

/// <summary>
/// Offset of the path name in the string bytes array
/// </summary>
public uint PathOffset { get; set; }

/// <summary>
/// Steam depot id of the depot the file is a part of
/// </summary>
public uint DepotId { get; set; }

/// <summary>
/// Offset of the file within its sid volume
/// </summary>
public ulong Offset { get; set; }

/// <summary>
/// Size of the file, in bytes
/// </summary>
public ulong Size { get; set; }

/// <summary>
/// Which # installer disc the file is on
/// </summary>
public byte DiscNumber { get; set; }

/// <summary>
/// Which # sid volume the file is in
/// </summary>
public byte VolumeNumber { get; set; }

/// <summary>
/// Whether the file is encrypted or not
/// </summary>
/// <remarks>
/// This flag isn't entirely reliable, as sometimes the file is encrypted in the sid without this
/// flag being set in the sid. The reverse is not currently known to be true, though, so there at
/// least shouldn't be any false positives when using this flag.
/// </remarks>
public bool Encrypted { get; set; }
Comment thread
mnadareski marked this conversation as resolved.

/// <summary>
/// Unknown 1-byte value, likely just padding
/// </summary>
public byte Unknown { get; set; }

// The filename and path aren't stored in the fileentry structure in the .sim, but the offsets are,
// so it just makes sense to also parse and store the strings for the file here too.

/// <summary>
/// The filename
/// </summary>
public string Name { get; set; } = string.Empty;

/// <summary>
/// The file path
/// </summary>
public string Path { get; set; } = string.Empty;
}
}
30 changes: 30 additions & 0 deletions SabreTools.Data.Models/Steam2Installer/Header.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Runtime.InteropServices;

namespace SabreTools.Data.Models.Steam2Installer
{
/// <summary>
/// Header from the .sim file.
/// </summary>
public sealed class Header
{
/// <summary>
/// .sim file magic value
/// </summary>
public uint Magic { get; set; }

/// <summary>
/// Version (0x00000001)
/// </summary>
public uint Version { get; set; }

/// <summary>
/// Number of discs
/// </summary>
public uint Discs { get; set; }

/// <summary>
/// Size of the section containing file and path strings.
/// </summary>
public uint StringsSize { get; set; }
}
}
31 changes: 31 additions & 0 deletions SabreTools.Data.Models/Steam2Installer/SimFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace SabreTools.Data.Models.Steam2Installer
{
/// <summary>
/// .sim file for a .sim/.sid archive
/// </summary>
/// <remarks>
/// While it is not known for sure what .sim stands for, it's assumed to mean Steam Installer Manifest.
/// This contains information about the .sid data volumes. It's used for retail installer discs for
/// Steam2 (Steam pre-Steam3/SteamPipe). Preceded by GCF (GCF and NCF are still used for actual game
/// installs on Steam2, sim/sid just replaced GCF for retail discs. NCF was never directly stored on
/// retail discs since sim/sid had already come out). Succeeded by csm/csd for Steam3/SteamPipe.
/// Code is based on NickNine's python-based sim/sid extractor at https://pastebin.com/sj6F64XP
/// While this was not referenced for any of the code in SabreTools, https://codeberg.org/CYBERDEV/SIDEx
/// provides additional documentation if it is needed.
/// </remarks>
public sealed class SimFile
{
/// <summary>
/// .sim file header
/// </summary>
public Header Header { get; set; } = new();

// The string bytes array would go here, but it gets parsed out into FileEntries during deserialization,
// so there's no point storing it in the model. Same with the file entries size and count.

/// <summary>
/// Array of file entries
/// </summary>
public FileEntry[] FileEntries { get; set; } = [];
}
}
15 changes: 15 additions & 0 deletions SabreTools.Data.Models/Steam2Installer/Steam2InstallerSet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace SabreTools.Data.Models.Steam2Installer
{
public class Steam2InstallerSet
{
/// <summary>
/// The sim file
/// </summary>
public SimFile Sim { get; set; } = new();

/// <summary>
/// The set of sid file volumes
/// </summary>
// public SidFile[] Sid { get; set; } = [];
}
}
72 changes: 72 additions & 0 deletions SabreTools.Serialization.Readers.Test/Steam2InstallerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System.IO;
using System.Linq;
using Xunit;

namespace SabreTools.Serialization.Readers.Test
{
public class Steam2InstallerTests
{
[Fact]
public void NullArray_Null()
{
byte[]? data = null;
int offset = 0;
var deserializer = new Steam2Installer();

var actual = deserializer.Deserialize(data, offset);
Assert.Null(actual);
}

[Fact]
public void EmptyArray_Null()
{
byte[]? data = [];
int offset = 0;
var deserializer = new Steam2Installer();

var actual = deserializer.Deserialize(data, offset);
Assert.Null(actual);
}

[Fact]
public void InvalidArray_Null()
{
byte[]? data = [.. Enumerable.Repeat<byte>(0xFF, 1024)];
int offset = 0;
var deserializer = new Steam2Installer();

var actual = deserializer.Deserialize(data, offset);
Assert.Null(actual);
}

[Fact]
public void NullStream_Null()
{
Stream? data = null;
var deserializer = new Steam2Installer();

var actual = deserializer.Deserialize(data);
Assert.Null(actual);
}

[Fact]
public void EmptyStream_Null()
{
Stream? data = new MemoryStream([]);
var deserializer = new Steam2Installer();

var actual = deserializer.Deserialize(data);
Assert.Null(actual);
}

[Fact]
public void InvalidStream_Null()
{
Stream? data = new MemoryStream([.. Enumerable.Repeat<byte>(0xFF, 1024)]);
var deserializer = new Steam2Installer();

var actual = deserializer.Deserialize(data);
Assert.Null(actual);
}
}
}
Loading