Diablo II는 게임 문자를 디스크에 .d2s 파일로 저장합니다. 이것은 모든 통계, 항목, 이름 및 기타 데이터를 인코딩하는 이진 파일 형식입니다.
정수는 Little Endian 바이트 순서로 저장되며, 이는 X86 아키텍처 디아블로 II의 기본 바이트 순서입니다.
각 .D2S 파일은 765 바이트 헤더로 시작하고 그 후 데이터는 가변 길이입니다.
| 마녀 | 바이트 | 길이 | DESC |
|---|---|---|---|
| 0x00 | 0 | 4 | 서명 (0xAA55AA55) |
| 0x04 | 4 | 4 | 버전 ID |
| 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 Name ID |
| 0xB9 | 185 | 2 | Merc 유형 |
| 0xBB | 187 | 4 | Merc Experience |
| 0xBF | 191 | 144 | ? |
| 0x14f | 335 | 298 | 탐구 |
| 0x279 | 633 | 81 | 웨이 포인트 |
| 0x2ca | 714 | 51 | NPC |
| 0x2fd | 765 | 통계 | |
| 항목 |
파일 버전. 다음 값이 알려져 있습니다.
71 1.00에서 v1.06입니다87 은 1.07 또는 확장 세트 v1.08입니다89 는 표준 게임 v1.08입니다92 는 v1.09입니다 (표준 게임과 확장 세트 모두).96 v1.10+입니다체크섬을 계산하려면 .d2s 데이터의 값을 0으로 설정하고 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는 저장 파일을 열지 않습니다.
TODO
문자 이름은 나머지 바이트에 대해 0x00 으로 패딩 된 널 종료 문자열을 포함하는 16 자 배열로 저장됩니다. 문자는 8 비트 ASCII로 저장되지만 유효한 규칙을 따라야합니다.
- ) 또는 밑줄 ( _ )을 포함 할 수 있습니다.이것은 8 비트 필드입니다.
| 조금 | DESC |
|---|---|
| 0 | ? |
| 1 | ? |
| 2 | 하드 코어 |
| 3 | 죽었다 |
| 4 | ? |
| 5 | 확장 |
| 6 | ? |
| 7 | ? |
TODO
| ID | 수업 |
|---|---|
| 0 | 아마존 |
| 1 | 마법사 |
| 2 | 점쟁이 |
| 3 | 무협가 |
| 4 | 야만인 |
| 5 | 드루이드 |
| 6 | 암살자 |
이 레벨 값은 문자 선택 화면에서만 볼 수 있으며 통계 섹션에서는 이와 동일해야합니다.
TODO
메뉴에서 캐릭터가 어떻게 보이는지 정의하는 32 바이트 구조는 게임 내 모양이 변경되지 않습니다.
캐릭터가 잠금 해제 한 세 가지 어려움 중 어느 것을 나타내는 3 바이트의 데이터. 각 바이트는 어려움 중 하나를 대표합니다. 이 순서대로 : 정상, 악몽, 지옥. 각 바이트는 다음과 같이 구성된 비트 필드입니다.
| 7 | 6 | 5 | 4 | 3 | 2, 1, 0 |
|---|---|---|---|---|---|
| 활동적인? | 알려지지 않은 | 알려지지 않은 | 알려지지 않은 | 알려지지 않은 | 어떤 행동 (0-4)? |
TODO
TODO
웨이 포인트 데이터는 2 chars "ws"와 6 개의 알려지지 않은 바이트, 항상 = {0x01, 0x00, 0x00, 0x50, 0x00}로 시작합니다.
오프셋 641, 665 및 689에서 각 난이도에 대해 세 가지 구조가 제자리에 있습니다.
이 구조의 내용은 다음과 같습니다
| 바이트 | 바이트 크기 | 내용물 |
|---|---|---|
| 0 | 2 바이트 | {0x02, 0x01} 알 수없는 목적 |
| 2 | 5 바이트 | 웨이 포인트 비트 필드는 가장 유의미한 비트입니다 |
| 7 | 17 바이트 | 알려지지 않은 |
웨이 포인트 비트 필드에서 비트 값 1은 웨이 포인트가 활성화되어 있음을 의미합니다. 0은 가장 낮은 곳에서 가장 높은 순서 가므로 0은 Rogue Encampment (ACT I) 등입니다. 각 난이도의 첫 번째 웨이 포인트는 항상 활성화됩니다.
TODO
TODO (9 비트 인코딩)
항목은이 헤더로 설명 된 목록에 저장됩니다.
| 바이트 | 크기 | DESC |
|---|---|---|
| 0 | 2 | "JM" |
| 2 | 2 | 항목 수 |
이 후에는 n 항목이 있습니다. 각 항목은 기본 14 바이트 구조로 시작합니다. 이 구조의 많은 필드는 "바이트 정렬"이 아니며 비트 위치와 크기로 설명됩니다.
| 조금 | 크기 | DESC |
|---|---|---|
| 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 | 런 워드 |
| 43 | 15 | ? |
| 58 | 3 | 조상 |
| 61 | 4 | 갖추게 하는 |
| 65 | 4 | 열 |
| 69 | 3 | 열 |
| 72 | 1 | ? |
| 73 | 3 | 숨기는 장소 |
| 76 | 4 | ? |
| 80 | 24 | 코드 유형 (3 글자) |
| 108 | 확장 된 항목 데이터 |
항목이 Compact 으로 표시되면 (비트 37이 설정 됨) 확장 된 항목 정보가없고 항목이 완료됩니다.
항목 헤더의 정보를 기반으로 확장 된 정보 저장 비트가있는 항목. 예를 들어, Socketed 으로 표시된 항목은 항목의 소켓 수를 인코딩하는 추가 3 비트 정수를 저장합니다.
| 조금 | 크기 | DESC |
|---|---|---|
| 108 | 소켓 | |
| 맞춤형 그래픽 | ||
| 클래스 특이 적 | ||
| 품질 | ||
| 모드 |
사용자 정의 그래픽은 단일 비트로 표시되며, 세트는 그래픽 인덱스의 3 비트 번호를 의미합니다. 비트가 설정되지 않으면 3 비트가 없습니다.
| 조금 | 크기 | DESC |
|---|---|---|
| 0 | 1 | 항목에는 사용자 정의 그래픽이 있습니다 |
| 1 | 3 | 대체 그래픽 색인 |
Barbarian Helms 또는 Amazon Bows와 같은 클래스 항목에는 이러한 종류의 품목에 맞는 특수 부동산이 있습니다. 첫 번째 비트가 비어 있으면 나머지 11 비트가 없습니다.
| 조금 | 크기 | DESC |
|---|---|---|
| 0 | 1 | 항목에는 클래스 특정 데이터가 있습니다 |
| 1 | 11 | 클래스 별 비트 |
품목 품질은 4 비트 정수로 인코딩됩니다.
각 항목이 개조 목록이면. 목록은 키가 9 비트 번호이고 값은 키에 따라 다릅니다. 목록은 키 511 ( 0x1ff )이 발견되면 9 비트가 모두 설정되어 있습니다.
파일 ItemStatCost.txt 파일을 탭으로 표시된 CSV 파일로 사용하면 9 비트 키에 맵핑되는 ID 열을 추출 할 수 있습니다. 열은 Save Bits 하고 Param Bits 모드가 얼마나 큰지를 설명합니다.
유일한 예외는 CSV의 다음 행을 사용하여 모드의 "최대"부분을 저장하는 Min-Max 스타일 수정 자입니다. 이 두 가지의 비트 크기는 다를 수 있으며 총 크기를 얻기 위해 합산해야합니다.
TODO
모든 품목은 어딘가에 위치하고 있으며 보석을 삽입 할 때와 같은 다른 품목이 될 수있는 "부모"가 있습니다.
| 값 | DESC |
|---|---|
| 0 | 저장 |
| 1 | 갖추게 하는 |
| 2 | 벨트 |
| 4 | 커서 |
| 6 | 목 |
비트 73에서 시작하여 인코딩 된 3 비트 정수 "저장"항목의 경우 항목을 보관할 위치를 설명합니다.
| 값 | DESC |
|---|---|
| 1 | 목록 |
| 4 | 호 라드릭 큐브 |
| 5 | 숨기는 장소 |
장착 된 항목은 슬롯을 설명합니다.
| 값 | 슬롯 |
|---|---|
| 1 | 헬멧 |
| 2 | 부적 |
| 3 | 갑옷 |
| 4 | 무기 (오른쪽) |
| 5 | 무기 (왼쪽) |
| 6 | 반지 (오른쪽) |
| 7 | 링 (왼쪽) |
| 8 | 벨트 |
| 9 | 부츠 |
| 10 | 장갑 |
| 11 | 대체 무기 (오른쪽) |
| 12 | 대체 무기 (왼쪽) |