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 "
";

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") . "
"); } } 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") . "
"); } } 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") . "
"); } } $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'];