Diablo II menyimpan karakter game Anda di disk sebagai file .d2s. Ini adalah format file biner yang mengkodekan semua statistik, item, nama, dan data lainnya.
Integer disimpan dalam urutan byte endian kecil, yang merupakan pemesanan byte asli pada arsitektur x86 Diablo II.
Setiap file .d2s dimulai dengan header 765 byte, setelah itu data memiliki panjang variabel.
| Hex | Byte | Panjang | Desc |
|---|---|---|---|
| 0x00 | 0 | 4 | Tanda tangan (0xaa55aa55) |
| 0x04 | 4 | 4 | ID Versi |
| 0x08 | 8 | 4 | Ukuran file |
| 0x0c | 12 | 4 | Checksum |
| 0x10 | 16 | 4 | Senjata aktif |
| 0x14 | 20 | 16 | Nama Karakter |
| 0x24 | 36 | 1 | Status karakter |
| 0x25 | 37 | 1 | Perkembangan karakter |
| 0x26 | 38 | 2 | ? |
| 0x28 | 40 | 1 | Kelas Karakter |
| 0x29 | 41 | 2 | ? |
| 0x2b | 43 | 1 | Tingkat |
| 0x2c | 44 | 4 | ? |
| 0x30 | 48 | 4 | Waktu |
| 0x34 | 52 | 4 | ? |
| 0x38 | 56 | 64 | Hotkeys |
| 0x78 | 120 | 4 | Mouse kiri |
| 0x7c | 124 | 4 | Tikus kanan |
| 0x80 | 128 | 4 | Mouse kiri (sakelar senjata) |
| 0x84 | 132 | 4 | Mouse kanan (sakelar senjata) |
| 0x88 | 136 | 32 | Penampilan Menu Karakter |
| 0xA8 | 168 | 3 | Kesulitan |
| 0xab | 171 | 4 | Peta |
| 0xaf | 175 | 2 | ? |
| 0xB1 | 177 | 2 | Merc Dead? |
| 0xB3 | 179 | 4 | Biji merc? |
| 0xB7 | 183 | 2 | ID Nama Merc |
| 0xB9 | 185 | 2 | Jenis Merc |
| 0xBB | 187 | 4 | Pengalaman Merc |
| 0xbf | 191 | 144 | ? |
| 0x14f | 335 | 298 | Pencarian |
| 0x279 | 633 | 81 | Waypoint |
| 0x2ca | 714 | 51 | NPC |
| 0x2fd | 765 | Statistik | |
| Item |
Versi file. Nilai -nilai berikut diketahui:
71 adalah 1,00 hingga v1.0687 adalah 1,07 atau set ekspansi v1.0889 adalah game standar v1.0892 adalah v1.09 (baik game standar dan set ekspansi.)96 adalah v1.10+Untuk menghitung checksum, atur nilai dalam data .d2s menjadi nol dan beralih melalui semua byte dalam data yang menghitung checksum 32-bit:
sum = ( sum << 1 ) + data [ i ];Sumber: #5
const fs = require ( "fs" ) ;
const path = require ( "path" ) ;
const file = path . join ( process . cwd ( ) , "path_to_save.d2s" ) ;
function calculateSum ( data ) {
let sum = 0 ;
for ( let i = 0 ; i < data . length ; i ++ ) {
let ch = data [ i ] ;
if ( i >= 12 && i < 16 ) {
ch = 0 ;
}
ch += sum < 0 ;
sum = ( sum << 1 ) + ch ;
}
return sum ;
}
function littleToBigEndian ( number ) {
return new DataView (
Int32Array . of (
new DataView ( Int32Array . of ( number ) . buffer ) . getUint32 ( 0 , true )
) . buffer
) ;
}
function ashex ( buffer ) {
return buffer . getUint32 ( 0 , false ) . toString ( 16 ) ;
}
async function readSafeFile ( ) {
return await new Promise ( ( resolve , reject ) => {
fs . readFile ( file , ( err , data ) => {
if ( err ) return reject ( err ) ;
return resolve ( data ) ;
} ) ;
} ) ;
}
async function writeCheckSumToSafeFile ( data ) {
return await new Promise ( ( resolve , reject ) => {
fs . writeFile ( file , data , err => {
if ( err ) reject ( err ) ;
resolve ( ) ;
} ) ;
} ) ;
}
readSafeFile ( ) . then ( data => {
const sum = calculateSum ( data ) ;
const bufferSum = littleToBigEndian ( sum ) ;
const hex = ashex ( bufferSum ) ;
const newData = data ;
for ( let i = 0 ; i < 4 ; i ++ ) {
newData [ 12 + i ] = bufferSum . getInt8 ( i ) ;
}
writeCheckSumToSafeFile ( newData ) . then ( ( ) => console . log ( hex ) ) ;
} ) ;Sumber: https://github.com/gucio321/d2d2s/blob/66f91e2af7b3949ca7f279aae397bd8904519e2d/pkg/d2s/d2s.go#l397
// CalculateChecksum calculates a checksum and saves in a byte slice
func CalculateChecksum ( data * [] byte ) {
var sum uint32
for i := range * data {
sum = (( sum << 1 ) % math . MaxUint32 ) | ( sum >> ( int32Size * byteLen - 1 ))
sum += uint32 (( * data )[ i ])
}
sumBytes := make ([] byte , int32Size )
binary . LittleEndian . PutUint32 ( sumBytes , sum )
const (
int32Size = 4
checksumPosition = 12
)
for i := 0 ; i < int32Size ; i ++ {
( * data )[ checksumPosition + i ] = sumBytes [ i ]
}
}Jika checksum tidak valid, Diablo II tidak akan membuka file simpan.
Todo
Nama karakter disimpan sebagai array 16 karakter yang berisi string yang diakhiri nol empuk dengan 0x00 untuk byte yang tersisa. Karakter disimpan sebagai ASCII 8-bit, tetapi ingat bahwa valid harus mengikuti aturan ini:
- ) atau garis bawah ( _ )Ini adalah bidang 8-bit:
| Sedikit | Desc |
|---|---|
| 0 | ? |
| 1 | ? |
| 2 | Hardcore |
| 3 | Mati |
| 4 | ? |
| 5 | Ekspansi |
| 6 | ? |
| 7 | ? |
Todo
| PENGENAL | Kelas |
|---|---|
| 0 | Amazon |
| 1 | Penyihir |
| 2 | Ahli nujum |
| 3 | Paladin |
| 4 | Barbar |
| 5 | Druid |
| 6 | Pembunuh |
Nilai level ini hanya terlihat di layar pilih karakter dan harus sama dengan bagian ini di bagian statistik.
Todo
Struktur 32 byte yang mendefinisikan bagaimana karakter terlihat di menu tidak mengubah tampilan dalam game
3 byte data yang menunjukkan mana dari tiga kesulitan karakter yang telah dibuka. Setiap byte representitif dari salah satu kesulitan. Dalam urutan ini: Normal, mimpi buruk, dan neraka. Setiap byte terstruktur bitfield seperti ini:
| 7 | 6 | 5 | 4 | 3 | 2, 1, 0 |
|---|---|---|---|---|---|
| Aktif? | Tidak dikenal | Tidak dikenal | Tidak dikenal | Tidak dikenal | Tindakan mana (0-4)? |
Todo
Todo
Data Waypoint dimulai dengan 2 chars "WS" dan 6 byte yang tidak diketahui, selalu = {0x01, 0x00, 0x00, 0x00, 0x50, 0x00}
Tiga struktur tersedia untuk setiap kesulitan, pada offset 641, 665 dan 689.
Isi struktur ini adalah sebagai berikut
| byte | Bytesize | isi |
|---|---|---|
| 0 | 2 byte | {0x02, 0x01} tujuan yang tidak diketahui |
| 2 | 5 byte | Waypoint Bitfield dalam urutan bit paling tidak signifikan |
| 7 | 17 byte | tidak dikenal |
Di Waypoint Bitfield, nilai bit 1 berarti bahwa titik arah diaktifkan dalam urutan dari yang terendah ke tertinggi, jadi 0 adalah perkemahan jahat (Kisah I) dll. Titik jalan pertama dalam setiap kesulitan selalu diaktifkan.
Todo
TODO (pengkodean 9-bit)
Item disimpan dalam daftar yang dijelaskan oleh header ini:
| Byte | Ukuran | Desc |
|---|---|---|
| 0 | 2 | "JM" |
| 2 | 2 | Jumlah item |
Setelah ini datang dan item. Setiap item dimulai dengan struktur 14-byte dasar. Banyak bidang dalam struktur ini tidak "selaras byte" dan dijelaskan oleh posisi dan ukuran bit mereka.
| Sedikit | Ukuran | Desc |
|---|---|---|
| 0 | 16 | "JM" (terpisah dari header daftar) |
| 16 | 4 | ? |
| 20 | 1 | Diidentifikasi |
| 21 | 6 | ? |
| 27 | 1 | Terhubung |
| 28 | 1 | ? |
| 29 | 1 | Diambil sejak Save Terakhir |
| 30 | 2 | ? |
| 32 | 1 | Telinga |
| 33 | 1 | Perlengkapan starter |
| 34 | 3 | ? |
| 37 | 1 | Kompak |
| 38 | 1 | Sangat halus |
| 39 | 1 | ? |
| 40 | 1 | Dipersonalisasi |
| 41 | 1 | ? |
| 42 | 1 | Runeword |
| 43 | 15 | ? |
| 58 | 3 | Induk |
| 61 | 4 | Lengkap |
| 65 | 4 | Kolom |
| 69 | 3 | Baris |
| 72 | 1 | ? |
| 73 | 3 | Menyimpan |
| 76 | 4 | ? |
| 80 | 24 | Ketik kode (3 huruf) |
| 108 | Data item yang diperluas |
Jika item ditandai sebagai Compact (bit 37 diatur) tidak ada informasi item yang diperpanjang akan ada dan item selesai.
Item dengan bit toko informasi yang diperluas berdasarkan informasi di header item. Misalnya, item yang ditandai sebagai Socketed akan menyimpan integer 3-bit tambahan yang mengkode berapa banyak soket yang dimiliki item tersebut.
| Sedikit | Ukuran | Desc |
|---|---|---|
| 108 | Soket | |
| Grafik khusus | ||
| Spesifik kelas | ||
| Kualitas | ||
| Mods |
Grafik khusus dilambangkan dengan satu bit, yang jika diatur berarti nomor 3-bit untuk indeks grafik mengikuti. Jika bit tidak diatur, 3-bit tidak ada.
| Sedikit | Ukuran | Desc |
|---|---|---|
| 0 | 1 | Item memiliki grafik khusus |
| 1 | 3 | Indeks grafik alternatif |
Barang kelas seperti Helm Barbarian atau Busur Amazon memiliki properti khusus khusus untuk jenis barang tersebut. Jika bit pertama kosong, 11 bit yang tersisa tidak akan ada.
| Sedikit | Ukuran | Desc |
|---|---|---|
| 0 | 1 | Item memiliki data spesifik kelas |
| 1 | 11 | Bit spesifik kelas |
Kualitas item dikodekan sebagai bilangan bulat 4-bit.
Setelah setiap item adalah daftar mod. Daftar ini adalah serangkaian pasangan nilai kunci di mana kunci adalah angka 9-bit dan nilainya tergantung pada kunci. Daftar berakhir ketika kunci 511 ( 0x1ff ) ditemukan yang semuanya 9-bit diatur.
Menggunakan file ItemStatCost.txt sebagai file CSV yang dibatasi tab Anda dapat mengekstrak kolom ID yang memetakan ke tombol 9-bit. Kolom Save Bits dan Param Bits menggambarkan seberapa besar mod.
Satu-satunya pengecualian adalah pengubah gaya Min-Max yang menggunakan baris berikutnya di CSV untuk menyimpan bagian "maks" mod. Ukuran bit dari keduanya bisa berbeda dan Anda harus merangkumnya untuk mendapatkan ukuran total.
Todo
Semua item terletak di suatu tempat dan memiliki "orang tua" yang bisa menjadi barang lain, seperti saat memasukkan permata.
| Nilai | Desc |
|---|---|
| 0 | Tersimpan |
| 1 | Lengkap |
| 2 | Sabuk |
| 4 | Kursor |
| 6 | Barang |
Untuk item yang "disimpan" integer 3-bit yang dikodekan mulai dari Bit 73 menjelaskan di mana untuk menyimpan item:
| Nilai | Desc |
|---|---|
| 1 | Inventaris |
| 4 | Kubus Horadric |
| 5 | Menyimpan |
Item yang dilengkapi menjelaskan slot mereka:
| Nilai | Slot |
|---|---|
| 1 | Helm |
| 2 | Jimat |
| 3 | Baja |
| 4 | Senjata (kanan) |
| 5 | Senjata (kiri) |
| 6 | Cincin (kanan) |
| 7 | Cincin (kiri) |
| 8 | Sabuk |
| 9 | Sepatu bot |
| 10 | Sarung tangan |
| 11 | Senjata alternatif (kanan) |
| 12 | Senjata alternatif (kiri) |