Diablo II almacena su personaje de juego en el disco como un archivo .d2s. Este es un formato de archivo binario que codifica todas las estadísticas, elementos, nombre y otros datos.
Los enteros se almacenan en Little Endian Byte Order, que es el orden de bytes nativo en una arquitectura X86 en la que se basa Diablo II.
Cada archivo .d2s comienza con un encabezado de 765 bytes, después de lo cual los datos son de longitud variable.
| Maleficio | Byte | Longitud | Descomunal |
|---|---|---|---|
| 0x00 | 0 | 4 | Firma (0xAA55AA55) |
| 0x04 | 4 | 4 | ID de versión |
| 0x08 | 8 | 4 | Tamaño de archivo |
| 0x0c | 12 | 4 | Suma de verificación |
| 0x10 | 16 | 4 | Arma activa |
| 0x14 | 20 | 16 | Nombre del personaje |
| 0x24 | 36 | 1 | Estado del personaje |
| 0x25 | 37 | 1 | Progresión del personaje |
| 0x26 | 38 | 2 | ? |
| 0x28 | 40 | 1 | Clase de carácter |
| 0x29 | 41 | 2 | ? |
| 0x2b | 43 | 1 | Nivel |
| 0x2c | 44 | 4 | ? |
| 0x30 | 48 | 4 | Tiempo |
| 0x34 | 52 | 4 | ? |
| 0x38 | 56 | 64 | Teclas de acceso |
| 0x78 | 120 | 4 | Ratón izquierdo |
| 0x7c | 124 | 4 | Ratón derecho |
| 0x80 | 128 | 4 | Mouse izquierdo (interruptor de arma) |
| 0x84 | 132 | 4 | Ratón derecho (interruptor de armas) |
| 0x88 | 136 | 32 | Apariencia del menú de personajes |
| 0xa8 | 168 | 3 | Dificultad |
| 0xab | 171 | 4 | Mapa |
| 0xaf | 175 | 2 | ? |
| 0xb1 | 177 | 2 | Merc muerto? |
| 0xb3 | 179 | 4 | ¿Semilla Merc? |
| 0xb7 | 183 | 2 | Merc Name ID |
| 0xb9 | 185 | 2 | Tipo Merc |
| 0xbb | 187 | 4 | Experiencia Merc |
| 0xbf | 191 | 144 | ? |
| 0x14f | 335 | 298 | Búsqueda |
| 0x279 | 633 | 81 | Punto de referencia |
| 0x2ca | 714 | 51 | NPC |
| 0x2fd | 765 | Estadísticas | |
| Elementos |
Versión de archivo. Se conocen los siguientes valores:
71 es 1.00 a v1.0687 es 1.07 o conjunto de expansión V1.0889 es el juego estándar v1.0892 es V1.09 (tanto el juego estándar como el conjunto de expansión).96 es V1.10+Para calcular la suma de verificación, establece el valor de TI en los datos .d2s para ser cero e iterar a través de todos los bytes en los datos que calculan una suma de verificación de 32 bits:
sum = ( sum << 1 ) + data [ i ];Fuente: #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 ) ) ;
} ) ;Fuente: 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 ]
}
}Si la suma de verificación no es válida, Diablo II no abrirá el archivo Guardar.
HACER
Los nombres de los personajes son una tienda como una matriz de 16 caracteres que contienen una cadena nula terminada acolchada con 0x00 para los bytes restantes. Los caracteres se almacenan como ASCII de 8 bits, pero recuerde que válido debe seguir estas reglas:
- ) o bajo subscore ( _ )Este es un campo de 8 bits:
| Poco | Descomunal |
|---|---|
| 0 | ? |
| 1 | ? |
| 2 | Duro |
| 3 | Fallecido |
| 4 | ? |
| 5 | Expansión |
| 6 | ? |
| 7 | ? |
HACER
| IDENTIFICACIÓN | Clase |
|---|---|
| 0 | Amazonas |
| 1 | Hechicera |
| 2 | Nigromante |
| 3 | Paladín |
| 4 | Bárbaro |
| 5 | druida |
| 6 | Asesino |
Este valor de nivel es visible solo en la pantalla Seleccionar caracteres y debe ser el mismo que en la sección Estadísticas.
HACER
32 Estructura de bytes que define cómo se ve el personaje en el menú no cambia el aspecto en el juego
3 bytes de datos que indican cuál de las tres dificultades ha desbloqueado el personaje. Cada byte es representativo de una de las dificultades. En este orden: Normal, Pesadilla e Infierno. Cada byte está un campo de bits estructurado como este:
| 7 | 6 | 5 | 4 | 3 | 2, 1, 0 |
|---|---|---|---|---|---|
| ¿Activo? | Desconocido | Desconocido | Desconocido | Desconocido | ¿Qué acto (0-4)? |
HACER
HACER
Los datos de Waypoint comienzan con 2 caracteres "WS" y 6 bytes desconocidos, siempre = {0x01, 0x00, 0x00, 0x00, 0x50, 0x00}
Hay tres estructuras para cada dificultad, en las compensaciones 641, 665 y 689.
Los contenidos de esta estructura son los siguientes
| byte | byte | contenido |
|---|---|---|
| 0 | 2 bytes | {0x02, 0x01} Propósito desconocido |
| 2 | 5 bytes | Waypoint bitfield en orden de bit menos significativo |
| 7 | 17 bytes | desconocido |
En Waypoint Bitfield, un valor de 1 de 1 significa que el waypoint está habilitado en un orden de más bajo a más alto, por lo que 0 es un campamento deshonesto (acto I), etc. El primer punto de referencia en cada dificultad siempre se activa.
HACER
TODO (codificación de 9 bits)
Los elementos se almacenan en las listas descritas por este encabezado:
| Byte | Tamaño | Descomunal |
|---|---|---|
| 0 | 2 | "JM" |
| 2 | 2 | Recuento de artículos |
Después de esto viene n ítems. Cada elemento comienza con una estructura básica de 14 bytes. Muchos campos en esta estructura no están "alineados por los bytes" y se describen por su posición y tamaños de bits.
| Poco | Tamaño | Descomunal |
|---|---|---|
| 0 | 16 | "JM" (separado del encabezado de la lista) |
| 16 | 4 | ? |
| 20 | 1 | Identificado |
| 21 | 6 | ? |
| 27 | 1 | Enchufado |
| 28 | 1 | ? |
| 29 | 1 | Recogido desde el último salvamento |
| 30 | 2 | ? |
| 32 | 1 | Oreja |
| 33 | 1 | Equipo de arranque |
| 34 | 3 | ? |
| 37 | 1 | Compacto |
| 38 | 1 | Etéreo |
| 39 | 1 | ? |
| 40 | 1 | Personalizado |
| 41 | 1 | ? |
| 42 | 1 | Raneño |
| 43 | 15 | ? |
| 58 | 3 | Padre |
| 61 | 4 | Equipado |
| 65 | 4 | Columna |
| 69 | 3 | Fila |
| 72 | 1 | ? |
| 73 | 3 | Alijo |
| 76 | 4 | ? |
| 80 | 24 | Tipo de código (3 letras) |
| 108 | Datos de elementos extendidos |
Si el elemento está marcado como Compact (se establece el bit 37) no existirá información extendida del elemento y el elemento está finalizado.
Los elementos con información extendida almacenan bits basados en información en el encabezado del artículo. Por ejemplo, un elemento marcado como Socketed almacenará un entero adicional de 3 bits que codificará cuántos enchufes tiene el artículo.
| Poco | Tamaño | Descomunal |
|---|---|---|
| 108 | Bocadillos | |
| Gráficos personalizados | ||
| Clase específica | ||
| Calidad | ||
| Modos |
Los gráficos personalizados se denotan por un solo bit, que si se establece significa que sigue un número de 3 bits para el índice gráfico. Si el bit no está configurado, los 3 bits no están presentes.
| Poco | Tamaño | Descomunal |
|---|---|---|
| 0 | 1 | El elemento tiene gráficos personalizados |
| 1 | 3 | Índice gráfico alternativo |
Los artículos de clase como Barbarian Helms o Amazon Bows tienen propiedades especiales específicas para ese tipo de elementos. Si el primer bit está vacío, los 11 bits restantes no estarán presentes.
| Poco | Tamaño | Descomunal |
|---|---|---|
| 0 | 1 | El elemento tiene datos específicos de clase |
| 1 | 11 | Bits específicos de la clase |
La calidad del artículo está codificada como un entero de 4 bits.
Después de cada elemento hay una lista de modificaciones. La lista es una serie de pares de valor clave donde la clave es un número de 9 bits y el valor depende de la clave. La lista finaliza cuando se encuentra la tecla 511 ( 0x1ff ), que se establecen los 9 bits.
Usando el archivo ItemStatCost.txt como un archivo CSV delimitado por pestañas, puede extraer la columna ID que se mapea a las teclas de 9 bits. Las columnas Save Bits y Param Bits describen qué tan grande es el mod.
La única excepción son los modificadores de estilo min-max que usan la siguiente fila en el CSV para almacenar la parte "máxima" del mod. Los tamaños de bits de estos dos pueden ser diferentes y debe sumarlos para obtener el tamaño total.
HACER
Todos los artículos están ubicados en algún lugar y tienen un "padre" que puede ser otro artículo, como al insertar una joya.
| Valor | Descomunal |
|---|---|
| 0 | Almacenado |
| 1 | Equipado |
| 2 | Cinturón |
| 4 | Cursor |
| 6 | Artículo |
Para los elementos que se "almacenan" un entero de 3 bits codificado a partir del bit 73 describe dónde almacenar el artículo:
| Valor | Descomunal |
|---|---|
| 1 | Inventario |
| 4 | Cubo horadric |
| 5 | Alijo |
Los elementos equipados describen su ranura:
| Valor | Ranura |
|---|---|
| 1 | Casco |
| 2 | Amuleto |
| 3 | Armadura |
| 4 | Arma (derecha) |
| 5 | Arma (izquierda) |
| 6 | Anillo (a la derecha) |
| 7 | Anillo (izquierda) |
| 8 | Cinturón |
| 9 | Botas |
| 10 | Guantes |
| 11 | Arma alternativa (derecha) |
| 12 | Arma alternativa (izquierda) |