Diablo II хранит вашего игрового персонажа на диске в качестве файла .d2s. Это двоичный формат файла, который кодирует все статистики, элементы, имя и другие части данных.
Целые числа хранятся в порядке Little Endian Byte, который является местным порядок байта на x86 Architecture Diablo II.
Каждый файл .d2s начинается с заголовка 765 байтов, после чего данные имеют переменную длину.
| Гекс | Байт | Длина | Посягательство |
|---|---|---|---|
| 0x00 | 0 | 4 | Подпись (0xAA55AA55) |
| 0x04 | 4 | 4 | Идентификатор версии |
| 0x08 | 8 | 4 | Размер файла |
| 0x0c | 12 | 4 | Контрольная сумма |
| 0x10 | 16 | 4 | Активное оружие |
| 0x14 | 20 | 16 | Имя персонажа |
| 0x24 | 36 | 1 | Статус персонажа |
| 0x25 | 37 | 1 | Прогресс персонажа |
| 0x26 | 38 | 2 | ? |
| 0x28 | 40 | 1 | Персонаж класс |
| 0x29 | 41 | 2 | ? |
| 0x2b | 43 | 1 | Уровень |
| 0x2c | 44 | 4 | ? |
| 0x30 | 48 | 4 | Время |
| 0x34 | 52 | 4 | ? |
| 0x38 | 56 | 64 | Горячие клавиши |
| 0x78 | 120 | 4 | Оставил мышь |
| 0x7c | 124 | 4 | Правая мышь |
| 0x80 | 128 | 4 | Левая мышь (выключатель оружия) |
| 0x84 | 132 | 4 | Правая мышь (выключатель оружия) |
| 0x88 | 136 | 32 | Появление меню персонажа |
| 0xa8 | 168 | 3 | Сложность |
| 0xab | 171 | 4 | Карта |
| 0xaf | 175 | 2 | ? |
| 0xb1 | 177 | 2 | Merc Dead? |
| 0xb3 | 179 | 4 | Merc Seed? |
| 0xb7 | 183 | 2 | Идентификатор имени Merc |
| 0xb9 | 185 | 2 | Merc Type |
| 0xbb | 187 | 4 | Merc Experience |
| 0xbf | 191 | 144 | ? |
| 0x14f | 335 | 298 | Квест |
| 0x279 | 633 | 81 | Путевая точка |
| 0x2ca | 714 | 51 | Npc |
| 0x2fd | 765 | Статистика | |
| Предметы |
Файл версия. Следующие значения известны:
71 - с 1,00 до v1.0687 - 1,07 или набор расширения v1.0889 - стандартная игра v1.0892 - v1.09 (как стандартная игра, так и набор расширения.)96 - v1.10+Чтобы вычислить контрольную сумму, установите значение его в данных .D2S, чтобы быть нулевым и обработать все байты в данных, рассчитывающих 32-разрядную контрольную сумму:
sum = ( sum << 1 ) + data [ i ];Источник: #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 ) ) ;
} ) ;Источник: 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 ]
}
}Если контрольная сумма недействительна, Diablo II не откроет файл сохранения.
Тодо
Имена символов хранятся в виде массива из 16 символов, которые содержат нулевую заканчированную строку, мягкую с 0x00 для оставшихся байтов. Персонажи хранятся как 8-битный ASCII, но помните, что действительный должен следовать этим правилам:
- ) или подчеркивания ( _ )Это 8-битное поле:
| Кусочек | Посягательство |
|---|---|
| 0 | ? |
| 1 | ? |
| 2 | Хардкор |
| 3 | Умер |
| 4 | ? |
| 5 | Расширение |
| 6 | ? |
| 7 | ? |
Тодо
| ИДЕНТИФИКАТОР | Сорт |
|---|---|
| 0 | Амазонка |
| 1 | Колдунья |
| 2 | Некромант |
| 3 | Паладин |
| 4 | Варвар |
| 5 | Друид |
| 6 | Убийца |
Это значение уровня видно только на экране выбора символов и должно быть таким же, как и в разделе «Статистики».
Тодо
Структура 32 байта, которая определяет, как выглядит персонаж в меню, не меняет игру в игре
3 байта данных, которые указывают, какие из трех трудностей, которые персонаж разблокировал. Каждый байт представляет одну из трудностей. В этом порядке: нормальный, кошмар и ад. Каждый байт немного структурирован как это:
| 7 | 6 | 5 | 4 | 3 | 2, 1, 0 |
|---|---|---|---|---|---|
| Активный? | Неизвестный | Неизвестный | Неизвестный | Неизвестный | Какой акт (0-4)? |
Тодо
Тодо
Данные путевой точки начинаются с 2 Chars "WS" и 6 неизвестных байтов, всегда = {0x01, 0x00, 0x00, 0x00, 0x50, 0x00}
Для каждой сложности существуют три структуры, по при смещениям 641, 665 и 689.
Содержимое этой структуры следующее
| байт | байтзизировать | содержимое |
|---|---|---|
| 0 | 2 байта | {0x02, 0x01} Неизвестная цель |
| 2 | 5 байтов | Waypoint Bitfield в порядке наименее значительного бита |
| 7 | 17 байтов | неизвестный |
В Bitfield Waypoint битовое значение 1 означает, что емкость включена в порядок от наименьшего до самых высоких, поэтому 0 - это лагерь из мошенничества (ACT I) и т. Д. Первая точка для каждой сложности всегда активируется.
Тодо
Todo (9-битная кодировка)
Элементы хранятся в списках, описанных этим заголовком:
| Байт | Размер | Посягательство |
|---|---|---|
| 0 | 2 | "JM" |
| 2 | 2 | Подсчет предметов |
После этого наступают n предметов. Каждый элемент начинается с базовой 14-байтовой структуры. Многие поля в этой структуре не «выровнены» и описаны и описаны их положением и размерами.
| Кусочек | Размер | Посягательство |
|---|---|---|
| 0 | 16 | "JM" (отдельно от заголовка списка) |
| 16 | 4 | ? |
| 20 | 1 | Идентифицировано |
| 21 | 6 | ? |
| 27 | 1 | Гнездо |
| 28 | 1 | ? |
| 29 | 1 | Поднят с момента последнего спасения |
| 30 | 2 | ? |
| 32 | 1 | Ухо |
| 33 | 1 | Стартовое снаряжение |
| 34 | 3 | ? |
| 37 | 1 | Компакт |
| 38 | 1 | Эфирный |
| 39 | 1 | ? |
| 40 | 1 | Персонализированный |
| 41 | 1 | ? |
| 42 | 1 | Runeword |
| 43 | 15 | ? |
| 58 | 3 | Родительский |
| 61 | 4 | Оснащен |
| 65 | 4 | Столбец |
| 69 | 3 | Ряд |
| 72 | 1 | ? |
| 73 | 3 | Хранение |
| 76 | 4 | ? |
| 80 | 24 | Тип кода (3 буквы) |
| 108 | Данные расширенного элемента |
Если элемент помечен как Compact (бит 37 установлен), информации об расширенном элементе не будет, и элемент завершен.
Элементы с битами расширенной информационной магазина на основе информации в заголовке элемента. Например, предмет, помеченный как Socketed хранит дополнительную 3-битную целочисленную кодировку, сколько сокетов имеет.
| Кусочек | Размер | Посягательство |
|---|---|---|
| 108 | Гнезда | |
| Пользовательская графика | ||
| Специфический класс | ||
| Качество | ||
| Моды |
Пользовательская графика обозначается одним битом, который, если установить, означает 3-битный номер для графического индекса. Если бит не установлен, 3-битные не присутствуют.
| Кусочек | Размер | Посягательство |
|---|---|---|
| 0 | 1 | У элемента есть пользовательская графика |
| 1 | 3 | Альтернативный графический индекс |
Классы, такие как варварские шлеры или луки Amazon, имеют особые свойства, относящиеся к таким предметам. Если первый бит пуст, оставшиеся 11 бит не будут присутствовать.
| Кусочек | Размер | Посягательство |
|---|---|---|
| 0 | 1 | У элемента есть данные, конкретные |
| 1 | 11 | Класс конкретные биты |
Качество товара кодируется как 4-битное целое число.
После каждого элемента список модов. Список представляет собой серию пар значений ключей, где ключ представляет собой 9-битное число, а значение зависит от ключа. Список заканчивается, когда найден ключ 511 ( 0x1ff ), который установлен на 9 бит.
Используя файл ItemStatCost.txt в качестве файла CSV, определяемого вкладками, вы можете извлечь столбец ID , который отображает в 9-битные клавиши. Столбцы Save Bits и Param Bits описывающие, насколько велик мод.
Единственным исключением являются модификаторы стиля MIN-MAX, которые используют следующую строку в CSV для хранения «максимальной» части мода. Размеры битов этих двух могут быть разными, и вы должны суммировать их, чтобы получить общий размер.
Тодо
Все предметы расположены где -то и имеют «родитель», который может быть еще одним предметом, например, когда вставка драгоценного камня.
| Ценить | Посягательство |
|---|---|
| 0 | Хранится |
| 1 | Оснащен |
| 2 | Пояс |
| 4 | Курсор |
| 6 | Элемент |
Для предметов, которые «хранятся» 3-битный целочисленный кодированный, начиная с бита 73, описывает, где хранить элемент:
| Ценить | Посягательство |
|---|---|
| 1 | Инвентарь |
| 4 | Горадрический куб |
| 5 | Хранение |
Предметы, которые оснащены, описывают их слот:
| Ценить | Слот |
|---|---|
| 1 | Шлем |
| 2 | Амулет |
| 3 | Доспехи |
| 4 | Оружие (справа) |
| 5 | Оружие (слева) |
| 6 | Кольцо (справа) |
| 7 | Кольцо (слева) |
| 8 | Пояс |
| 9 | Сапоги |
| 10 | Перчатки |
| 11 | Альтернативное оружие (справа) |
| 12 | Альтернативное оружие (слева) |