D2SSharp 0.0.7
See the version list below for details.
dotnet add package D2SSharp --version 0.0.7
NuGet\Install-Package D2SSharp -Version 0.0.7
<PackageReference Include="D2SSharp" Version="0.0.7" />
<PackageVersion Include="D2SSharp" Version="0.0.7" />
<PackageReference Include="D2SSharp" />
paket add D2SSharp --version 0.0.7
#r "nuget: D2SSharp, 0.0.7"
#:package D2SSharp@0.0.7
#addin nuget:?package=D2SSharp&version=0.0.7
#tool nuget:?package=D2SSharp&version=0.0.7
D2SSharp
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.Model;
// Read save file (uses built-in game data)
byte[] saveBytes = File.ReadAllBytes("MyCharacter.d2s");
D2Save save = D2Save.Read(saveBytes);
// 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);
File.WriteAllBytes("MyCharacter.d2s", buffer.AsSpan(0, bytesWritten).ToArray());
Reading Shared Stash
byte[] stashBytes = File.ReadAllBytes("SharedStashSoftCoreV2.d2i");
D2StashSave stash = D2StashSave.Read(stashBytes);
foreach (var tab in stash)
{
Console.WriteLine($"Tab: {tab.Name}, Items: {tab.Items.Count}");
}
External Data
The library includes embedded game data tables for versions 96, 97, and 99, which are used automatically. For modded games with custom items/stats, you can provide your own txt files:
using D2SSharp.Data;
// Load from a directory containing version subdirectories
// Directory structure:
// MyTxtFiles/
// 96/
// armor.txt, itemstatcost.txt, itemtypes.txt, misc.txt, weapons.txt
// 99/
// armor.txt, itemstatcost.txt, itemtypes.txt, misc.txt, weapons.txt
var modData = new TxtFileExternalData(@"C:\path\to\MyTxtFiles");
// Or load from a single directory for a specific version
var modData = new TxtFileExternalData(@"C:\path\to\MyTxtFiles\99", version: 99);
// Then use it for reading/writing
var save = D2Save.Read(File.ReadAllBytes("modded.d2s"), modData);
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:
// Read a 1.14 save (version 96)
var save = D2Save.Read(File.ReadAllBytes("old_character.d2s"));
// Write as D2R format (version 99)
byte[] buffer = new byte[save.EstimateSize()];
int written = save.Write(buffer, 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: See External Data section for loading mod-specific .txt files
- Trailing data: Any bytes after the standard sections are preserved in
D2Save.TrailingDataand 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 | Versions 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. |
-
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.