D2SSharp 0.0.4

There is a newer version of this package available.
See the version list below for details.
dotnet add package D2SSharp --version 0.0.4
                    
NuGet\Install-Package D2SSharp -Version 0.0.4
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="D2SSharp" Version="0.0.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="D2SSharp" Version="0.0.4" />
                    
Directory.Packages.props
<PackageReference Include="D2SSharp" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add D2SSharp --version 0.0.4
                    
#r "nuget: D2SSharp, 0.0.4"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package D2SSharp@0.0.4
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=D2SSharp&version=0.0.4
                    
Install as a Cake Addin
#tool nuget:?package=D2SSharp&version=0.0.4
                    
Install as a Cake Tool

D2SSharp

Build NuGet

A C# library for reading and writing Diablo 2 save files (.d2s character saves and .d2i shared stash files). Supports both the original Diablo 2: Lord of Destruction format (1.10+) and Diablo 2 Resurrected.

Features

  • Full round-tripping support - produces identical outputs, as verified by tests.
  • Supports providing external .txt files for loading characters that have been saved by mods.
  • Full read/write support for character save files (.d2s)
  • Shared stash file support (.d2i)
  • Supports D2 LOD (version 96) and D2R (version 97+) formats
  • Complete item parsing including stats, sockets, runewords, and set bonuses
  • Zero external dependencies beyond .NET

Acknowledgments

This project was vibe coded with Claude.

Installation

dotnet add package D2SSharp

Or clone and build from source:

git clone https://github.com/ResurrectedTrader/D2SSharp.git
cd D2SSharp
dotnet build

Usage

Reading a Character Save

using D2SSharp.Data;
using D2SSharp.Model;

// Load game data (uses embedded txt files)
var gameData = new TxtFileExternalData();

// Read save file
byte[] saveBytes = File.ReadAllBytes("MyCharacter.d2s");
D2Save save = D2Save.Read(saveBytes, gameData);

// Access character info
Console.WriteLine($"Character: {save.Character.Preview.Name}");
Console.WriteLine($"Level: {save.Stats.Level}");
Console.WriteLine($"Class: {save.Character.Class}");

// Iterate items
foreach (var item in save.Items)
{
    Console.WriteLine($"Item: {item.ItemCodeString} (Quality: {item.Quality})");
}

Modifying and Saving

// Modify stats
save.Stats.Strength = 200;
save.Stats.GoldInStash = 2500000;

// Write back to file
byte[] buffer = new byte[save.EstimateSize()];
int bytesWritten = save.Write(buffer, gameData);
File.WriteAllBytes("MyCharacter.d2s", buffer.AsSpan(0, bytesWritten).ToArray());

Reading Shared Stash

byte[] stashBytes = File.ReadAllBytes("SharedStashSoftCoreV2.d2i");
var reader = new BitReader(stashBytes);
D2StashSave stash = D2StashSave.Read(ref reader, gameData, saveVersion: 97);

foreach (var tab in stash)
{
    Console.WriteLine($"Tab: {tab.Name}, Items: {tab.Items.Count}");
}

External Data

The library requires game data tables to correctly parse items:

  • TxtFileExternalData: Loads from D2 .txt data files. Uses embedded data by default for versions 96, 97, and 99.
// Use embedded data (default) - supports all standard versions
var gameData = new TxtFileExternalData();

// Or load from a directory containing version subdirectories
// Directory structure should be:
//   MyTxtFiles/
//     96/
//       armor.txt, itemstatcost.txt, itemtypes.txt, misc.txt, weapons.txt
//     99/
//       armor.txt, itemstatcost.txt, itemtypes.txt, misc.txt, weapons.txt
var gameData = new TxtFileExternalData(@"C:\path\to\MyTxtFiles");

The library selects version data based on exact match with the save file's version. If the required version is not available, an exception is thrown listing the available versions.

Save Format Versions

Version Game Notes
96 D2 LOD 1.10+ 32-bit item codes, 7-bit strings
97 D2 Resurrected Huffman-encoded item codes, 7-bit strings
98+ D2 Resurrected Huffman-encoded item codes, 8-bit strings

Version Conversion

The library supports converting saves between D2 LOD (1.14) and D2R formats by specifying a target version when writing:

// Use embedded data (contains 96, 97 and 99 versions)
var gameData = new TxtFileExternalData();

// Read a 1.14 save (version 96)
var save = D2Save.Read(File.ReadAllBytes("old_character.d2s"), gameData);

// Write as D2R format (version 99)
byte[] buffer = new byte[save.EstimateSize()];
int written = save.Write(buffer, gameData, targetVersion: 99);
File.WriteAllBytes("new_character.d2s", buffer.AsSpan(0, written).ToArray());

Conversion Details

The library handles the following format differences automatically:

1.14 → D2R
Field Conversion
Character.Name Moved to Character.Preview.Name (D2R uses UTF-8 preview name)
Character.Preview.* Populated from equipped items (Head, Torso, LeftHand, RightHand)
Character.Preview.Transform Looked up from Character.AppearanceTints
Character.Preview.FileIndex Extracted from item quality data
Character.Preview.Flags Set to Targeting for primary weapon, 0 for others
Item.Position.BodyLocation Zeroed for non-equipped items (D2R requires this)
D2R → 1.14
Field Conversion
Character.Preview.Name Moved to Character.Name
Character.Preview.* Zeroed (1.14 doesn't use preview items)
Item.Position.BodyLocation Kept as None for stored items (D2R doesn't preserve original equip slot)
Binary Differences

When comparing converted saves to saves created by the game:

Section Reason
Items Item ordering may differ between saves of the same character
Header (Checksum) Differs due to item ordering differences

These differences do not affect gameplay - the converted saves are fully functional.

Mod Compatibility

The library provides limited support for modded save files:

Supported

  • Custom item data: Use TxtFileExternalData with a directory containing version subdirectories with mod-specific .txt files:
    // Directory structure: ModData/99/armor.txt, ModData/99/itemstatcost.txt, etc.
    var modData = new TxtFileExternalData(@"C:\path\to\ModData");
    var save = D2Save.Read(File.ReadAllBytes("modded_character.d2s"), modData);
    
  • Trailing data: Any bytes after the standard sections are preserved in D2Save.TrailingData and written back during round-trip

Not Supported

  • Missing sections: Save files must contain all required sections. Files with missing or malformed sections (e.g., Expansion flag set but no MercItems/IronGolem sections) will fail to parse
  • Modified section formats: The library expects standard D2/D2R section layouts

Building

dotnet build D2SSharp.sln
dotnet test

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.12 80 1/3/2026
0.0.11 156 12/21/2025
0.0.10 150 12/21/2025
0.0.9 146 12/21/2025
0.0.8 155 12/21/2025
0.0.7 149 12/21/2025
0.0.6 145 12/21/2025
0.0.5 146 12/21/2025
0.0.4 119 12/21/2025
0.0.3 107 12/21/2025
0.0.2 112 12/21/2025
0.0.1 111 12/21/2025