"; $objects = array(); for( $z=0; $z < 10; $z++ ) for( $a=0; $a < 3; $a++ ) { echoObjects( $objects, $object_layout_index, $object_bank, $z, $a ); } foreach($objects as $index=>$object ) { echo binhex(chr($index)).' '; } return; } $colors = array(); $blockcolors = array(); $map = array(); $tiles = array(); $tilelocs = array(); $blocks = array(); // Getting level data fseek( $rom, $map_index + 2*$zone ); $map_zone_index = getWord($rom); fseek( $rom, $map_zone_index + 2*$act ); $level_address = getWord($rom); fseek( $rom, $level_address); $block_bank = getByte($rom); $block_address = getWord($rom) + $block_bank * 0x4000 - 0x8000; $map_bank = getByte($rom); $map_address = getWord($rom) + $map_bank * 0x4000 - 0x8000; $level_width = getWord($rom); $level_height = getByte($rom)/4; if( $level_width == 0 ) { echo "Level garbled - width $level_width
"; return; } fseek( $rom, $object_layout_index + 2*$zone ); $object_layout_zone_index = getWord($rom) + $object_bank - 0x8000; fseek( $rom, $object_layout_zone_index + 2*$act ); $object_layout_address = getWord($rom) + $object_bank - 0x8000; if( $game == 'chaos' ) { loadSCMap($map_address); } elseif( $game == 's2gg' ) { loadS2Map($map_address); } else { loadSTTMap($map_address); } $auto_level_height = sizeof($map) / $level_width; if( $level_height == 0 || ($auto_level_height < $level_height) || $game == 's2gg' ) { $level_height = floor($auto_level_height/4) * 4; // chaos ss1/intro have 0 height. why? who knows. } fseek( $rom, $tile_index + $zone*21 + $act*7 ); if( $game == 'stt' && $zone == 0x0A ) { // Yes, it's ugly, but the game does this itself, // by checking for game mode 0x11 (time attack). fseek( $rom, 0x6B743 ); // All it does is move the offset to the previous level... // why didn't they just change the offsets for that level? // Who knows. } $tile_bank = getByte($rom); $unknown = getWord($rom); $tile_address = getWord($rom) + $tile_bank * 0x4000 - 0x8000; fseek( $rom, $tile_address ); $unknown = getWord($rom); $tile_num = getWord($rom); $guideloc = getWord($rom) + $tile_address; $tileloc = $tile_address + 6; $image = imagecreatetruecolor( $level_width*32, $level_height*32 ); $blockimage = imagecreatetruecolor( 32*256, 32 ); // Creating the palette fseek( $rom, $palette_id_address + $zone*6 + $act*2 ); // No hax this time, the game actually does this. $palette_id_bg = getByte($rom); $palette_id_obj = getByte($rom); fseek( $rom, $palette_address + $palette_id_bg * 0x20 ); for( $c=0; $c<32; $c++ ) { if( $c == 16 ) fseek( $rom, $palette_address + $palette_id_obj * 0x20 ); $color = ord(fread( $rom, 1 )); $color2 = ord(fread( $rom, 1 )); $red = 16 * ($color % 16); $green = 16 * floor($color / 16); $blue = 16 * ($color2 % 16); if( $_GET['gray'] == 1 ) { $red = 16*($c%16); $green = 16*($c%16); $blue = 16*($c%16); } $colors[$c] = imagecolorallocate( $image, $red, $green, $blue ); $blockcolors[$c] = imagecolorallocate( $blockimage, $red, $green, $blue ); } // Loading tiles $p = 3; $guidebyte = 0; for( $t=$tileloadat; $t<$tileloadat+0x100; $t++ ) { $data = array(); $strings = array(); $p++; if( $p == 4 ) { fseek( $rom, $guideloc++ ); $guidebyte = getByte( $rom ); $p = 0; } $guide = bittest( $guidebyte, 2*$p ) + 2* bittest( $guidebyte, 2*$p+1 ); $tilelocs[$t] = dechex($tileloc).':'.$guide; if( $guide == 0 ) { $tiles[$t] = ''; } if( $guide == 1 ) { fseek( $rom, $tileloc ); $tiles[$t] = fread( $rom, 32 ); $tileloc += 32; } if( $guide == 2 ) { $tiles[$t] = decodeTile( $tileloc ); } if( $guide == 3 ) { $tiles[$t] = decodeTile( $tileloc ); $ix = 0; for( $b=7; $b>0; $b-- ) { $tiles[$t][$ix+2] = $tiles[$t][$ix+0] ^ $tiles[$t][$ix+2]; $tiles[$t][$ix+3] = $tiles[$t][$ix+1] ^ $tiles[$t][$ix+3]; $tiles[$t][$ix+18] = $tiles[$t][$ix+16] ^ $tiles[$t][$ix+18]; $tiles[$t][$ix+19] = $tiles[$t][$ix+17] ^ $tiles[$t][$ix+19]; $ix += 2; } } } // Loading tile blocks for( $b = 0; $b < 256; $b++) { $blocks[$b] = array(); fseek( $rom, $block_address + 2*$b ); $loc = ord(fread($rom,1)) + ord(fread($rom,1))*256; fseek( $rom, $block_bank * 0x4000 - 0x8000 + $loc ); for( $i = 0; $i < 16; $i++) { $t = ord( fread( $rom, 1 ) ); $d = ord( fread( $rom, 1 ) ); $blocks[$b][$i] = $d * 256 + $t; } } // Caching the blocks in a second image for( $b = 0; $b < 256; $b++ ) { createBlock( $blockimage, $b, $b*32,0); } if( $_GET['blocks'] == 1 ) { // Show blocks for( $t=$tileloadat; $t<$tileloadat + 0x100; $t++ ) { displayTile( $image, $tiles[$t], 0, (($t-$tileloadat) % 128)*8, floor(($t-$tileloadat) / 128) * 8 ); } $b = 0; for( $c = 0; $c < 16; $c++ ) for( $r = 0; $r < 16; $r++ ) { displayBlock( $image, $b, $c*64,$r*32+32); imagestring( $image, 1, $c*64+36, $r*32+14+32, dechex($b), $colors[15] ); $b++; } } elseif( isset( $_GET['tiles'] ) ) { // Show tiles $t = 192; for( $i=0; $i<8; $i++ ) for( $j=0; $j<8; $j++ ) { imagestring( $image, 1, $j*100 + 20, $i*24, ($tilelocs[$t]), $colors[15] ); imagestring( $image, 1, $j*100 + 20, $i*24 + 10, ($tilelocs[$t+1]), $colors[15] ); imagestring( $image, 1, $j*100 + 60, $i*24, ($tilelocs[$t+2]), $colors[15] ); imagestring( $image, 1, $j*100 + 60, $i*24 + 10, ($tilelocs[$t+3]), $colors[15] ); displayTile( $image, $tiles[$t+0], 0, $j*100+0, $i*24 ); displayTile( $image, $tiles[$t+1], 0, $j*100+0, $i*24 +8 ); displayTile( $image, $tiles[$t+2], 0, $j*100+8, $i*24 ); displayTile( $image, $tiles[$t+3], 0, $j*100+8, $i*24 +8 ); $t += 4; } } else { // Show map $m = 0; for( $r = 0; $r < $level_height; $r++ ) for( $c = 0; $c < $level_width; $c++ ) { displayBlock( $image, $map[$m++], $c*32,$r*32); } } // Add objects fseek( $rom, $object_layout_address ); do { $num = getByte($rom); if( $num == 0xFF ) continue; $r = getWord($rom) - 0x100; $c = getWord($rom) - 0x100; $unk = getWord($rom); $unk = getWord($rom); imagestring( $image, 1, $r, $c, strtoupper(dechex($num)), $colors[15] ); } while( $num != 0xFF ); // Output if( strlen( $outputfile ) ) { imagepng( $image, $outputfile ); } else { header('Content-type: image/png'); imagepng($image); } } //////////////////////////////////////////////////////// // Functions function decodeTile( &$data_address ) { global $rom; fseek( $rom, $data_address ); $dataflags = fread( $rom, 4 ); $data_address += 4; $data = ''; for( $i=0; $i<32; $i++ ) { $b = $i % 8; $c = (int) ($i/8); if( ord($dataflags[$c]) & pow(2,$b) ) { $data .= fread( $rom, 1 ); $data_address++; } else { $data .= chr(0); } } return $data; } function binhex( $data ) { $str = ''; for( $i=0; $i0; $i-- ) { $map[$m++] = $map[$m2++]; } } } } } function getByte( &$source ) { return ord( fread( $source,1 ) ); } function getWord( &$source ) { return getByte( $source ) + getByte( $source ) * 0x100; } function echoObjects( &$objects, $object_layout_index, $object_bank, $zone, $act ) { global $rom; fseek( $rom, $object_layout_index + 2*$zone ); $object_layout_zone_index = getWord($rom) + $object_bank - 0x8000; fseek( $rom, $object_layout_zone_index + 2*$act ); $object_layout_address = getWord($rom) + $object_bank - 0x8000; fseek( $rom, $object_layout_address ); $o = 0; do { $num = getByte($rom); if( $num == 0xFF ) continue; echo binhex( chr($num)).":"; echo binhex( fread($rom,2) ).' '; echo binhex( fread($rom,2) ).' '; echo binhex( fread($rom,2) ).' '; echo binhex( fread($rom,2) ).' '; echo "
"; $o++; $objects[$num] = ''; } while( $num != 0xFF && $o < 100 ); echo "-- $zone-$act ".dechex($object_layout_zone_index)." ".dechex($object_layout_address)." : $o
"; } ?>