d2s

package module
v1.3.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 8, 2022 License: MIT Imports: 8 Imported by: 1

README

Diablo II character file parser

Go Report Card GoDoc

D2s is a binary parser written in Go that's used to parse .d2s files. This is the binary format that the game Diablo II uses to save all information about a certain character.

Motivation

This package was built for a private server of Diablo II called Slash Diablo to build an Armory for all characters on the server. Where anyone could see everything about a particular character at any given point in time. Here's a few examples.

Install

$ go get github.com/nokka/d2s

Usage

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/nokka/d2s"
)

func main() {
	path := "nokka.d2s"
	file, err := os.Open(path)
	if err != nil {
		log.Fatal("Error while opening .d2s file", err)
	}

	defer file.Close()

	char, err := d2s.Parse(file)
	if err != nil {
		log.Fatal(err)
	}

	// Prints character name and class.
	fmt.Println(char.Header.Name)
	fmt.Println(char.Header.Class)
}

Binary layout

Header

The header is 765 byte long struct containing most of the character meta data.

Offset Bytes Description
0 4 Identifier
4 4 Version ID
8 4 File size
12 4 Checksum
16 4 Active weapon
20 16 Character Name
36 1 Character Status
37 1 Character progression
38 2 Unknown
40 1 Character Class
41 2 Unknown
43 1 Character Level
44 4 Unknown
48 4 Last played
52 4 Unknown
56 64 Assigned skills
120 4 Left mouse button skill ID
124 4 Right mouse button skill ID
128 4 Left swap mouse button skill ID
132 4 Right swap mouse button skill ID
136 32 Character menu appearance
168 3 Difficulty
171 4 Map ID
175 2 Unknown
177 2 Mercenary dead
179 4 Mercenary ID
183 2 Mercenary Name ID
185 2 Mercenary type
187 4 Mercenary experience
191 144 Unknown
335 298 Quests
633 81 Waypoints
714 51 NPC Introductions
Character name

Character names are storted as a [16]byte which will contain the name, one letter per byte. The name can be 16 characters long, and a name that's shorter will have padded 0x00's behind the name until we reach 16 bytes.

  • Must be 2-15 in length
  • Must begin with a letter
  • May contain up to one - or _.
Character status

Character status is a byte where different bits will be set, depending on the status of the character. Still haven't figured them all out, but here's the most important ones.

Bit position
7 6 5 4 3 2 1 0
? Ladder Expansion ? Died Hardcore ? ?
Character progression

Not implemented yet.

The value is incremented every time you kill an act boss.

Classic
Value Standard Hardcore
0-3 - -
4-7 Sir/Dame Count/Countess
8-11 Lord/Lady Duke/Duchess
12 Baron/Baroness King/Queen
Expansion
Value Expansion Expansion hardcore
0-3 - -
5-8 Slayer Destroyer
10-13 Champion Conqueror
15 Patriarch/Matriarch Guardian
Character class

Character class is a byte where different values represent a class.

Class Value
Amazon 0x00
Sorceress 0x01
Necromancer 0x02
Paladin 0x03
Barbarian 0x04
Druid 0x05
Assassin 0x06
Last played

Last played is saved as a unit32 unix timestamp e.g 1495882861.

Assigned skills

Assigned skills section is a an array of 16 skill ids, each a 4 byte integer (uint32). If no skill is assigned the value is 0x00.

Quests

The quests struct is 298 byte section that describes all quests in the game but also contains data about act traveling and NPC introductions. Each quest is 2 byte long.

Header
Offset Bytes Content
335 4 Woo!
339 6 Unknown
Quest structs

A quest is 2 byte long, I've created a general quest struct that holds the most important data of a quest, if it's completed or not. Each quest has a lot of unique bits set depending on different milestones of the quest. For example if you've consumed the Scroll of Resistance from the quest "Prison of Ice" or not.

General
Bit Description
0 Quest completed
Prison of Ice

Prison of Ice is the only quest I bothered to implement, because I needed to know if the character has increased resistances from the scroll or not.

Bit Description
0 Quest completed
7 Consumed scroll
Quest structure

This structure repeats it self 3 times, once for Normal, Nightmare and Hell. The offset is the offset into the quest struct.

Offset Bytes Description
0 2 Set to 1 if you have been introduced to Warriv in Act I.
2 [6]quest All six quests for Act I.
14 2 Set to 1 if you have traveled to Act II.
16 2 Set to 1 if you have been introduced to Jerhyn.
18 [6]quest All six quests for Act II.
30 2 Set to 1 if you have traveled to Act III.
32 2 Set to 1 if you have been introduced to Hratli.
34 [6]quest All six quests for Act III.
46 2 Set to 1 if you have traveled to Act IV.
48 2 Set to 1 if you have been introduced to Act IV. (which you have if you have traveled)
50 [6]quest Act IV only has 3 quests, so the struct has 6 empty bytes here.
62 2 Set to 1 if you have traveled to Act V.
64 2 Seems to be set to 1 after completing Terror's End and talking to Cain in act IV.
66 4 Seems to be some kind of padding.
70 [6]quest All six quests for Act V.
82 14 Some kind of padding after all the quest data.
Waypoints

Not implemented

NPC Introductions

Not implemented

Attributes

Following the header is the attributes section, this sections layout consists of an array of 9 bit attribute id, followed by a n bit length attribute value. The section is terminated by a 9 bit value of 0x1ff. It's worth mentioning that these fields are bit reversed. Basically if you find the bits 00100111 they are reversed into 11100100.

Attribute IDs
ID Attribute
0 Strength
1 Energy
2 Dexterity
3 Vitality
4 Unused stats
5 Unused skills
6 Current HP
7 Max HP
8 Current mana
9 Max mana
10 Current stamina
11 Max stamina
12 Level
13 Experience
14 Gold
15 Stashed gold
Attribute bit lengths
Bit length Attribute
10 Strength
10 Energy
10 Dexterity
10 Vitality
10 Unused stats
8 Unused skills
21 Current HP
21 Max HP
21 Current mana
21 Max mana
21 Current stamina
21 Max stamina
7 Level
32 Experience
25 Gold
25 Stashed gold
Example of the reading
for {
    // 1. read 9 bits id. (reverse them)
    // 2. if the id is 0x1ff, terminate the loop
    // 3. read bit length from attribute map for that id.
    // 4. read bit length nr of bits. 
}
3. Skills

Skills are a 32 byte section containing a 2 byte header with the value if and 30 byte of skill data. Each class has 30 skills available to them, so each skill get 1 byte each. The tricky part about the skill mapping is that each class has a different offset into the skill map where their class specific skills start, and then go 30 indexes into the map. So for example Assassin has an offset of 251. Which means Assassin skills are between the indexes of 251 and 281 which is exactly 30 indexes.

Layout
Type Bytes Value
Header 2 if
Skills 30 [30]skill
Skill offset map
Class Offset
Amazon 6
Sorceress 36
Necromancer 66
Paladin 96
Barbarian 126
Druid 221
Assassin 251
4. Items

This is by far the most tricky part to read. The items section starts of with a 4 byte header, containing the value JM, and a uint16 value which is the item count your character currently has. Equipped, inventory, stash, cube and belt all included.

The byte length of the section is unknown before reading it in it's entirety, because the bit length of each item varies depending on its quality, number of sockets and magical attributes it possess.

Each item follows a certain pattern though, which is:

Simple items

Each item starts of with 111 bits of simple data, which all items contain. This is information like item type, if it's socketed, position id like equipped or stash and so on.

Each item also has a boolean called SimpleItem which is 1 bit long, if this is set to 1, the item contains no more bits, and the next item starts.

Advanced items

If the item is not a simple items, this means it will have tons of data following the initial 111 bits. A few examples of this is the rarity level, magical suffix, magical affix, if it's a runeword, personalized, part of a set, class specific and so on.

Last but not least if the item has will have lists of magical properties depending on if it's a runeword, magical, rare, crafted, unique part of set and so on.

These lists are similar to the attributes section where we will read:

  1. 9 bit id
  2. n bits of magical properties
  3. 0x1ff terminator

When we hit the terminator 0x1ff the next item begins.

Magical properties

A magical property is a unique property that can occur on an item, each property has a different bit length, and the map is huge.

Example

This is the magical property with id 83 which contains 2 bit fields each 3 bits long.

83: {Bits: []uint{3, 3}, Name: "+{1} to {0} Skill Levels"},

All magical properties are mapped in the item.go file.

5. Corpse data

If your character is currently dead, and a corpse is on the ground when you enter a game, your equipped items will be in this item struct. It's a corpse header 16 bytes containing the header JM followed by an item count similar to the item list.

Reading the corpse items are done in the exact way as the previous section of items.

6. Expansion data

If your character is created in the expansion Lord of Destruction if will contain 2 more sections.

7. Mercenary items

The mercenary sections starts of with a 2 byte header with the value jf and is followed by a 4 byte item header containing the number of items the mercenary is currently wearing. The items are read like any other item list.

8. Golem

If your character is both a Necromancer and an expansion character, this section starts of with a 3 byte header, where the first two bytes are the header kf followed by a boolean called hasGolem, if this value is true, there's an item list with the length 1 following the header.

Contributing

Please see CONTRIBUTING.md.

Documentation

Index

Constants

View Source
const (
	Amazon      = 0x00
	Sorceress   = 0x01
	Necromancer = 0x02
	Paladin     = 0x03
	Barbarian   = 0x04
	Druid       = 0x05
	Assassin    = 0x06
)

Mapper of the hex values of classes.

View Source
const (
	// Armor
	AlphaHelm         string = "dr6"
	AncientArmor      string = "aar"
	Antlers           string = "dr3"
	ArchonPlate       string = "utp"
	Armet             string = "ulm"
	AssaultHelmet     string = "ba4"
	AvengerGuard      string = "ba5"
	BalrogSkin        string = "upl"
	Basinet           string = "xhl"
	BattleBelt        string = "ztb"
	BattleBoots       string = "xtb"
	BattleGauntlets   string = "xtg"
	Belt              string = "mbl"
	BloodSpirit       string = "drb"
	BoneHelm          string = "bhm"
	BoneVisage        string = "uh9"
	Boneweave         string = "uhn"
	BoneweaveBoots    string = "umb"
	Bracers           string = "mgl"
	BrambleMitts      string = "ulg"
	BreastPlate       string = "brs"
	Cap               string = "cap"
	CarnageHelm       string = "bab"
	Casque            string = "xlm"
	ChainBoots        string = "mbt"
	ChainMail         string = "chn"
	ChaosArmor        string = "xul"
	Circlet           string = "ci0"
	ColossusGirdle    string = "uhc"
	ConquerorCrown    string = "bae"
	Corona            string = "urn"
	Coronet           string = "ci1"
	Crown             string = "crn"
	CrusaderGauntlets string = "utg"
	Cuirass           string = "xrs"
	DeathMask         string = "xsk"
	Demonhead         string = "usk"
	DemonhideArmor    string = "xla"
	DemonhideBoots    string = "xlb"
	DemonhideGloves   string = "xlg"
	DemonhideSash     string = "zlb"
	DestroyerHelm     string = "bad"
	Diadem            string = "ci3"
	DiamondMail       string = "ung"
	DreamSpirit       string = "drf"
	DuskShroud        string = "uui"
	EarthSpirit       string = "drd"
	EmbossedPlate     string = "xth"
	FalconMask        string = "dr4"
	FangedHelm        string = "ba2"
	FieldPlate        string = "fld"
	FullHelm          string = "fhl"
	PlateMail         string = "plt"
	FullPlateMail     string = "ful"
	FuryVisor         string = "bac"
	Gauntlets         string = "hgl"
	GhostArmor        string = "xui"
	GiantConch        string = "uhl"
	Girdle            string = "hbl"
	Gloves            string = "lgl"
	GothicPlate       string = "gth"
	GrandCrown        string = "xrn"
	GreatHauberk      string = "urs"
	GreatHelm         string = "ghm"
	GriffonHeadress   string = "dr7"
	GrimHelm          string = "xh9"
	GuardianCrown     string = "baf"
	HardLeatherArmor  string = "hla"
	HawkHelm          string = "dr2"
	HeavyBelt         string = "tbl"
	HeavyBoots        string = "vbt"
	HeavyBracers      string = "xmg"
	HeavyGloves       string = "vgl"
	HellforgedPlate   string = "ult"
	Helm              string = "hlm"
	HornedHelm        string = "ba3"
	HuntersGuise      string = "dr8"
	Hydraskull        string = "ukp"
	Hyperion          string = "urg"
	JawboneCap        string = "ba1"
	JawboneVisor      string = "ba6"
	KrakenShell       string = "uld"
	LacqueredPlate    string = "uth"
	LeatherArmor      string = "lea"
	LeatherBoots      string = "lbt"
	LightBelt         string = "vbl"
	LightGauntlets    string = "tgl"
	LightPlate        string = "ltp"
	LightPlatedBoots  string = "tbt"
	LinkedMail        string = "xng"
	LionHelm          string = "ba7"
	LoricatedMail     string = "ucl"
	MagePlate         string = "xtp"
	Mask              string = "msk"
	MeshArmor         string = "xhn"
	MeshBelt          string = "zmb"
	MeshBoots         string = "xmb"
	MirroredBoots     string = "utb"
	MithrilCoil       string = "umc"
	MyrmidonGreaves   string = "uhb"
	OgreGauntlets     string = "uhg"
	OrnatePlate       string = "xar"
	PlateBoots        string = "hbt"
	QuiltedArmor      string = "qui"
	RageMask          string = "ba8"
	RingMail          string = "rng"
	RussetArmor       string = "xpl"
	SacredArmor       string = "uar"
	SacredFeathers    string = "dr9"
	Sallet            string = "xkp"
	Sash              string = "lbl"
	SavageHelmet      string = "ba9"
	ScaleMail         string = "scl"
	ScarabHusk        string = "ula"
	ScarabshellBoots  string = "uvb"
	SerpentskinArmor  string = "xea"
	ShadowPlate       string = "uul"
	Shako             string = "uap"
	SharkskinBelt     string = "zvb"
	SharkskinBoots    string = "xvb"
	SharkskinGloves   string = "xvg"
	SharktoothArmor   string = "xld"
	SkullCap          string = "skp"
	SkySpirit         string = "dre"
	SlayerGuard       string = "baa"
	SpiderwebSash     string = "ulc"
	SpiredHelm        string = "uhm"
	SpiritMask        string = "dr5"
	SplintMail        string = "spl"
	StuddedLeather    string = "stu"
	SunSpirit         string = "drc"
	TemplarCoat       string = "xlt"
	Tiara             string = "ci2"
	TigulatedMail     string = "xcl"
	TotemicMask       string = "dra"
	TrellisedArmor    string = "xtu"
	TrollBelt         string = "utc"
	Vambraces         string = "umg"
	VampireboneGloves string = "uvg"
	VampirefangBelt   string = "uvc"
	WarBelt           string = "zhb"
	WarBoots          string = "xhb"
	WarGauntlets      string = "xhg"
	WarHat            string = "xap"
	WingedHelm        string = "xhm"
	WireFleece        string = "utu"
	WolfHead          string = "dr1"
	Wyrmhide          string = "uea"
	WyrmhideBoots     string = "ulb"

	// Misc
	Amethyst                      string = "gsv"
	AmnRune                       string = "r11"
	Amulet                        string = "amu"
	AntidotePotion                string = "yps"
	Arrows                        string = "aqv"
	BaalsEye                      string = "bey"
	BarkScroll                    string = "bks"
	BerRune                       string = "r30"
	Bolts                         string = "cqv"
	BookofSkill                   string = "ass"
	BurningEssenceofTerror        string = "bet"
	ChamRune                      string = "r32"
	ChargedEssenceofHatred        string = "ceh"
	GrandCharm                    string = "cm3"
	LargeCharm                    string = "cm2"
	SmallCharm                    string = "cm1"
	ChippedAmethyst               string = "gcv"
	ChippedDiamond                string = "gcw"
	ChippedEmerald                string = "gcg"
	ChippedRuby                   string = "gcr"
	ChippedSapphire               string = "gcb"
	ChippedSkull                  string = "skc"
	ChippedTopaz                  string = "gcy"
	DecipheredBarkScroll          string = "bkd"
	DiablosHorn                   string = "dhn"
	Diamond                       string = "gsw"
	DolRune                       string = "r14"
	ElRune                        string = "r01"
	EldRune                       string = "r02"
	Elixir                        string = "elx"
	Emerald                       string = "gsg"
	EthRune                       string = "r05"
	FalRune                       string = "r19"
	FesteringEssenceofDestruction string = "fed"
	FlawedAmethyst                string = "gfv"
	FlawedDiamond                 string = "gfw"
	FlawedEmerald                 string = "gfg"
	FlawedRuby                    string = "gfr"
	FlawedSapphire                string = "gfb"
	FlawedSkull                   string = "skf"
	FlawedTopaz                   string = "gfy"
	FlawlessAmethyst              string = "gzv"
	FlawlessDiamond               string = "glw"
	FlawlessEmerald               string = "glg"
	FlawlessRuby                  string = "glr"
	FlawlessSapphire              string = "glb"
	FlawlessSkull                 string = "skl"
	FlawlessTopaz                 string = "gly"
	FullHealingPotion             string = "hpf"
	FullManaPotion                string = "mpf"
	FullRejuvenationPotion        string = "rvl"
	Gold                          string = "gld"
	GoldBird                      string = "g34"
	GreaterHealingPotion          string = "hp5"
	GreaterManaPotion             string = "mp5"
	GulRune                       string = "r25"
	HealingPotion                 string = "hp3"
	HealingPotionMid              string = "hpo"
	HelRune                       string = "r15"
	Herb                          string = "hrb"
	HoradricCube                  string = "box"
	IdentifyBook                  string = "ibk"
	IdentifyScroll                string = "isc"
	IoRune                        string = "r16"
	IstRune                       string = "r24"
	IthRune                       string = "r06"
	JadeFigurine                  string = "j34"
	JahRune                       string = "r31"
	Jewel                         string = "jew"
	KeyofDestruction              string = "pk3"
	KeyofHate                     string = "pk2"
	KeyofTerror                   string = "pk1"
	KhalimsBrain                  string = "qbr"
	KhalimsEye                    string = "qey"
	KhalimsHeart                  string = "qhr"
	KoRune                        string = "r18"
	LamEsensTome                  string = "bbb"
	LargeBluePotion               string = "bpl"
	LargeRedPotion                string = "rpl"
	LemRune                       string = "r20"
	LesserHealingPotion           string = "hp1"
	LesserManaPotion              string = "mp1"
	LightHealingPotion            string = "hp2"
	LightManaPotion               string = "mp2"
	LoRune                        string = "r28"
	LumRune                       string = "r17"
	Maguffin                      string = "ice"
	MalRune                       string = "r23"
	ManaPotion                    string = "mp3"
	ManaPotionMid                 string = "mpo"
	MephistosBrain                string = "mbr"
	MephistoKey                   string = "luv"
	MephistoSoulStone             string = "mss"
	NefRune                       string = "r04"
	OhmRune                       string = "r27"
	OrtRune                       string = "r09"
	PerfectAmethyst               string = "gpv"
	PerfectDiamond                string = "gpw"
	PerfectEmerald                string = "gpg"
	PerfectRuby                   string = "gpr"
	PerfectSapphire               string = "gpb"
	PerfectSkull                  string = "skz"
	PerfectTopaz                  string = "gpy"
	PlayerEar                     string = "ear"
	PulRune                       string = "r21"
	RalRune                       string = "r08"
	RejuvenationPotion            string = "rvs"
	Ring                          string = "rin"
	Ruby                          string = "gsr"
	Sapphire                      string = "gsb"
	Scroll                        string = "0sc"
	ScrollofHoradric              string = "tr1"
	ScrollofMalah                 string = "tr2"
	PotionofLife                  string = "xyz"
	ShaelRune                     string = "r13"
	SkeletonKey                   string = "key"
	Skull                         string = "sku"
	SmallBluePotion               string = "bps"
	SmallRedPotion                string = "rps"
	SolRune                       string = "r12"
	StaminaPotion                 string = "vps"
	StandardofHeroes              string = "std"
	StrongHealingPotion           string = "hp4"
	StrongManaPotion              string = "mp4"
	SurRune                       string = "r29"
	TalRune                       string = "r07"
	ThawingPotion                 string = "wms"
	ThulRune                      string = "r10"
	TirRune                       string = "r03"
	TokenofAbsolution             string = "toa"
	Topaz                         string = "gsy"
	Torch                         string = "tch"
	TownPortalBook                string = "tbk"
	TownPortalScroll              string = "tsc"
	TwistedEssenceofSuffering     string = "tes"
	UmRune                        string = "r22"
	VexRune                       string = "r26"
	ViperAmulet                   string = "vip"
	ZodRune                       string = "r33"

	// Shields
	Aegis            string = "uow"
	AerinShield      string = "pa4"
	AkaranRondache   string = "pa7"
	AkaranTarge      string = "pa6"
	KurastShield     string = "pad"
	AncientShield    string = "xts"
	BarbedShield     string = "xpk"
	BladeBarrier     string = "upk"
	BloodlordSkull   string = "nef"
	BoneShield       string = "bsh"
	Buckler          string = "buc"
	CantorTrophy     string = "ne9"
	CrownShield      string = "pa5"
	Defender         string = "xuc"
	DemonHead        string = "ne5"
	DragonShield     string = "xit"
	FetishTrophy     string = "ne7"
	GargoyleHead     string = "ne4"
	GothicShield     string = "gts"
	GrimShield       string = "xsh"
	GildedShield     string = "pa9"
	HeirophantTrophy string = "nea"
	Heater           string = "uuc"
	HellspawnSkull   string = "neg"
	HeraldicShield   string = "pa3"
	KiteShield       string = "kit"
	LargeShield      string = "lrg"
	Luna             string = "uml"
	MinionSkull      string = "neb"
	Monarch          string = "uit"
	MummifiedTrophy  string = "ne6"
	OverseerSkull    string = "ned"
	Pavise           string = "xow"
	PreservedHead    string = "ne1"
	ProtectorShield  string = "pa8"
	Rondache         string = "pa2"
	RoundShield      string = "xml"
	RoyalShield      string = "paa"
	SacredRondache   string = "pac"
	SacredTarge      string = "pab"
	Scutum           string = "xrg"
	SextonTrophy     string = "ne8"
	SmallShield      string = "sml"
	SpikedShield     string = "spk"
	SuccubusSkull    string = "nee"
	Targe            string = "pa1"
	TowerShield      string = "tow"
	TrollNest        string = "ush"
	UnravellerHead   string = "ne3"
	VortexShield     string = "paf"
	Ward             string = "uts"
	ZakarumShield    string = "pae"
	ZombieHead       string = "ne2"

	// Weapons
	AncientAxe          string = "9gi"
	AncientSword        string = "9wd"
	Arbalest            string = "8lx"
	ArchonStaff         string = "6ws"
	AshwoodBow          string = "am6"
	Ataghan             string = "7sm"
	Axe                 string = "axe"
	BalancedAxe         string = "bal"
	BalancedKnife       string = "bkf"
	Balista             string = "8hx"
	BalrogBlade         string = "7gs"
	BalrogSpear         string = "7s7"
	BarbedClub          string = "9sp"
	Bardiche            string = "bar"
	BastardSword        string = "bsw"
	BattleAxe           string = "btx"
	BattleCestus        string = "7cs"
	BattleDart          string = "9tk"
	BattleHammer        string = "9wh"
	BattleScythe        string = "9s8"
	BattleStaff         string = "bst"
	BattleSword         string = "9bs"
	BeardedAxe          string = "9ba"
	BecDeCorbin         string = "9h9"
	BerserkerAxe        string = "7wa"
	Bill                string = "9vo"
	Blade               string = "bld"
	BladeBow            string = "6hb"
	BladeTalons         string = "btl"
	BoneKnife           string = "7dg"
	BoneWand            string = "bwn"
	Brandistock         string = "brn"
	BroadAxe            string = "bax"
	BroadSword          string = "bsd"
	BurntWand           string = "9wn"
	Caduceus            string = "7ws"
	CedarBow            string = "8lb"
	CedarStaff          string = "8cs"
	CeremonialBow       string = "am7"
	CeremonialJavelin   string = "ama"
	CeremonialPike      string = "am9"
	CeremonialSpear     string = "am8"
	Cestus              string = "ces"
	ChampionAxe         string = "7ga"
	ChampionSword       string = "7b7"
	ChokingGasPotion    string = "gpm"
	ChuKoNu             string = "8rx"
	Cinquedeas          string = "9kr"
	ClaspedOrb          string = "ob4"
	Claws               string = "clw"
	Claymore            string = "clm"
	Cleaver             string = "9ax"
	CloudySphere        string = "ob8"
	Club                string = "clb"
	ColossalSword       string = "7fb"
	ColossalBlade       string = "7gd"
	ColossusCrossbow    string = "6hx"
	ColossusVoulge      string = "7vo"
	CompositeBow        string = "cbw"
	ConquestSword       string = "7bs"
	Crossbow            string = "mxb"
	Crowbill            string = "9mp"
	CrusaderBow         string = "6l7"
	CrypticAxe          string = "7pa"
	CrypticSword        string = "7ls"
	CrystalSword        string = "crs"
	CrystallineGlobe    string = "ob7"
	Cudgel              string = "9cl"
	Cutlass             string = "9sm"
	DacianFalx          string = "9cm"
	Dagger              string = "dgr"
	Decapitator         string = "7bt"
	DecoyDagger         string = "d33"
	DemonCrossbow       string = "6rx"
	DemonHeart          string = "obd"
	DevilStar           string = "7mt"
	DiamondBow          string = "6s7"
	DimensionalBlade    string = "9cr"
	DimensionalShard    string = "obf"
	Dirk                string = "dir"
	DivineScepter       string = "9ws"
	DoubleAxe           string = "2ax"
	DoubleBow           string = "8cb"
	DragonStone         string = "ob5"
	EableOrb            string = "ob1"
	EdgeBow             string = "8sb"
	ElderStaff          string = "6cs"
	EldritchOrb         string = "obc"
	ElegantBlade        string = "7sb"
	Espadon             string = "92h"
	EttinAxe            string = "72a"
	ExecutionerSword    string = "9gd"
	ExplodingPotion     string = "opm"
	Falcata             string = "7ss"
	Falchion            string = "flc"
	FangedKnife         string = "7kr"
	Fascia              string = "9xf"
	FeralAxe            string = "7la"
	FeralClaws          string = "7lw"
	Flail               string = "fla"
	Flamberge           string = "flb"
	FlangedMace         string = "9ma"
	FlyingAxe           string = "7ta"
	FlyingKnife         string = "7tk"
	Francisca           string = "9ta"
	FulmatingPotion     string = "opl"
	Fuscina             string = "9tr"
	GhostGlaive         string = "7gl"
	GhostSpear          string = "7st"
	GhostWand           string = "7yw"
	GiantAxe            string = "gix"
	GiantSword          string = "gis"
	GiantThresher       string = "7wc"
	Gidbinn             string = "g33"
	Gladius             string = "9ss"
	Glaive              string = "glv"
	GloriousAxe         string = "7gi"
	GlowingOrb          string = "ob6"
	GnarledStaff        string = "cst"
	GorgonCrossbow      string = "6mx"
	GothicAxe           string = "9ga"
	GothicBow           string = "8lw"
	GothicStaff         string = "8bs"
	GothicSword         string = "9b9"
	GrandMatronBow      string = "amc"
	GrandScepter        string = "gsc"
	GraveWand           string = "9gw"
	GreatAxe            string = "gax"
	GreatBow            string = "6cb"
	GreatMaul           string = "gma"
	GreatPilum          string = "9pi"
	GreatPoleaxe        string = "7h7"
	GreatSword          string = "gsd"
	GreaterClaws        string = "9lw"
	GreaterTalons       string = "9tw"
	GrimScythe          string = "9wc"
	GrimWand            string = "gwn"
	Halberd             string = "hal"
	HandAxe             string = "hax"
	HandScythe          string = "9cs"
	Harpoon             string = "9ts"
	Hatchet             string = "9ha"
	HatchetHands        string = "axf"
	HeavenlyStone       string = "obb"
	HeavyCrossbow       string = "hxb"
	HellforgeHammer     string = "hfh"
	HighlandBlade       string = "7cm"
	HolyWaterSprinkler  string = "9qs"
	HoradricMalus       string = "hdm"
	HoradricStaff       string = "hst"
	HuntersBow          string = "hbw"
	Hurlbat             string = "9b8"
	HydraBow            string = "6lw"
	HydraEdge           string = "7fc"
	HyperionJavelin     string = "7ja"
	HyperionSpear       string = "7sr"
	JaggedStar          string = "9mt"
	Javelin             string = "jav"
	JoStaff             string = "8ss"
	Katar               string = "ktr"
	KhalimFlail         string = "qf1"
	Knout               string = "9fl"
	Kriss               string = "kri"
	Lance               string = "9p9"
	LargeAxe            string = "lax"
	LegendSpike         string = "7bl"
	LegendSword         string = "72h"
	LegendaryMallet     string = "7wh"
	LichWand            string = "7bw"
	LightCrossbow       string = "lxb"
	LochaberAxe         string = "9b7"
	LongBattleBow       string = "lbb"
	LongBow             string = "lbw"
	LongSiegeBow        string = "8l8"
	LongStaff           string = "lst"
	LongSword           string = "lsd"
	LongWarBow          string = "lwb"
	Mace                string = "mac"
	MaidenJavelin       string = "am5"
	MaidenPike          string = "am4"
	MaidenSpear         string = "am3"
	Mancatcher          string = "7br"
	MarteldeFer         string = "9gm"
	MatriarchalBow      string = "amb"
	MatriarchalPike     string = "ame"
	MatriarchalSpear    string = "amd"
	MatriarchalJavelin  string = "amf"
	Maul                string = "mau"
	MightyScepter       string = "7sc"
	MilitaryAxe         string = "9la"
	MilitaryPick        string = "mpi"
	MithralPoint        string = "7di"
	MorningStar         string = "mst"
	MythicalSword       string = "7wd"
	Naga                string = "9wa"
	OgreAxe             string = "7o7"
	OgreMaul            string = "7m7"
	OilPotion           string = "ops"
	Partizan            string = "9pa"
	PelletBow           string = "6lx"
	PetrifiedWand       string = "9yw"
	PhaseBlade          string = "7cr"
	Pike                string = "pik"
	Pilum               string = "pil"
	Poignard            string = "9dg"
	Poleaxe             string = "pax"
	PolishedWand        string = "7wn"
	QuarterStaff        string = "8ls"
	Quhab               string = "9ar"
	RancidGasPotion     string = "gps"
	RazorBow            string = "8hb"
	ReflexBow           string = "am2"
	ReinforcedMace      string = "7ma"
	RepeatingCrossbow   string = "rxb"
	Rondel              string = "9di"
	RuneBow             string = "8sw"
	RuneScepter         string = "9sc"
	RuneStaff           string = "8ws"
	RuneSword           string = "9ls"
	RunicTalons         string = "7tw"
	Sabre               string = "sbr"
	SacredGlobe         string = "ob2"
	Scepter             string = "scp"
	Scimitar            string = "scm"
	ScissorsKatar       string = "skr"
	ScissorsQuhab       string = "9qr"
	ScissorsSuwayyah    string = "7qr"
	Scourge             string = "7fl"
	Scythe              string = "scy"
	SeraphRod           string = "7qs"
	ShadowBow           string = "6lb"
	Shamshir            string = "9sb"
	Shillelah           string = "6bs"
	ShortBattleBow      string = "sbb"
	ShortBow            string = "sbw"
	ShortSiegeBow       string = "8s8"
	ShortSpear          string = "ssp"
	ShortStaff          string = "sst"
	ShortSword          string = "ssd"
	ShortWarBow         string = "swb"
	SiegeCrossbow       string = "8mx"
	SilverEdgedAxe      string = "7ba"
	Simbilan            string = "9s9"
	SmallCrescent       string = "7ax"
	SmokedSphere        string = "ob3"
	SparklingBall       string = "ob9"
	Spear               string = "spr"
	Spetum              string = "spt"
	Spiculum            string = "9gl"
	SpiderBow           string = "6sb"
	SpikedClub          string = "spc"
	StaffOfTheKings     string = "msf"
	StagBow             string = "am1"
	Stalagmite          string = "6ls"
	Stilleto            string = "9bl"
	StranglingGasPotion string = "gpl"
	StygianPike         string = "7tr"
	StygianPilum        string = "7pi"
	SuperKhalimFlail    string = "qf2"
	Suwayyah            string = "7ar"
	SwirlingCrystal     string = "oba"
	Tabar               string = "9bt"
	Thresher            string = "7s8"
	ThrowingAxe         string = "tax"
	ThrowingKnife       string = "tkf"
	ThrowingSpear       string = "tsp"
	ThunderMaul         string = "7gm"
	Tomahawk            string = "7ha"
	TombWand            string = "9bw"
	Trident             string = "tri"
	Truncheon           string = "7cl"
	Tulwar              string = "9fc"
	TuskSword           string = "9gs"
	TwinAxe             string = "92a"
	TwoHandedSword      string = "2hs"
	TyrantClub          string = "7sp"
	UnearthedWand       string = "7gw"
	VortexOrb           string = "obe"
	Voulge              string = "vou"
	WalkingStick        string = "6ss"
	Wand                string = "wnd"
	WarAxe              string = "wax"
	WarClub             string = "9m9"
	WarDart             string = "9bk"
	WarFist             string = "7xf"
	WarFork             string = "9br"
	WarHammer           string = "whm"
	WarJavelin          string = "9ja"
	WarPike             string = "7p7"
	WarScepter          string = "wsp"
	WarScythe           string = "wsc"
	WarSpear            string = "9sr"
	WarSpike            string = "7mp"
	WarStaff            string = "wst"
	WarSword            string = "wsd"
	WardBow             string = "6sw"
	WingedAxe           string = "7b8"
	WingedHarpoon       string = "7ts"
	WingedKnife         string = "7bk"
	WirtsLeg            string = "leg"
	WristBlade          string = "wrb"
	WristSpike          string = "9wb"
	WristSword          string = "7wb"
	Yari                string = "9st"
	YewWand             string = "ywn"
	Zweihander          string = "9fb"
)

Item code value constant used as an internal reference or "ID".

Variables

This section is empty.

Functions

This section is empty.

Types

type Attributes

type Attributes struct {
	Strength          uint64 `json:"strength"`
	Energy            uint64 `json:"energy"`
	Dexterity         uint64 `json:"dexterity"`
	Vitality          uint64 `json:"vitality"`
	UnusedStats       uint64 `json:"unused_stats"`
	UnusedSkillPoints uint64 `json:"unused_skill_points"`
	CurrentHP         uint64 `json:"current_hp"`
	MaxHP             uint64 `json:"max_hp"`
	CurrentMana       uint64 `json:"current_mana"`
	MaxMana           uint64 `json:"max_mana"`
	CurrentStamina    uint64 `json:"current_stamina"`
	MaxStamina        uint64 `json:"max_stamina"`
	Level             uint64 `json:"level"`
	Experience        uint64 `json:"experience"`
	Gold              uint64 `json:"gold"`
	StashedGold       uint64 `json:"stashed_gold"`
}

Attributes are the different type of attributes a character can have.

type Character

type Character struct {
	Header      Header     `json:"header"`
	Attributes  Attributes `json:"attributes"`
	Skills      []Skill    `json:"skills"`
	Items       []Item     `json:"items"`
	CorpseItems []Item     `json:"corpse_items"`
	MercItems   []Item     `json:"merc_items"`
	GolemItem   *Item      `json:"golem_item"`
	IsDead      uint16     `json:"is_dead"`
}

Character represents all the d2s character data.

func Parse

func Parse(file io.Reader) (*Character, error)

Parse will read the data from a d2s character file and return a normalized struct.

func ParseFromContent

func ParseFromContent(data []byte) (*Character, error)

ParseFromContent will read the character from a byte slice.

type Header struct {
	Identifier  uint32      `json:"identifier"`
	Version     uint32      `json:"version"`
	FileSize    uint32      `json:"filesize"`
	CheckSum    uint32      `json:"checksum"`
	ActiveArms  uint32      `json:"active_arms"`
	Name        name        `json:"name"`
	Status      status      `json:"status"`
	Progression progression `json:"progression"`

	Class class `json:"class"`

	Level byte `json:"level"`

	LastPlayed uint32 `json:"last_played"`

	AssignedSkills [16]uint32 `json:"assigned_skills"`
	LeftSkill      uint32     `json:"left_skill"`
	RightSkill     uint32     `json:"right_skill"`
	LeftSwapSkill  uint32     `json:"left_swap_skill"`
	RightSwapSkill uint32     `json:"right_swap_skill"`

	CurrentDifficulty difficulty `json:"difficulty"`
	MapID             uint32     `json:"map_id"`

	DeadMerc   uint16 `json:"dead_merc"`
	MercID     uint32 `json:"merc_id"`
	MercNameID uint16 `json:"merc_name_id"`
	MercType   uint16 `json:"merc_type"`
	MercExp    uint32 `json:"merc_experience"`

	QuestHeader [4]byte `json:"-"`

	QuestsNormal   quests  `json:"quests_normal"`
	QuestsNm       quests  `json:"quests_nm"`
	QuestsHell     quests  `json:"quests_hell"`
	WaypointHeader [2]byte `json:"-"`

	WaypointsNormal [24]byte `json:"-"`
	WaypointsNm     [24]byte `json:"-"`
	WaypointsHell   [24]byte `json:"-"`
	WaypointTrailer byte     `json:"-"`
	NPCHeader       [2]byte  `json:"-"`

	NPCIntroNormal [5]byte `json:"-"`

	NPCIntroNm [5]byte `json:"-"`

	NPCIntroHell [5]byte `json:"-"`

	NPCReturnNorm [4]byte `json:"-"`

	NPCReturnNm [4]byte `json:"-"`

	NPCReturnHell [4]byte `json:"-"`

	StatHeader [2]byte `json:"-"`
	// contains filtered or unexported fields
}

Header determines the header data of a d2s file.

func (*Header) MarshalJSON

func (h *Header) MarshalJSON() ([]byte, error)

MarshalJSON ...

type Item

type Item struct {
	Identified          uint64             `json:"identified"`
	Socketed            uint64             `json:"socketed"`
	New                 uint64             `json:"new"`
	IsEar               uint64             `json:"is_ear"`
	StarterItem         uint64             `json:"starter_item"`
	SimpleItem          uint64             `json:"simple_item"`
	Ethereal            uint64             `json:"ethereal"`
	Personalized        uint64             `json:"personalized"`
	PersonalizedName    string             `json:"personalized_name,omitempty"`
	GivenRuneword       uint64             `json:"given_runeword"`
	Version             uint64             `json:"version"`
	LocationID          uint64             `json:"location_id"`
	EquippedID          uint64             `json:"equipped_id,omitempty"`
	PositionX           uint64             `json:"position_x"`
	PositionY           uint64             `json:"position_y"`
	AltPositionID       uint64             `json:"alt_position_id"`
	Type                string             `json:"type"`
	TypeID              uint64             `json:"type_id"`
	TypeName            string             `json:"type_name"`
	NrOfItemsInSockets  uint64             `json:"nr_of_items_in_sockets"`
	ID                  uint64             `json:"id"`
	Level               uint64             `json:"level"`
	Quality             uint64             `json:"quality"`
	MultiplePictures    uint64             `json:"multiple_pictures"`
	PictureID           uint64             `json:"picture_id"`
	ClassSpecific       uint64             `json:"class_specific"`
	LowQualityID        uint64             `json:"low_quality_id"`
	Timestamp           uint64             `json:"timestamp"`
	EarAttributes       earAttributes      `json:"ear_attributes"`
	DefenseRating       int64              `json:"defense_rating,omitempty"`
	MaxDurability       uint64             `json:"max_durability,omitempty"`
	CurrentDurability   uint64             `json:"current_durability,omitempty"`
	TotalNrOfSockets    uint64             `json:"total_nr_of_sockets"`
	Quantity            uint64             `json:"quantity,omitempty"`
	MagicPrefix         uint64             `json:"magic_prefix,omitempty"`
	MagicPrefixName     string             `json:"magic_prefix_name,omitempty"`
	MagicSuffix         uint64             `json:"magic_suffix,omitempty"`
	MagicSuffixName     string             `json:"magic_suffix_name,omitempty"`
	RunewordID          uint64             `json:"runeword_id,omitempty"`
	RunewordName        string             `json:"runeword_name,omitempty"`
	RunewordAttributes  []MagicAttribute   `json:"runeword_attributes"`
	SetID               uint64             `json:"set_id"`
	SetName             string             `json:"set_name,omitempty"`
	SetListCount        uint64             `json:"set_list_count"`
	SetAttributes       [][]MagicAttribute `json:"set_attributes"`
	SetAttributesNumReq []uint             `json:"set_attributes_num_req,omitempty"`
	SetAttributesIDsReq []uint64           `json:"set_attributes_ids_req,omitempty"`
	RareName            string             `json:"rare_name,omitempty"`
	RareName2           string             `json:"rare_name2,omitempty"`
	MagicalNameIDs      []uint64           `json:"magical_name_ids,omitempty"`
	UniqueID            uint64             `json:"unique_id"`
	UniqueName          string             `json:"unique_name,omitempty"`
	MagicAttributes     []MagicAttribute   `json:"magic_attributes"`
	SocketedItems       []Item             `json:"socketed_items"`
	BaseDamage          *WeaponDamage      `json:"base_damage,omitempty"`
}

Item describes any type of item and all it's data.

func ParseItemList added in v1.3.0

func ParseItemList(byteReader io.ByteReader, itemCount int) ([]Item, error)

type MagicAttribute added in v1.2.1

type MagicAttribute struct {
	ID     uint64  `json:"id"`
	Name   string  `json:"name"`
	Values []int64 `json:"values"`
}

MagicAttribute describes one of potentially many attributes on an item.

The values array is replaced into the name string using the value's own array index wrapped in curly braces, e.g., `{i}`. Some values will need to be denormalized before replacing, such as class names, and various calculations.

Note the values array is of the type int64, this is because some properties contain negative values, such as - % requirements.

type MagicalProperty added in v1.2.1

type MagicalProperty struct {
	Bits []uint
	Bias uint64
	Name string
}

MagicalProperty describes a string template, bias, and bit length for a particular kind of magical property, such as bonuses to all resistences or magic item finding.

type Skill

type Skill struct {
	ID     int    `json:"id"`
	Points int    `json:"points"`
	Name   string `json:"name"`
}

Skill represents an available character skill in d2.

type WeaponDamage added in v1.2.1

type WeaponDamage struct {
	Min    int `json:"min,omitempty"`
	Max    int `json:"max,omitempty"`
	TwoMin int `json:"twohand_min,omitempty"`
	TwoMax int `json:"twohand_max,omitempty"`
}

WeaponDamage contains integer ranges for any weapon's one-handed, two-handed (and maybe soon throwing as well?) damage.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL