diff --git a/CharEditor.php b/CharEditor.php new file mode 100644 index 0000000..14ae461 --- /dev/null +++ b/CharEditor.php @@ -0,0 +1,134 @@ +"; + +$filePath = "D:\Diablo II\MODS\ironman-dev\save\Barb.d2s"; +$fp = fopen($filePath, "rb"); + + + +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; +} + +foreach ($qNorm as $k => $v) { + fseek($fp, $k); + $questsNorm[$k] = fread($fp, 2); +} + +foreach ($qNM as $k => $v) { + fseek($fp, $k); + $questsNM[$k] = fread($fp, 2); +} + +foreach ($qHell as $k => $v) { + fseek($fp, $k); + $questsHell[$k] = fread($fp, 2); +} + +foreach ($offsets as $k => $v) { + fseek($fp, $k); + $data[$k] = fread($fp, $v); +} + + + +$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]); + +$x = str_split(strtobits($data[168]), 8); + +$onDifficulty['Norm'] = $x[0][0]; +$onDifficulty['NM'] = $x[1][0]; +$onDifficulty['Hell'] = $x[2][0]; + +$charData['Difficulty'] = array_filter($onDifficulty); + +//$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])) . "
"); + } +} +foreach ($questsNM as $k => $v) { + $x = array_filter(str_split(strtobits($v), 8)); + if ($x[0][0]) { + $quests[] = ($qNM[$k] . " => " . (($x[0][0])) . "
"); + } +} +foreach ($questsHell as $k => $v) { + $x = array_filter(str_split(strtobits($v), 8)); + if ($x[0][0]) { + $quests[] = ($qHell[$k] . " => " . (($x[0][0])) . "
"); + } +} + +$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']; diff --git a/D2SaveFileStructure.php b/D2SaveFileStructure.php deleted file mode 100644 index 7060483..0000000 --- a/D2SaveFileStructure.php +++ /dev/null @@ -1,416 +0,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 "
";
-
-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']; diff --git a/D2SaveFileStructureData.php b/D2SaveFileStructureData.php deleted file mode 100644 index 117ba4c..0000000 --- a/D2SaveFileStructureData.php +++ /dev/null @@ -1,830 +0,0 @@ - 'Attack', -1 => 'Kick', -2 => 'Throw', -3 => 'Unsummon', -4 => 'Left Hand Throw', -5 => 'Left Hand Swing', -6 => 'Magic Arrow', -7 => 'Fire Arrow', -8 => 'Inner Sight', -9 => 'Critical Strike', -10 => 'Jab', -11 => 'Cold Arrow', -12 => 'Multiple Shot', -13 => 'Dodge', -14 => 'Power Strike', -15 => 'Poison Javelin', -16 => 'Exploding Arrow', -17 => 'Slow Missiles', -18 => 'Avoid', -19 => 'Impale', -20 => 'Lightning Bolt', -21 => 'Ice Arrow', -22 => 'Guided Arrow', -23 => 'Penetrate', -24 => 'Charged Strike', -25 => 'Plague Javelin', -26 => 'Strafe', -27 => 'Immolation Arrow', -28 => 'Decoy', -29 => 'Evade', -30 => 'Fend', -31 => 'Freezing Arrow', -32 => 'Valkyrie', -33 => 'Pierce', -34 => 'Lightning Strike', -35 => 'Lightning Fury', -36 => 'Fire Bolt', -37 => 'Warmth', -38 => 'Charged Bolt', -39 => 'Ice Bolt', -40 => 'Frozen Armor', -41 => 'Inferno', -42 => 'Static Field', -43 => 'Telekinesis', -44 => 'Frost Nova', -45 => 'Ice Blast', -46 => 'Blaze', -47 => 'Fire Ball', -48 => 'Nova', -49 => 'Lightning', -50 => 'Shiver Armor', -51 => 'Fire Wall', -52 => 'Enchant', -53 => 'Chain Lightning', -54 => 'Teleport', -55 => 'Glacial Spike', -56 => 'Meteor', -57 => 'Thunder Storm', -58 => 'Energy Shield', -59 => 'Blizzard', -60 => 'Chilling Armor', -61 => 'Fire Mastery', -62 => 'Hydra', -63 => 'Lightning Mastery', -64 => 'Frozen Orb', -65 => 'Cold Mastery', -66 => 'Amplify Damage', -67 => 'Teeth', -68 => 'Bone Armor', -69 => 'Skeleton Mastery', -70 => 'Raise Skeleton', -71 => 'Dim Vision', -72 => 'Weaken', -73 => 'Poison Dagger', -74 => 'Corpse Explosion', -75 => 'Clay Golem', -76 => 'Iron Maiden', -77 => 'Terror', -78 => 'Bone Wall', -79 => 'Golem Mastery', -80 => 'Raise Skeletal Mage', -81 => 'Confuse', -82 => 'Life Tap', -83 => 'Poison Explosion', -84 => 'Bone Spear', -85 => 'Air Elemental', -86 => 'Attract', -87 => 'Decrepify', -88 => 'Bone Prison', -89 => 'Summon Resist', -90 => 'Iron Golem', -91 => 'Lower Resist', -92 => 'Poison Nova', -93 => 'Bone Spirit', -94 => 'Fire Golem', -95 => 'Revive', -96 => 'Sacrifice', -97 => 'Smite', -98 => 'Might', -99 => 'Prayer', -100 => 'Resist Fire', -101 => 'Holy Bolt', -102 => 'Holy Fire', -103 => 'Thorns', -104 => 'Defiance', -105 => 'Resist Cold', -106 => 'Zeal', -107 => 'Charge', -108 => 'Blessed Aim', -109 => 'Cleansing', -110 => 'Resist Lightning', -111 => 'Vengeance', -112 => 'Blessed Hammer', -113 => 'Concentration', -114 => 'Holy Freeze', -115 => 'Vigor', -116 => 'Conversion', -117 => 'Holy Shield', -118 => 'Holy Shock', -119 => 'Sanctuary', -120 => 'Meditation', -121 => 'Fist of the Heavens', -122 => 'Fanaticism', -123 => 'Conviction', -124 => 'Redemption', -125 => 'Salvation', -126 => 'Bash', -127 => 'Sword Mastery', -128 => 'Axe Mastery', -129 => 'Mace Mastery', -130 => 'Howl', -131 => 'Find Potion', -132 => 'Leap', -133 => 'Double Swing', -134 => 'Pole Arm Mastery', -135 => 'Throwing Mastery', -136 => 'Spear Mastery', -137 => 'Taunt', -138 => 'Shout', -139 => 'Stun', -140 => 'Double Throw', -141 => 'Increased Stamina', -142 => 'Find Item', -143 => 'Leap Attack', -144 => 'Concentrate', -145 => 'Iron Skin', -146 => 'Battle Cry', -147 => 'Frenzy', -148 => 'Increased Speed', -149 => 'Battle Orders', -150 => 'Grim Ward', -151 => 'Whirlwind', -152 => 'Berserk', -153 => 'Natural Resistance', -154 => 'War Cry', -155 => 'Battle Command', -190 => 'Prime Ice Nova', -195 => 'Diablo Fire', -201 => 'Andariel Poison Bolt', -217 => 'Scroll of Identify', -218 => 'Tome of Identify', -219 => 'Scroll of Townportal', -220 => 'Tome of Townportal', -221 => 'Raven', -222 => 'Werewolf', -223 => 'Werewolf', -224 => 'Lycanthropy', -225 => 'Firestorm', -226 => 'Oak Sage', -227 => 'Summon Spirit Wolf', -228 => 'Werebear', -229 => 'Molten Boulder', -230 => 'Arctic Blast', -231 => 'Carrion Vine', -232 => 'Feral Rage', -233 => 'Maul', -234 => 'Fissure', -235 => 'Cyclone Armor', -236 => 'Heart of Wolverine', -237 => 'Summon Dire Wolf', -238 => 'Rabies', -239 => 'Fire Claws', -240 => 'Twister', -241 => 'Solar Creeper', -242 => 'Hunger', -243 => 'Shock Wave', -244 => 'Volcano', -245 => 'Tornado', -246 => 'Spirit of Barbs', -247 => 'Summon Grizzly', -248 => 'Fury', -249 => 'Armageddon', -250 => 'Hurricane', -251 => 'Fire Blast', -252 => 'Claw Mastery', -253 => 'Psychic Hammer', -254 => 'Tiger Strike', -255 => 'Dragon Talon', -256 => 'Shock Web', -257 => 'Blade Sentinel', -258 => 'Burst of Speed', -259 => 'Fists of Fire', -260 => 'Dragon Claw', -261 => 'Charged Bolt Sentry', -262 => 'Wake of Fire', -263 => 'Weapon Block', -264 => 'Cloak of Shadows', -265 => 'Cobra Strike', -266 => 'Blade Fury', -267 => 'Fade', -268 => 'Shadow Warrior', -269 => 'Claws of Thunder', -270 => 'Dragon Tail', -271 => 'Lightning Sentry', -272 => 'Wake of Inferno', -273 => 'Mind Blast', -274 => 'Blades of Ice', -275 => 'Dragon Flight', -276 => 'Death Sentry', -277 => 'Blade Shield', -278 => 'Venom', -279 => 'Shadow Master', -280 => 'Phoenix Strike', -316 => 'Baal Nova', -317 => 'Baal Inferno', -318 => 'Baal Cold Missiles', -350 => 'Delirium', -358 => 'Poison Bolt', -359 => 'Poison Spray', -360 => 'StartingPassive', -366 => 'Prime Ice Nova', -371 => 'Diablo Fire', -377 => 'Andariel Poison Bolt', -378 => 'Baal Nova', -379 => 'Baal Inferno', -380 => 'Baal Cold Missiles', -383 => 'Might', -384 => 'Prayer', -385 => 'Resist Fire', -386 => 'Holy Fire', -387 => 'Thorns', -388 => 'Defiance', -389 => 'Resist Cold', -390 => 'Blessed Aim', -391 => 'Cleansing', -392 => 'Resist Lightning', -393 => 'Concentration', -394 => 'Holy Freeze', -395 => 'Vigor', -396 => 'Holy Shock', -397 => 'Sanctuary', -398 => 'Meditation', -399 => 'Fanaticism', -400 => 'Conviction', -401 => 'Redemption', -402 => 'Salvation', -403 => 'Warmth', -404 => 'Warmth', -405 => 'Warmth', -406 => 'Warmth', -407 => 'Warmth', -408 => 'Warmth', -409 => 'Warmth', -410 => 'Experience Boost', -411 => 'Experience Boost', -412 => 'Experience Boost', -413 => 'Experience Boost', -414 => 'Experience Boost', -415 => 'Experience Boost', -416 => 'Experience Boost', -417 => 'Andariel Poison Bolt', -418 => 'Blizzard', -419 => 'Bone Spirit', -420 => 'Chain Lightning', -421 => 'Charged Bolt', -422 => 'Diablo Fire', -425 => 'Fissure', -426 => 'Fire Ball', -427 => 'Fire Bolt', -428 => 'Fire Blast', -429 => 'Fire Wall', -430 => 'Firestorm', -431 => 'Fist of the Heavens', -432 => 'Frost Nova', -433 => 'Frozen Orb', -434 => 'Glacial Spike', -435 => 'Ice Blast', -436 => 'Ice Bolt', -439 => 'Lightning', -440 => 'Meteor', -441 => 'Molten Boulder', -442 => 'Nova', -443 => 'Poison Nova', -448 => 'Prime Ice Nova', -453 => 'Shock Wave', -454 => 'Static Field', -455 => 'Teeth', -456 => 'Tornado', -457 => 'Twister', -461 => 'Volcano', -462 => 'Diablo Red Lightning', -463 => 'Diablo Red Lightning', -464 => 'Summon Skeleton', -465 => 'Summon Returned', -466 => 'Summon BoneWarrior', -467 => 'Summon BurningDead', -468 => 'Summon Horror', -469 => 'Summon Zombie', -470 => 'Summon HungryDead', -471 => 'Summon Ghoul', -472 => 'Summon DrownedCarcass', -473 => 'Summon PlagueBearer', -474 => 'Summon Afflicted', -475 => 'Summon Tainted', -476 => 'Summon Misshapen', -477 => 'Summon Disfigured', -478 => 'Summon Damned', -479 => 'Summon FoulCrow', -480 => 'Summon BloodHawk', -481 => 'Summon BlackRaptor', -482 => 'Summon CloudStalker', -483 => 'Summon Fallen', -484 => 'Summon Carver', -485 => 'Summon Devilkin', -486 => 'Summon DarkOne', -487 => 'Summon WarpedFallen', -488 => 'Summon Brute', -489 => 'Summon Yeti', -490 => 'Summon Crusher', -491 => 'Summon WailingBeast', -492 => 'Summon GargantuanBeast', -493 => 'Summon SandRaider', -494 => 'Summon Marauder', -495 => 'Summon Invader', -496 => 'Summon Infidel', -497 => 'Summon Assailant', -498 => 'Summon Ghost', -499 => 'Summon Wraith', -500 => 'Summon Specter', -501 => 'Summon Apparition', -502 => 'Summon DarkShape', -503 => 'Summon DarkHunter', -504 => 'Summon VileHunter', -505 => 'Summon DarkStalker', -506 => 'Summon BlackRogue', -507 => 'Summon FleshHunter', -508 => 'Summon DuneBeast', -509 => 'Summon RockDweller', -510 => 'Summon JungleHunter', -511 => 'Summon DoomApe', -512 => 'Summon TempleGuard', -513 => 'Summon MoonClan', -514 => 'Summon NightClan', -515 => 'Summon BloodClan', -516 => 'Summon HellClan', -517 => 'Summon DeathClan', -518 => 'Summon FallenShaman', -519 => 'Summon CarverShaman', -520 => 'Summon DevilkinShaman', -521 => 'Summon DarkShaman', -522 => 'Summon WarpedShaman', -523 => 'Summon QuillRat', -524 => 'Summon SpikeFiend', -525 => 'Summon ThornBeast', -526 => 'Summon RazorSpine', -527 => 'Summon JungleUrchin', -528 => 'Summon SandMaggot', -529 => 'Summon RockWorm', -530 => 'Summon Devourer', -531 => 'Summon GiantLamprey', -532 => 'Summon WorldKiller', -533 => 'Summon TombViper', -534 => 'Summon ClawViper', -535 => 'Summon Salamander', -536 => 'Summon PitViper', -537 => 'Summon SerpentMagus', -538 => 'Summon SandLeaper', -539 => 'Summon CaveLeaper', -540 => 'Summon TombCreeper', -541 => 'Summon TreeLurker', -542 => 'Summon RazorPitRaakshus', -543 => 'Summon Huntress', -544 => 'Summon SaberCat', -545 => 'Summon NightTiger', -546 => 'Summon HellCat', -547 => 'Summon Itchies', -548 => 'Summon BlackLocusts', -549 => 'Summon PlagueBugs', -550 => 'Summon HellSwarm', -551 => 'Summon DungSoldier', -552 => 'Summon SandWarrior', -553 => 'Summon Scarab', -554 => 'Summon SteelWeevil', -555 => 'Summon AlbinoRoach', -556 => 'Summon DriedCorpse', -557 => 'Summon Decayed', -558 => 'Summon Embalmed', -559 => 'Summon PreservedDead', -560 => 'Summon Cadaver', -561 => 'Summon HollowOne', -562 => 'Summon Guardian', -563 => 'Summon Unraveler', -564 => 'Summon Horadrim Ancient', -565 => 'Summon Baal Subject Mummy', -566 => 'Summon CarrionBird', -567 => 'Summon UndeadScavenger', -568 => 'Summon HellBuzzard', -569 => 'Summon WingedNightmare', -570 => 'Summon Sucker', -571 => 'Summon Feeder', -572 => 'Summon BloodHook', -573 => 'Summon BloodWing', -574 => 'Summon Gloam', -575 => 'Summon SwampGhost', -576 => 'Summon BurningSoul', -577 => 'Summon BlackSoul', -578 => 'Summon Arach', -579 => 'Summon SandFisher', -580 => 'Summon PoisonSpinner', -581 => 'Summon FlameSpider', -582 => 'Summon SpiderMagus', -583 => 'Summon ThornedHulk', -584 => 'Summon BrambleHulk', -585 => 'Summon Thrasher', -586 => 'Summon Spikefist', -587 => 'Summon GhoulLord', -588 => 'Summon NightLord', -589 => 'Summon DarkLord', -590 => 'Summon BloodLord', -591 => 'Summon Banished', -592 => 'Summon DesertWing', -593 => 'Summon Fiend', -594 => 'Summon Gloombat', -595 => 'Summon BloodDiver', -596 => 'Summon DarkFamiliar', -597 => 'Summon RatMan', -598 => 'Summon Fetish', -599 => 'Summon Flayer', -600 => 'Summon SoulKiller', -601 => 'Summon StygianDoll', -602 => 'Summon Andariel', -603 => 'Summon DarkRanger', -604 => 'Summon VileArcher', -605 => 'Summon DarkArcher', -606 => 'Summon BlackArcher', -607 => 'Summon FleshArcher', -608 => 'Summon DarkSpearwoman', -609 => 'Summon VileLancer', -610 => 'Summon DarkLancer', -611 => 'Summon BlackLancer', -612 => 'Summon FleshLancer', -613 => 'Summon SkeletonArcher', -614 => 'Summon ReturnedArcher', -615 => 'Summon BoneArcher', -616 => 'Summon BurningDeadArcher', -617 => 'Summon HorrorArcher', -618 => 'Summon SandMaggotYoung', -619 => 'Summon RockWormYoung', -620 => 'Summon DevourerYoung', -621 => 'Summon GiantLampreyYoung', -622 => 'Summon WorldKillerYoung', -623 => 'Summon Blunderbore', -624 => 'Summon Gorbelly', -625 => 'Summon Mauler', -626 => 'Summon Urdar', -627 => 'Summon SandMaggotEgg', -628 => 'Summon RockWormEgg', -629 => 'Summon DevourerEgg', -630 => 'Summon GiantLampreyEgg', -631 => 'Summon WorldKillerEgg', -632 => 'Summon FoulCrowNest', -633 => 'Summon BloodHawkNest', -634 => 'Summon BlackVultureNest', -635 => 'Summon CloudStalkerNest', -636 => 'Summon Duriel', -637 => 'Summon Undead RatMan', -638 => 'Summon Undead Fetish', -639 => 'Summon Undead Flayer', -640 => 'Summon Undead SoulKiller', -641 => 'Summon Undead StygianDoll', -642 => 'Summon Radament', -643 => 'Summon FlyingScimitar', -644 => 'Summon Zakarumite', -645 => 'Summon Faithful', -646 => 'Summon Zealot', -647 => 'Summon Sexton', -648 => 'Summon Cantor', -649 => 'Summon Heirophant', -650 => 'Summon Heirophant', -651 => 'Summon Mephisto', -652 => 'Summon Diablo', -653 => 'Summon Swamp Dweller', -654 => 'Summon Bog Creature', -655 => 'Summon Slime Prince', -656 => 'Summon Summoner', -657 => 'Summon izual', -658 => 'Summon Bloodraven', -659 => 'Summon GargoyleTrap', -660 => 'Summon ReturnedMage', -661 => 'Summon BoneMage', -662 => 'Summon BurningDeadMage', -663 => 'Summon HorrorMage', -664 => 'Summon RatManShaman', -665 => 'Summon FetishShaman', -666 => 'Summon FlayerShaman', -667 => 'Summon SoulKillerShaman', -668 => 'Summon StygianDollShaman', -669 => 'Summon SandMaggotQueen', -670 => 'Summon RockWormQueen', -671 => 'Summon DevourerQueen', -672 => 'Summon GiantLampreyQueen', -673 => 'Summon WorldKillerQueen', -674 => 'Summon ClayGolem', -675 => 'Summon BloodGolem', -676 => 'Summon IronGolem', -677 => 'Summon FireGolem', -678 => 'Summon FleshSpawner', -679 => 'Summon StygianHag', -680 => 'Summon Grotesque', -681 => 'Summon FleshBeast', -682 => 'Summon StygianDog', -683 => 'Summon GrotesqueWyrm', -684 => 'Summon Groper', -685 => 'Summon Strangler', -686 => 'Summon StormCaster', -687 => 'Summon Corpulent', -688 => 'Summon CorpseSpitter', -689 => 'Summon MawFiend', -690 => 'Summon DoomKnight', -691 => 'Summon AbyssKnight', -692 => 'Summon OblivionKnight', -693 => 'Summon QuillBear', -694 => 'Summon SpikeGiant', -695 => 'Summon ThornBrute', -696 => 'Summon RazorBeast', -697 => 'Summon GiantUrchin', -698 => 'Summon Council Member', -699 => 'Summon Council Member', -700 => 'Summon Council Member', -701 => 'Summon Turret', -702 => 'Summon Turret', -703 => 'Summon Turret', -704 => 'Summon Hydra', -705 => 'Summon Hydra', -706 => 'Summon Hydra', -707 => 'Summon Balrog', -708 => 'Summon PitLord', -709 => 'Summon VenomLord', -710 => 'Summon Griswold', -711 => 'Summon LightningSpire', -712 => 'Summon FireTower', -713 => 'Summon Slinger', -714 => 'Summon SpearCat', -715 => 'Summon NightSlinger', -716 => 'Summon HellSlinger', -717 => 'Summon ReturnedMage', -718 => 'Summon BoneMage', -719 => 'Summon BaalColdMage', -720 => 'Summon HorrorMage', -721 => 'Summon ReturnedMage', -722 => 'Summon BoneMage', -723 => 'Summon BurningDeadMage', -724 => 'Summon HorrorMage', -725 => 'Summon ReturnedMage', -726 => 'Summon BoneMage', -727 => 'Summon BurningDeadMage', -728 => 'Summon HorrorMage', -729 => 'Summon Hell Bovine', -730 => 'Summon SpearCat', -731 => 'Summon NightSlinger', -732 => 'Summon RatMan', -733 => 'Summon Fetish', -734 => 'Summon Flayer', -735 => 'Summon SoulKiller', -736 => 'Summon StygianDoll', -737 => 'Summon The Smith', -738 => 'Summon TrappedSoul', -739 => 'Summon TrappedSoul', -740 => 'Summon RatMan', -741 => 'Summon The Feature Creep', -742 => 'Summon RotWalker', -743 => 'Summon ReanimatedHorde', -744 => 'Summon ProwlingDead', -745 => 'Summon UnholyCorpse', -746 => 'Summon DefiledWarrior', -747 => 'Summon Siege Beast', -748 => 'Summon CrushBiest', -749 => 'Summon BloodBringer', -750 => 'Summon GoreBearer', -751 => 'Summon DeamonSteed', -752 => 'Summon Snow Drifter', -753 => 'Summon Abominable', -754 => 'Summon Chilled Froth', -755 => 'Summon Frozen Abyss', -756 => 'Summon FanaticMinion', -757 => 'Summon BerserkSlayer', -758 => 'Summon ConsumedIceBoar', -759 => 'Summon ConsumedFireBoar', -760 => 'Summon FrenziedHellSpawn', -761 => 'Summon FrenziedIceSpawn', -762 => 'Summon InsaneHellSpawn', -763 => 'Summon InsaneIceSpawn', -764 => 'Summon Succubusexp', -765 => 'Summon VileTemptress', -766 => 'Summon StygianHarlot', -767 => 'Summon Hell Temptress', -768 => 'Summon Blood Temptress', -769 => 'Summon Dominus', -770 => 'Summon VileWitch', -771 => 'Summon StygianFury', -772 => 'Summon Blood Witch', -773 => 'Summon Hell Witch', -774 => 'Summon OverSeer', -775 => 'Summon Lasher', -776 => 'Summon OverLord', -777 => 'Summon BloodBoss', -778 => 'Summon HellWhip', -779 => 'Summon Raakshus Portal', -780 => 'Summon Raakshus Portal', -781 => 'Summon Raakshus Portal', -782 => 'Summon Raakshus Portal', -783 => 'Summon Raakshus Portal', -784 => 'Summon Raakshus Portal', -785 => 'Summon Raakshus Portal', -786 => 'Summon Raakshus Portal', -787 => 'Summon Raakshus Imp', -788 => 'Summon Raakshus Rascal', -789 => 'Summon Raakshus Gremlin', -790 => 'Summon Raakshus Trickster', -791 => 'Summon Raakshus Sprite', -792 => 'Summon Frozen Creeper', -793 => 'Summon Frozen Terror', -794 => 'Summon Frozen Scourge', -795 => 'Summon Frozen Horror', -796 => 'Summon Frozen Scorch', -797 => 'Summon Moon Lord', -798 => 'Summon Night Lord', -799 => 'Summon Blood Lord', -800 => 'Summon Death Lord', -801 => 'Summon Hell Lord', -802 => 'Summon Death Mauler', -803 => 'Summon Death Brawler', -804 => 'Summon Death Slasher', -805 => 'Summon Death Berserker', -806 => 'Summon Death Brigadier', -807 => 'Summon Putrid Defiler', -808 => 'Summon Wretched Defiler', -809 => 'Summon Fetid Defiler', -810 => 'Summon Rancid Defiler', -811 => 'Summon Rank Defiler', -812 => 'Summon Pain Worm', -813 => 'Summon Torment Worm', -814 => 'Summon Agony Worm', -815 => 'Summon Menace Worm', -816 => 'Summon Anguish Worm', -817 => 'Summon VenomLord', -818 => 'Summon Festering Appendages', -819 => 'Summon Festering Appendages', -820 => 'Summon Festering Appendages', -821 => 'Summon Festering Appendages', -822 => 'Summon Festering Appendages', -823 => 'Summon Baal', -824 => 'Summon Stone Giant', -825 => 'Ice Golem', -826 => 'Ice Golem', -827 => 'Ice Golem', -828 => 'Ice Golem', -829 => 'Ice Golem', -830 => 'Ice Golem', -831 => 'Teeth Nova', -832 => 'Teeth Nova', -833 => 'Teeth Nova', -834 => 'Teeth Nova', -835 => 'Teeth Nova', -836 => 'Teeth Nova', -837 => 'Teeth Nova', -838 => 'Find Potion', -839 => 'Find Potion', -840 => 'Find Potion', -841 => 'Find Potion', -842 => 'Find Potion', -843 => 'Find Potion', -844 => 'Find Potion', -845 => 'Find Item', -846 => 'Find Item', -847 => 'Find Item', -848 => 'Find Item', -849 => 'Find Item', -850 => 'Find Item', -851 => 'Find Item', -852 => 'Poisonous Wildfire Nova', -853 => 'Poisonous Wildfire', -854 => 'Acid Rain', -855 => 'Poison Orb', -856 => 'Toxic Fumes', -857 => 'Djinni', -858 => 'dummy', -859 => 'dummy', -860 => 'dummy', -861 => 'dummy', -862 => 'dummy', -863 => 'dummy', -864 => 'dummy', -865 => 'dummy', -866 => 'dummy', -867 => 'Poison Arrow', -868 => 'dummy', -869 => 'dummy', -870 => 'Summon Leopard', -871 => 'Summon EarthSpirit', -872 => 'Summon Snake', -873 => 'Poison Mastery', -874 => 'Magic Mastery', -875 => 'Fire Mastery', -876 => 'Cold Mastery', -877 => 'Lightning Mastery', -878 => 'Poison Mastery', -879 => 'Magic Mastery', -880 => 'Fire Mastery', -881 => 'Cold Mastery', -882 => 'Lightning Mastery', -883 => 'Poison Mastery', -884 => 'Magic Mastery', -885 => 'Fire Mastery', -886 => 'Cold Mastery', -887 => 'Lightning Mastery', -888 => 'Poison Mastery', -889 => 'Magic Mastery', -890 => 'Fire Mastery', -891 => 'Cold Mastery', -892 => 'Lightning Mastery', -893 => 'Poison Mastery', -894 => 'Magic Mastery', -895 => 'Fire Mastery', -896 => 'Cold Mastery', -897 => 'Lightning Mastery', -898 => 'Poison Mastery', -899 => 'Magic Mastery', -900 => 'Fire Mastery', -901 => 'Cold Mastery', -902 => 'Lightning Mastery', -903 => 'Poison Mastery', -904 => 'Magic Mastery', -905 => 'dummy', -906 => 'dummy', -907 => 'dummy', -908 => 'Flying Djinni', -909 => 'dummy', -910 => 'dummy', -911 => 'Bone Golem', -912 => 'dummy', -913 => 'Summon Wild Dog', -914 => 'dummy', -915 => 'dummy', -916 => 'dummy', -917 => 'dummy', -918 => 'Lava Golem', -919 => 'dummy', -920 => 'Summon Blood Wolf', -921 => 'dummy', -922 => 'Summon Mastery', -923 => 'Summon Mastery', -924 => 'Summon Mastery', -925 => 'Summon Mastery', -926 => 'dummy', -927 => 'Summon Mastery', -928 => 'Summon Mastery', -929 => 'Green Slime', -930 => 'Purple Slime', -931 => 'Red Slime', -932 => 'Yellow Slime', -933 => 'Blue Slime', -934 => 'dummy', -935 => 'dummy', -936 => 'dummy', -937 => 'dummy', -938 => 'dummy', -939 => 'dummy', -940 => 'dummy', -941 => 'dummy', -942 => 'dummy', -943 => 'dummy', -944 => 'dummy', -945 => 'dummy', -946 => 'dummy', -947 => 'dummy', -948 => 'dummy', -949 => 'dummy', -950 => 'dummy', -951 => 'dummy', -952 => 'dummy', -953 => 'dummy', -954 => 'dummy', -955 => 'dummy', -956 => 'dummy', -957 => 'dummy', -958 => 'dummy', -959 => 'dummy', -960 => 'dummy', -961 => 'dummy', -962 => 'dummy', -963 => 'dummy' -]; - - -$class = [ - 0 => 'Amazon', - 1 => 'Sorceress', - 2 => 'Necromancer', - 3 => 'Paladin', - 4 => 'Barbarian', - 5 => 'Druid', - 6 => 'Assassin' -]; - - -$characterStatus = [ - 0 => '', - 1 => '', - 2 => 'Hardcore', - 3 => 'Died', - 4 => '', - 5 => 'Expansion', - 6 => 'Ladder', - 7 => '' - -]; \ No newline at end of file diff --git a/img/chars/Amazon.gif b/img/chars/Amazon.gif new file mode 100644 index 0000000..0a83fa3 Binary files /dev/null and b/img/chars/Amazon.gif differ diff --git a/img/chars/Amazon.webp b/img/chars/Amazon.webp new file mode 100644 index 0000000..e343164 Binary files /dev/null and b/img/chars/Amazon.webp differ diff --git a/img/chars/Assassin.gif b/img/chars/Assassin.gif new file mode 100644 index 0000000..6f081ec Binary files /dev/null and b/img/chars/Assassin.gif differ diff --git a/img/chars/Assassin.webp b/img/chars/Assassin.webp new file mode 100644 index 0000000..18e821e Binary files /dev/null and b/img/chars/Assassin.webp differ diff --git a/img/chars/Barbarian.gif b/img/chars/Barbarian.gif new file mode 100644 index 0000000..0fece2c Binary files /dev/null and b/img/chars/Barbarian.gif differ diff --git a/img/chars/Barbarian_diablo_II.webp b/img/chars/Barbarian_diablo_II.webp new file mode 100644 index 0000000..b1f19f2 Binary files /dev/null and b/img/chars/Barbarian_diablo_II.webp differ diff --git a/img/chars/Druid.gif b/img/chars/Druid.gif new file mode 100644 index 0000000..0d82ec3 Binary files /dev/null and b/img/chars/Druid.gif differ diff --git a/img/chars/Druid.webp b/img/chars/Druid.webp new file mode 100644 index 0000000..ff5ab3f Binary files /dev/null and b/img/chars/Druid.webp differ diff --git a/img/chars/Necromancer.gif b/img/chars/Necromancer.gif new file mode 100644 index 0000000..5399fe9 Binary files /dev/null and b/img/chars/Necromancer.gif differ diff --git a/img/chars/Necromancer.webp b/img/chars/Necromancer.webp new file mode 100644 index 0000000..0ca6699 Binary files /dev/null and b/img/chars/Necromancer.webp differ diff --git a/img/chars/Paladin.gif b/img/chars/Paladin.gif new file mode 100644 index 0000000..6aecddc Binary files /dev/null and b/img/chars/Paladin.gif differ diff --git a/img/chars/Paladin.webp b/img/chars/Paladin.webp new file mode 100644 index 0000000..a717da4 Binary files /dev/null and b/img/chars/Paladin.webp differ diff --git a/img/chars/Sorceress.gif b/img/chars/Sorceress.gif new file mode 100644 index 0000000..b1fe23c Binary files /dev/null and b/img/chars/Sorceress.gif differ diff --git a/img/chars/Sorceress.webp b/img/chars/Sorceress.webp new file mode 100644 index 0000000..d907e1c Binary files /dev/null and b/img/chars/Sorceress.webp differ diff --git a/index.php b/index.php index 3e0fe0b..943a65f 100755 --- a/index.php +++ b/index.php @@ -85,6 +85,11 @@ if (!isset($_SESSION['modname']) || (!file_exists(APP_DB)) || (!file_exists($_SE require_once './src/D2Files.php'; require_once './src/D2TxtParser.php'; require_once './src/D2ItemDesc.php'; + require_once './src/D2SaveFile.php'; + + $chars = new D2SaveFile(); + + $idesc = new D2ItemDesc(); $db = new D2Database(); diff --git a/scratchSpaceFile.php b/scratchSpaceFile.php deleted file mode 100644 index 7cab236..0000000 --- a/scratchSpaceFile.php +++ /dev/null @@ -1,193 +0,0 @@ - 4, - 4 => 4, - 8 => 4, - 12 => 4, - 16 => 4, - 20 => 16, - 36 => 1, - 37 => 1, - 38 => 2, - 40 => 1, - 41 => 2, - 43 => 1, - 44 => 4, - 48 => 4, - 52 => 4, - 56 => 64, - 120 => 4, - 124 => 4, - 128 => 4, - 132 => 4, - 136 => 32, - 168 => 3, - 171 => 4, - 175 => 2, - 177 => 2, - 179 => 4, - 183 => 2, - 185 => 2, - 187 => 4, - 191 => 144, - 335 => 298, - 633 => 81, - 714 => 51, -]; - -foreach ($offsets as $k => $v){ - fseek($fp, $k); - $data[$k] = fread($fp, $v); -} -echo "
";
-print_r($data);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//$sql = "SELECT code FROM uniqueitems";
-//$res = PDO_FetchAll($sql);
-//
-//
-//foreach ($res as $code){
-//    $c = $code['code'];
-//    
-//    $sql = "SELECT flippyfile FROM misc WHERE code = '$c'";    
-//    $ret = PDO_FetchOne($sql);
-//    
-//    if(empty($ret)){
-//        $sql = "SELECT flippyfile FROM armor WHERE code = '$c'";    
-//        $ret = PDO_FetchOne($sql);        
-//    }
-//    if(empty($ret)){
-//        $sql = "SELECT flippyfile FROM armor WHERE normcode = '$c'";    
-//        $ret = PDO_FetchOne($sql);        
-//    }
-//    if(empty($ret)){
-//        $sql = "SELECT flippyfile FROM armor WHERE ubercode = '$c'";    
-//        $ret = PDO_FetchOne($sql);        
-//    }
-//    if(empty($ret)){
-//        $sql = "SELECT flippyfile FROM armor WHERE ultracode = '$c'";    
-//        $ret = PDO_FetchOne($sql);        
-//    }    
-//    
-//    if(empty($ret)){
-//        $sql = "SELECT flippyfile FROM weapons WHERE code = '$c'";    
-//        $ret = PDO_FetchOne($sql);        
-//    }    
-//    if(empty($ret)){
-//        $sql = "SELECT flippyfile FROM weapons WHERE normcode = '$c'";    
-//        $ret = PDO_FetchOne($sql);        
-//    }  
-//    if(empty($ret)){
-//        $sql = "SELECT flippyfile FROM weapons WHERE ubercode = '$c'";    
-//        $ret = PDO_FetchOne($sql);        
-//    }  
-//    if(empty($ret)){
-//        $sql = "SELECT flippyfile FROM weapons WHERE ultracode = '$c'";    
-//        $ret = PDO_FetchOne($sql);        
-//    }      
-//    
-//    $return[] = $ret;
-//    
-//    echo $ret . "_uni
"; -//} - -//dump($return) -// -// - - -//$sql = "SELECT item FROM setitems"; -//$res = PDO_FetchAll($sql); -// -// -//foreach ($res as $code){ -// $c = $code['item']; -// -// -// $sql = "SELECT flippyfile FROM misc WHERE code = '$c'"; -// $ret = PDO_FetchOne($sql); -// -// if(empty($ret)){ -// $sql = "SELECT flippyfile FROM armor WHERE code = '$c'"; -// $ret = PDO_FetchOne($sql); -// } -// if(empty($ret)){ -// $sql = "SELECT flippyfile FROM armor WHERE normcode = '$c'"; -// $ret = PDO_FetchOne($sql); -// } -// if(empty($ret)){ -// $sql = "SELECT flippyfile FROM armor WHERE ubercode = '$c'"; -// $ret = PDO_FetchOne($sql); -// } -// if(empty($ret)){ -// $sql = "SELECT flippyfile FROM armor WHERE ultracode = '$c'"; -// $ret = PDO_FetchOne($sql); -// } -// -// if(empty($ret)){ -// $sql = "SELECT flippyfile FROM weapons WHERE code = '$c'"; -// $ret = PDO_FetchOne($sql); -// } -// if(empty($ret)){ -// $sql = "SELECT flippyfile FROM weapons WHERE normcode = '$c'"; -// $ret = PDO_FetchOne($sql); -// } -// if(empty($ret)){ -// $sql = "SELECT flippyfile FROM weapons WHERE ubercode = '$c'"; -// $ret = PDO_FetchOne($sql); -// } -// if(empty($ret)){ -// $sql = "SELECT flippyfile FROM weapons WHERE ultracode = '$c'"; -// $ret = PDO_FetchOne($sql); -// } -// -// $return[] = $ret; -// -// echo $ret . "_set
"; -//}; \ No newline at end of file diff --git a/src/D2Config.php b/src/D2Config.php index 9f3cd36..656e8c0 100755 --- a/src/D2Config.php +++ b/src/D2Config.php @@ -51,20 +51,25 @@ if (!empty($_POST)) { $modname = str_replace(' ', '', $_POST['modname']); $_SESSION['modname'] = $modname; $time = time(); + $savePath = ''; // write the D2Modder.db file and replace \ with \\ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { $path = rtrim($_POST['path'], "\\"); $path = str_replace("\\", "\\\\", $path); + $savePath .= $path . '\\\\save\\\\'; $tbl = $path.'\\\\data\\\\local\\\\lng\\\\eng\\\\'; $path .= '\\\\data\\\\global\\\\excel\\\\'; + + + PDO_Connect("sqlite:../D2Modder.db"); $sql = "CREATE TABLE IF NOT EXISTS D2Modder ( modname VARCHAR(255), - path VARCHAR(255), + path VARCHAR(255), tbl VARCHAR(255), lastused INT, theme INT @@ -80,6 +85,7 @@ ERROR: INVALID PATH'; // set this mod to active mod in session $_SESSION['path'] = $path; $_SESSION['tbl'] = $tbl; + $_SESSION['savePath'] = $savePath; // Don't yell at me, security is the least of my considerations atm // check modname in db $sql = "SELECT * FROM D2Modder WHERE modname=?"; diff --git a/src/D2Files.php b/src/D2Files.php index f3a5d61..3f26974 100755 --- a/src/D2Files.php +++ b/src/D2Files.php @@ -1,64 +1,78 @@ files = array_udiff($files, $filesToIgnore, 'strcasecmp'); + return $this->files; + } + + public function getSaveFiles() { + $glob = glob($_SESSION['savePath'] . '*.d2s'); + + + foreach ($glob as $g) { + $this->charFiles[] = basename($g); + } + + return $this->charFiles; + } - public function __construct() { - $filesToIgnore = [ - "aiparms.txt", - "shrines.txt", - "cubemain.txt", // cubemain is processed manually in sqlite cli tool - "misc.txt", // grew too large, process manually in processmanually function - "treasureclass.txt", - "treasureclassex.txt" - ]; - $glob = glob($_SESSION['path'].'*.txt'); - foreach ($glob as $g){ - $files[] = basename($g); - } - $this->files = array_udiff($files, $filesToIgnore, 'strcasecmp'); - return $this->files; - } } diff --git a/src/D2Functions.php b/src/D2Functions.php index ae2776b..5b0a868 100755 --- a/src/D2Functions.php +++ b/src/D2Functions.php @@ -41,20 +41,36 @@ */ function ddump($var) { - //echo "
";
-	//var_dump($var);
-	//echo "
"; + echo "
";
+	var_dump($var);
+	echo "
"; - header('Content-Type: application/json'); - echo json_encode($var, JSON_INVALID_UTF8_IGNORE | JSON_PRETTY_PRINT); + // header('Content-Type: application/json'); + // echo json_encode($var, JSON_INVALID_UTF8_IGNORE | JSON_PRETTY_PRINT); die(); } function dump($var) { - //echo "
";
-	//echo "$var";
-	//echo "
"; + echo "
";
+	echo "$var";
+	echo "
"; - header('Content-Type: application/json'); - echo json_encode($var, JSON_INVALID_UTF8_IGNORE | JSON_PRETTY_PRINT); + //header('Content-Type: application/json'); + //echo json_encode($var, JSON_INVALID_UTF8_IGNORE | JSON_PRETTY_PRINT); } + + +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; +} \ No newline at end of file diff --git a/src/D2SaveFile.php b/src/D2SaveFile.php old mode 100755 new mode 100644 index b558031..c26f5f1 --- a/src/D2SaveFile.php +++ b/src/D2SaveFile.php @@ -1,91 +1,135 @@ -path.DIRECTORY_SEPARATOR.$file, 'a+'); - $this->saveBackup($file); - fputcsv($fp, $data, "\t"); - } - - - public function saveBackup($file){ - - // if dir doesn't exist, create it - if (!is_dir($this->path.DIRECTORY_SEPARATOR."backup")) mkdir($this->path."backup", 0700); - - $oldfile = $this->path.$file; - - if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { - // set new file location to copy to (backup) - $newfile = $this->path."\\backup\\$file"; - } else { - $newfile = $this->path."backup/$file"; - } - if (!copy($oldfile, $newfile)) { - echo "Failed to create backup of $file...\n"; - } - } - - public function saveTblEnries($filename) { - $post = $_POST; - - if (!is_dir($this->path."tblEntries")) mkdir($this->path."tblEntries", 0700); - - // write for .tbl - $str = '"'.$post['index'].'"'."\t".'"'.$post['index'].'"'.PHP_EOL; - $file = $this->path."\\tblEntries\\$filename"; - file_put_contents($file, $str, FILE_APPEND); - } - - public function __construct() { - $this->path = TXT_PATH; - } - -} -?> \ No newline at end of file +getSaveFiles(); + + foreach ($files as $f) { + $filePath = $_SESSION['savePath'] . $f; + + $fp = fopen($filePath, "rb+"); + + + foreach ($sdata->qNorm as $k => $v) { + fseek($fp, $k); + $questsNorm[$k] = fread($fp, 2); + } + + foreach ($sdata->qNM as $k => $v) { + fseek($fp, $k); + $questsNM[$k] = fread($fp, 2); + } + + foreach ($sdata->qHell as $k => $v) { + fseek($fp, $k); + $questsHell[$k] = fread($fp, 2); + } + + + // read offsets here from sdata and put into $data which will be used for charData + + foreach ($sdata->offsets as $k => $v) { + fseek($fp, $k); + $data[$k] = fread($fp, $v); + } + + $charData['Identifier'] = bin2hex($data[0]); + + $charData['VersionID'] = $sdata->version[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'] = $sdata->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'] = $sdata->skills[unpack('i', $data[120])[1]]; + $charData['RightmousebuttonskillID'] = $sdata->skills[unpack('i', $data[124])[1]]; + $charData['LeftswapmousebuttonskillID'] = $sdata->skills[unpack('i', $data[128])[1]]; + $charData['RightswapmousebuttonskillID'] = $sdata->skills[unpack('i', $data[132])[1]]; + + // $charData['Charactermenuappearance'] = unpack('i', $data[136]); + + $x = str_split(strtobits($data[168]), 8); + + $onDifficulty['Norm'] = $x[0][0]; + $onDifficulty['NM'] = $x[1][0]; + $onDifficulty['Hell'] = $x[2][0]; + + $charData['Difficulty'] = array_filter($onDifficulty); + + //$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[] = ($sdata->qNorm[$k] . " => " . $x[0][0]); + } + } + foreach ($questsNM as $k => $v) { + $x = array_filter(str_split(strtobits($v), 8)); + if ($x[0][0]) { + $quests[] = ($sdata->qNM[$k] . " => " . $x[0][0]); + } + } + foreach ($questsHell as $k => $v) { + $x = array_filter(str_split(strtobits($v), 8)); + if ($x[0][0]) { + $quests[] = ($sdata->qHell[$k] . " => " . $x[0][0]); + } + } + + $charData['Quests'] = $quests; + + $charData['Waypoints'] = $data[633]; + $charData['NPCIntroductions'] = $data[714]; + + $this->charData[] = $charData; + + // ddump($charData); + + + } + + + return $this->charData; + + } + +} diff --git a/src/D2SaveFileStructureData.php b/src/D2SaveFileStructureData.php new file mode 100644 index 0000000..89ac70f --- /dev/null +++ b/src/D2SaveFileStructureData.php @@ -0,0 +1,230 @@ + 'Amazon', + 1 => 'Sorceress', + 2 => 'Necromancer', + 3 => 'Paladin', + 4 => 'Barbarian', + 5 => 'Druid', + 6 => 'Assassin' + ]; + public $characterStatus = [ + 0 => '', + 1 => '', + 2 => 'Hardcore', + 3 => 'Died', + 4 => '', + 5 => 'Expansion', + 6 => 'Ladder', + 7 => '' + ]; + public $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 + ]; + public $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', + ]; + public $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', + ]; + public $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', + ]; + + public $version = [ + 71 => "1.00 through v1.06", + 87 => "1.07 or Expansion Set v1.08", + 89 => "standard game v1.08", + 92 => "v1.09 (both the standard game and the Expansion Set.)", + 96 => "v1.10+" + ]; + + /* + Initialize Skills From Skills.txt + */ + + + public function __construct() { + $sql = " + SELECT + skills.Id, + skills.skilldesc, + skilldesc.skilldesc, + skilldesc.`str name`, + `strings`.`Key`, + strings.`String` + FROM skills, skilldesc, strings + WHERE skills.skilldesc = skilldesc.skilldesc + AND skilldesc.`str name` = strings.Key + "; + $res = PDO_FetchAll($sql); + + foreach ($res as $r) { + $this->skills[$r['Id']] = $r['String']; + } + } + +} diff --git a/src/D2SaveTXT.php b/src/D2SaveTXT.php new file mode 100755 index 0000000..fa2e45f --- /dev/null +++ b/src/D2SaveTXT.php @@ -0,0 +1,91 @@ +path.DIRECTORY_SEPARATOR.$file, 'a+'); + $this->saveBackup($file); + fputcsv($fp, $data, "\t"); + } + + + public function saveBackup($file){ + + // if dir doesn't exist, create it + if (!is_dir($this->path.DIRECTORY_SEPARATOR."backup")) mkdir($this->path."backup", 0700); + + $oldfile = $this->path.$file; + + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + // set new file location to copy to (backup) + $newfile = $this->path."\\backup\\$file"; + } else { + $newfile = $this->path."backup/$file"; + } + if (!copy($oldfile, $newfile)) { + echo "Failed to create backup of $file...\n"; + } + } + + public function saveTblEnries($filename) { + $post = $_POST; + + if (!is_dir($this->path."tblEntries")) mkdir($this->path."tblEntries", 0700); + + // write for .tbl + $str = '"'.$post['index'].'"'."\t".'"'.$post['index'].'"'.PHP_EOL; + $file = $this->path."\\tblEntries\\$filename"; + file_put_contents($file, $str, FILE_APPEND); + } + + public function __construct() { + $this->path = TXT_PATH; + } + +} +?> \ No newline at end of file diff --git a/src/header.php b/src/header.php index abe4480..e2b71b5 100755 --- a/src/header.php +++ b/src/header.php @@ -18,7 +18,7 @@ following disclaimer in the documentation and/or other materials provided with the distribution. - * This software must not be used for commercial purposes + * This software must not be used for commercial purposes * without my consent. Any sales or commercial use are prohibited * without my express knowledge and consent. @@ -43,80 +43,85 @@ ?> - section */ - require_once "head.php"; - ?> + section */ + require_once "head.php"; + ?> - -
-
-
-
+ +
+
+
+
-

-
- Active Mod: - [] -
-
-
-
-
- -
-
- -
- - -
- -
- -
- -
-
- -
-
+ + + +
+
+
+
+ +
+
+ +
+ -
\ No newline at end of file +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
\ No newline at end of file diff --git a/src/tabs/Chars.php b/src/tabs/Chars.php new file mode 100644 index 0000000..7bae38d --- /dev/null +++ b/src/tabs/Chars.php @@ -0,0 +1,124 @@ +charData); +?> + + + + +
+
+ +
+
+ + + +
+"; + } + + foreach ($chars->charData as $c) { + $tabContent .= << + +

{$c['CharacterName']}

+ + + + + + +
+ + +
    +
  • Version: {$c['VersionID']}
  • +
  • Filesize: {$c['Filesize']}
  • +
  • CharacterStatus: {$c['CharacterStatus']}
  • +
  • CharacterClass: {$c['CharacterClass']}
  • +
  • CharacterLevel: {$c['CharacterLevel']}
  • +
  • Lastplayed: {$c['Lastplayed']}
  • +
  • LeftmousebuttonskillID: {$c['LeftmousebuttonskillID']}
  • +
  • LeftmousebuttonskillID: {$c['LeftmousebuttonskillID']}
  • +
  • LeftswapmousebuttonskillID: {$c['LeftswapmousebuttonskillID']}
  • +
  • RightswapmousebuttonskillID: {$c['RightswapmousebuttonskillID']}
  • +
  • Difficulty: {$c['Difficulty']}
  • +
+
+
    +
  • {$c['Quests']}
  • + +
+
+ + +
+EOT; + } + echo $tabContent; +?> + +
diff --git a/test.php b/test.php new file mode 100644 index 0000000..b3bd31b --- /dev/null +++ b/test.php @@ -0,0 +1,28 @@ +