d2tools/D2SaveFileStructure.php
2022-05-07 00:39:13 -06:00

417 lines
9.9 KiB
PHP

<?php
error_reporting(E_ERROR | E_PARSE);
set_time_limit(-1);
ini_set('max_input_time', '-1');
ini_set('max_execution_time', '0');
session_start();
ob_start();
require_once './D2SaveFileStructureData.php';
$filePath = "D:\Diablo II\MODS\ironman-dev\save\Barb.d2s";
$fp = fopen($filePath, "rb");
$offsets = [
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
];
$questOffsetsNormal = [
345 => 2,
347 => 2,
349 => 2,
351 => 2,
353 => 2,
355 => 2,
357 => 2,
359 => 2,
361 => 2,
363 => 2,
365 => 2,
367 => 2,
369 => 2,
371 => 2,
373 => 2,
375 => 2,
377 => 2,
379 => 2,
381 => 2,
383 => 2,
385 => 2,
387 => 2,
389 => 2,
391 => 2,
393 => 2,
395 => 2,
397 => 2,
399 => 2,
401 => 2,
403 => 2,
405 => 2,
407 => 2,
409 => 2,
414 => 2,
416 => 2,
418 => 2,
420 => 2,
422 => 2,
424 => 2
];
$questOffsetsNM = [
438 => 2,
440 => 2,
442 => 2,
444 => 2,
446 => 2,
448 => 2,
450 => 2,
452 => 2,
454 => 2,
456 => 2,
458 => 2,
460 => 2,
462 => 2,
464 => 2,
466 => 2,
468 => 2,
470 => 2,
472 => 2,
474 => 2,
476 => 2,
478 => 2,
480 => 2,
482 => 2,
484 => 2,
486 => 2,
488 => 2,
490 => 2,
492 => 2,
494 => 2,
496 => 2,
498 => 2,
500 => 2,
502 => 2,
504 => 2,
506 => 2,
508 => 2,
510 => 2,
512 => 2,
514 => 2,
];
$questOffsetsHell = [
528 => 2,
530 => 2,
532 => 2,
534 => 2,
536 => 2,
538 => 2,
540 => 2,
542 => 2,
544 => 2,
546 => 2,
548 => 2,
550 => 2,
552 => 2,
554 => 2,
556 => 2,
558 => 2,
560 => 2,
562 => 2,
564 => 2,
566 => 2,
568 => 2,
570 => 2,
572 => 2,
574 => 2,
576 => 2,
578 => 2,
580 => 2,
582 => 2,
584 => 2,
586 => 2,
588 => 2,
590 => 2,
592 => 2,
594 => 2,
596 => 2,
598 => 2,
600 => 2,
602 => 2,
604 => 2,
];
$qNorm = [
345 => 'introWarriv',
347 => 'DenOfEvil',
349 => 'SistersBurialGrounds',
351 => 'ToolsOfTheTrade',
353 => 'TheSearchForCain',
355 => 'TheForgottenTower',
357 => 'SistersToTheSlaughter',
359 => 'traveledToAct2',
361 => 'introJerhyn',
363 => 'RadamentsLair',
365 => 'TheHoradricStaff',
367 => 'TaintedSun',
369 => 'ArcaneSanctuary',
371 => 'TheSummoner',
373 => 'TheSevenTombs',
375 => 'traveledToAct3',
377 => 'introHratli',
379 => 'LamEsensTome',
381 => 'KhalimsWill',
383 => 'BladeOfTheOldReligion',
385 => 'TheGoldenBird',
387 => 'TheBlackenedTemple',
389 => 'TheGuardian',
391 => 'traveledtoAct4',
393 => 'introToAct4',
395 => 'TheFallenAngel',
397 => 'TerrorsEnd',
399 => 'HellForge',
401 => 'empty',
403 => 'empty',
405 => 'empty',
407 => 'traveledToAct5',
409 => 'completedTerrorsEnd',
414 => 'SiegeOnHarrogath',
416 => 'RescueOnMountArreat',
418 => 'PrisonOfIce',
420 => 'BetrayalOfHarrogath',
422 => 'RiteOfPassage',
424 => 'EveOfDestruction',
];
$qNM = [
438 => 'introWarrivNM',
440 => 'DenOfEvilNM',
442 => 'SistersBurialGroundsNM',
444 => 'ToolsOfTheTradeNM',
446 => 'TheSearchForCainNM',
448 => 'TheForgottenTowerNM',
450 => 'SistersToTheSlaughterNM',
452 => 'traveledToAct2NM',
454 => 'introJerhynNM',
456 => 'RadamentsLairNM',
458 => 'TheHoradricStaffNM',
460 => 'TaintedSunNM',
462 => 'ArcaneSanctuaryNM',
464 => 'TheSummonerNM',
466 => 'TheSevenTombsNM',
468 => 'traveledToAct3NM',
470 => 'introHratliNM',
472 => 'LamEsensTomeNM',
474 => 'KhalimsWillNM',
476 => 'BladeOfTheOldReligionNM',
478 => 'TheGoldenBirdNM',
480 => 'TheBlackenedTempleNM',
482 => 'TheGuardianNM',
484 => 'traveledtoAct4NM',
486 => 'introToAct4NM',
488 => 'TheFallenAngelNM',
490 => 'TerrorsEndNM',
492 => 'HellForgeNM',
494 => 'emptyNM',
496 => 'emptyNM',
498 => 'emptyNM',
500 => 'traveledToAct5NM',
502 => 'completedTerrorsEndNM',
504 => 'SiegeOnHarrogathNM',
506 => 'RescueOnMountArreatNM',
508 => 'PrisonOfIceNM',
510 => 'BetrayalOfHarrogathNM',
512 => 'RiteOfPassageNM',
514 => 'EveOfDestructionNM',
];
$qHell = [
528 => 'introWarrivHell',
530 => 'DenOfEvilHell',
532 => 'SistersBurialGroundsHell',
534 => 'ToolsOfTheTradeHell',
536 => 'TheSearchForCainHell',
538 => 'TheForgottenTowerHell',
540 => 'SistersToTheSlaughterHell',
542 => 'traveledToAct2Hell',
544 => 'introJerhynHell',
546 => 'RadamentsLairHell',
548 => 'TheHoradricStaffHell',
550 => 'TaintedSunHell',
552 => 'ArcaneSanctuaryHell',
554 => 'TheSummonerHell',
556 => 'TheSevenTombsHell',
558 => 'traveledToAct3Hell',
560 => 'introHratliHell',
562 => 'LamEsensTomeHell',
564 => 'KhalimsWillHell',
566 => 'BladeOfTheOldReligionHell',
568 => 'TheGoldenBirdHell',
570 => 'TheBlackenedTempleHell',
572 => 'TheGuardianHell',
574 => 'traveledtoAct4Hell',
576 => 'introToAct4Hell',
578 => 'TheFallenAngelHell',
580 => 'TerrorsEndHell',
582 => 'HellForgeHell',
584 => 'emptyHell',
586 => 'emptyHell',
588 => 'emptyHell',
590 => 'traveledToAct5Hell',
592 => 'completedTerrorsEndHell',
594 => 'SiegeOnHarrogathHell',
596 => 'RescueOnMountArreatHell',
598 => 'PrisonOfIceHell',
600 => 'BetrayalOfHarrogathHell',
602 => 'RiteOfPassageHell',
604 => 'EveOfDestructionHell',
];
echo "<pre>";
function isCompleted($questData) {
return (($questData[0] >> 0) & 1) > 0;
}
foreach ($questOffsetsNormal as $k => $v) {
fseek($fp, $k);
$questsNorm[$k] = fread($fp, $v);
}
foreach ($questOffsetsNM as $k => $v) {
fseek($fp, $k);
$questsNM[$k] = fread($fp, $v);
}
foreach ($questOffsetsHell as $k => $v) {
fseek($fp, $k);
$questsHell[$k] = fread($fp, $v);
}
foreach ($offsets as $k => $v) {
fseek($fp, $k);
$data[$k] = fread($fp, $v);
}
function strtobits(string $str): string {
$ret = "";
for ($i = 0; $i < strlen($str); ++$i) {
$ord = ord($str[$i]);
for ($bitnum = 7; $bitnum >= 0; --$bitnum) {
if ($ord & (1 << $bitnum)) {
$ret .= "1";
} else {
$ret .= "0";
}
}
}
return $ret;
}
$charData['Identifier'] = bin2hex($data[0]);
$charData['VersionID'] = unpack('l', $data[4])[1]; // 96 is v1.10+ - check out
$charData['Filesize'] = round(unpack('l', $data[8])[1] / 1024, 2) . " KB"; // 1.41 KB (1,447 bytes) - checks out
// $charData['Checksum'] = bin2hex($data['12']);
// $charData['Activeweapon'] = unpack('l', $data['16']);
$charData['CharacterName'] = ($data[20]);
$charData['CharacterStatus'] = array_filter(str_split(strtobits($data[36])));
foreach ($charData['CharacterStatus'] as $k => $v) {
$str .= ($characterStatus[$k]) . " ";
}
$charData['CharacterStatus'] = $str;
// $charData['Characterprogression'] = bindec($data['37']);
$charData['CharacterClass'] = $class[unpack('C', $data[40])[1]];
$charData['CharacterLevel'] = unpack('C', $data[43])[1];
$charData['Lastplayed'] = gmdate("Y-m-d\TH:i:s\Z", unpack('I', $data[48])[0]);
// $charData['Assignedskills'] = (unpack('i16', $data['56']));
$charData['LeftmousebuttonskillID'] = $skills[unpack('i', $data[120])[1]];
$charData['RightmousebuttonskillID'] = $skills[unpack('i', $data[124])[1]];
$charData['LeftswapmousebuttonskillID'] = $skills[unpack('i', $data[128])[1]];
$charData['RightswapmousebuttonskillID'] = $skills[unpack('i', $data[132])[1]];
// $charData['Charactermenuappearance'] = unpack('i', $data[136]);
$charData['Difficulty'] = strtobits($data[168]);
// $charData['MapID'] = $data['171'];
//$charData['Mercenarydead'] = unpack('i', $data['177']);
//$charData['MercenaryID'] = $data['179'];
//$charData['MercenaryNameID'] = $data['183'];
//$charData['Mercenarytype'] = $data['185'];
//$charData['Mercenaryexperience'] = $data['187'];
foreach ($questsNorm as $k => $v) {
$x = array_filter(str_split(strtobits($v), 8));
if ($x[0][0]) {
$quests[] = ($qNorm[$k] . " => " . (($x[0][0]) ? "Completed" : "Not Started") . "<br>");
}
}
foreach ($questsNM as $k => $v) {
$x = array_filter(str_split(strtobits($v), 8));
if ($x[0][0]) {
$quests[] = ($qNM[$k] . " => " . (($x[0][0]) ? "Completed" : "Not Started") . "<br>");
}
}
foreach ($questsHell as $k => $v) {
$x = array_filter(str_split(strtobits($v), 8));
if ($x[0][0]) {
$quests[] = ($qHell[$k] . " => " . (($x[0][0]) ? "Completed" : "Not Started") . "<br>");
}
}
$charData['Quests'] = $quests;
$charData['Waypoints'] = $data[633];
$charData['NPCIntroductions'] = $data[714];
print_r($charData);
// $charData['Unknown'] = $data['44'];
// $charData['Unknown'] = $data['52'];
// $charData['Unknown'] = $data['175'];
// $charData['Unknown'] = $data['191'];