apple2_castle_wolfenstein_m.../inspector.html

399 lines
10 KiB
HTML
Raw Normal View History

2021-10-07 18:32:31 +00:00
<html>
<head>
<script>
"use strict";
var gFile,
gData,
2021-10-08 05:16:12 +00:00
gCatalog,
2021-10-07 18:32:31 +00:00
gCAT_TRK,
gCAT_SEC,
gContext,
2021-10-08 05:16:12 +00:00
gFont;
2021-10-07 18:32:31 +00:00
2021-10-08 05:16:12 +00:00
const
MAX_DISK_SIZE = 143360,
2021-10-07 18:32:31 +00:00
FILE_TYPE_TEXT = 0x00,
FILE_TYPE_INTEGER = 0x01,
FILE_TYPE_APPLESOFT = 0x02,
FILE_TYPE_BINARY = 0x04,
FILE_TYPE_S = 0x08,
FILE_TYPE_RELOC = 0x10,
FILE_TYPE_C = 0x20,
FILE_TYPE_D = 0x40,
2021-10-08 05:16:12 +00:00
FILE_TYPES = [ 'T', 'I', 'A', 'B', 'S', 'R', 'C', 'D' ];
2021-10-07 18:32:31 +00:00
console.log( "Loading..." );
// --- Utility ---
// Optional: width
// Optional: character to pad left with, such as '0'; will default to space
function padLeft( text, width, c )
{
if( !width ) return "";
if( !c ) c = ' ';
return ("" + new Array( width ).join( c ) + text).slice( -width );
}
function makeUnsigned( n )
{
return ((n + 256) & 0xFF)|0;
}
// @param FILE_TYPE_*
function makeFileType( fileType )
{
var type = 0, raw = fileType;
2021-10-08 05:16:12 +00:00
2021-10-07 18:32:31 +00:00
while (raw > 0)
{
type++;
raw >>= 1;
}
2021-10-08 05:16:12 +00:00
2021-10-07 18:32:31 +00:00
return FILE_TYPES[ type ];
}
2021-10-08 05:16:12 +00:00
function parseInt16( low, high )
{
var n = makeUnsigned( low )
n += makeUnsigned( high ) * 256;
return n;
}
2021-10-07 18:32:31 +00:00
function parseName( catalogEntry )
{
var name = '', c, MAX_FILENAME = 30;
2021-10-08 05:16:12 +00:00
2021-10-07 18:32:31 +00:00
for( var i = 0; i < MAX_FILENAME; i++ )
{
// 0x00: FTOC Track
// 0x01: FTOC Sector
// 0x02: Type
// 0x03: Filename
c = makeUnsigned( catalogEntry[ 0x3 + i ] ) & 0x7F;
name += String.fromCharCode( c );
}
name = name.trim();
return name;
}
2021-10-08 05:16:12 +00:00
// --- Disk Utility ---
2021-10-07 18:32:31 +00:00
function readTrackSectorBytes( track, sector, offset, length )
{
2021-10-08 05:16:12 +00:00
if (offset === undefined) offset = 0; // read entir sector
if (length === undefined) length = 256; // if not specified
2021-10-07 18:32:31 +00:00
if (track > 34) alert( "Track > 34" );
if (sector > 16) alert( "Sector > 16" );
if (offset >256) alert( "Offset > 256" );
2021-10-08 05:16:12 +00:00
if (length > MAX_DISK_SIZE) alert( "Size > " + MAX_DISK_SIZE );
2021-10-07 18:32:31 +00:00
if( gData )
{
var begin = track*16*256 + 256*sector + offset,
end = begin + length,
buffer = gData.slice( begin, end );
return new Int8Array( buffer );
}
return null;
}
2021-10-08 05:16:12 +00:00
// --- DOS3.3 Meta Utility ---
// @return array of blocks
function readFTOC( track, sector )
{
var ftoc = [],
next_trk = track,
next_sec = sector,
i, trk, sec, raw, block;
do
{
raw = readTrackSectorBytes( next_trk, next_sec, 0x00, 0x100 );
next_trk = raw[ 0x01 ]; // next FTOC track
next_sec = raw[ 0x02 ]; // next FTOC sector
for( i = 0x0C; i < 0x100; i += 2 )
{
trk = raw[ i+0 ];
sec = raw[ i+1 ];
if( !trk )
break;
block = trk*16 + sec;
ftoc.push( block );
}
} while (next_trk > 0);
return ftoc;
}
function readVTOC()
{
var VTOC_TRK = 0x11,
vTOC_SEC = 0x00,
TRK_SEC;
TRK_SEC = readTrackSectorBytes( VTOC_TRK, vTOC_SEC, 1, 2 );
gCAT_TRK = TRK_SEC[0]; // $11
gCAT_SEC = TRK_SEC[1]; // $0C
}
2021-10-07 18:32:31 +00:00
function readCatalogEntry( i )
{
2021-10-08 05:16:12 +00:00
var MAX_ENTRY_LENGTH = 0x23,
2021-10-07 18:32:31 +00:00
MAX_ENTRIES_PER_SECTOR = 7;
// Catalog $11,C .. $11,1
if( !gCAT_TRK || !gCAT_SEC )
return null;
// Search directory entries
var div6 = (i / MAX_ENTRIES_PER_SECTOR)|0,
mod6 = (i % MAX_ENTRIES_PER_SECTOR) ,
offset = MAX_ENTRY_LENGTH*mod6 + 0xB,
sector = gCAT_SEC - div6;
if (sector < 1)
return null;
// 0x00: FTOC Track
// 0x01: FTOC Sector
// 0x02: Type
// 0x03: Filename
// 0x21: Length Low
// 0x22: Length Hi
2021-10-08 05:16:12 +00:00
var raw = readTrackSectorBytes( gCAT_TRK, sector, offset, 35 ),
2021-10-07 18:32:31 +00:00
entry = {},
type = makeUnsigned( raw[0x02] );
2021-10-08 05:16:12 +00:00
entry.track = parseInt ( raw[0x00] );
entry.sector = parseInt ( raw[0x01] );
entry.locked = (type >= 128)|0;
entry.file_type = type & 0x7f;
entry.type = makeFileType( type & 0x7F );
entry.name = parseName ( raw );
entry.length = parseInt16 ( raw[0x21], raw[0x22] );
2021-10-07 18:32:31 +00:00
return entry;
}
2021-10-08 05:16:12 +00:00
// --- DOS 3.3 File Utility ---
function readBinaryAddressLength( track, sector )
{
var ftoc = readFTOC( track, sector );
var data = readBlock( ftoc[0] );
var address = parseInt16( data[0], data[1] );
var size = parseInt16( data[2], data[3] );
return { address: address, size: size };
}
function readBlock( block )
{
var trk = (block / 16)|0,
sec = (block % 16)|0;
return readTrackSectorBytes( trk, sec );
}
function readCatalog()
{
var entry, i = 0, LOCKED = [ ' ', '*' ], entries = [], text = (new Array( 40 ).join('-')) + '\n', totalSize = 0, start, length;
do
{
entry = readCatalogEntry( i++ );
if( entry && entry.track )
{
text += LOCKED[ entry.locked ] + entry.type + ' ' + padLeft( entry.length, 5, '0' ) + ' ' + padLeft( entry.name, 30 );
var temp;
switch( entry.file_type )
{
case FILE_TYPE_APPLESOFT: start = 0x0801; length = 0; break;
case FILE_TYPE_BINARY : temp = readBinaryAddressLength( entry.track, entry.sector ); start = temp.address; length = temp.size; break;
default: break;
}
text += '\n';
totalSize += entry.length;
entries.push( entry );
}
} while (entry !== null)
text += (new Array( 40 ).join('-')) + '\n';
text += 'Total Files: ' + entries.length + ', Sectors Used: ' + totalSize;
console.log( text );
gCatalog = entries;
return entries;
}
function readFile( name )
{
var i, file, result = {}, data = null, ftoc;
if( !gCatalog )
return null;
for( i = 0; i < gCatalog.length; i++ )
{
file = gCatalog[i];
if (name === file.name)
break;
else
file = null;
}
if( file )
{
ftoc = readFTOC( file.track, file.sector );
data = readFileData( ftoc, file );
}
return data;
}
// isBinary if 4 byte prefix should be stripped
function readFileData( ftoc, file )
{
var iBlock, sector, nBlocks = ftoc.length, data = new Array( nBlocks * 256 ), offset, temp;
for( iBlock = 0; iBlock < nBlocks; iBlock++ )
{
sector = readBlock( ftoc[ iBlock ] );
offset = iBlock*256;
for( var b = 0; b < 256; b++ )
data[ offset + b ] = sector[ b ];
}
var address = 0, length = data.length;
if (file.file_type === FILE_TYPE_BINARY)
{
address = parseInt16( data[0], data[1] );
length = parseInt16( data[2], data[3] );
data = data.slice( 4, 4 + length );
}
return { data: data, address: address, length: length };
}
2021-10-07 18:32:31 +00:00
// --- Implementation ---
/*
Version 1
---------------------------------------
*A 00006 ^HELLO
*I 00002 APPLESOFT
*B 00034 PIX
*B 00034 PICEX
*B 00065 SEKTOR
*B 00047 ^VOCAB
*B 00006 ^CHARSET
B 00064 CASTLE
B 00064 BACKUP
*T 00007 ^TEXT
*B 00020 @INIT
*B 00024 @WOLF
*B 00024 ^THINGS
---------------------------------------
Total Files: 13, Sectors Used: 397
2021-10-08 05:16:12 +00:00
Version 1
^HELLO , A$0801, L44 (L$002C)
APPLESOFT, A$0801, L39 (L$0027)
PIX , A$2000, L$2000
PICEX , A$2000, L$2000
SEKTOR , A$4004, L$3FF0
^VOCAB , A$17DA, L$2D63
^CHARSET , A$4400, L$0400
CASTLE , A$4004, L$3DFC
BACKUP , A$4004, L$3DFC
^TEXT , A$0000, L$????
@INIT , A$0880, L$12BE
@WOLF , A$0810, L$16EB
^THINGS , A$4800, L$1638
Version 2:
^TEXT , A$0000, L$???? <-- (shorter: 5 sectors vs 7 sectors)
PICEX , A$2000, L$2000
PIX , A$2000, L$2000
SEKTOR , A$4004, L$3FF0
^VOCAB , A$17DA, L$2D63
^CHARSET , A$4400, L$0400
CASTLE , A$4004, L$3DFC
BACKUP , A$4004, L$3DFC
@INIT , A$0880, L$1432 <-- LONGER : $1432 vs $12BE
@WOLF , A$0810, L$161C <-- SHORTER: $161C vs $16EB
^THINGS , A$4800, L$1638 <-- SHORTER: $1638 vs $16EB
^RAM , A$0810, L$0167 <-- NEW
*/
2021-10-07 18:32:31 +00:00
function main()
{
console.log( " Done" );
if (window.File && window.FileList && window.FileReader)
{
document.getElementById('id_select_dsk').addEventListener('change', onSelectFile, false);
}
2021-10-08 05:16:12 +00:00
var canvas = document.getElementById( "canvas" );
gContext = canvas.getContext( "2d" );
2021-10-07 18:32:31 +00:00
// gContext.drawImage( image, x, y );
}
// ProgressEvent
// .target = FileReader
// .target.result;
function onLoadFile( e )
{
2021-10-08 05:16:12 +00:00
if (e.loaded == MAX_DISK_SIZE) // .dsk is < 144K
2021-10-07 18:32:31 +00:00
{
gFile = e.target;
gData = e.target.result;
// Read VTOC to find CATALOG
2021-10-08 05:16:12 +00:00
readVTOC();
readCatalog();
gFont = readFile( '^CHARSET' );
2021-10-07 18:32:31 +00:00
}
}
function onSelectFile( e )
{
var iFile, tFile, aFile = e.target.files, nFile = aFile.length, reader;
e.stopPropagation();
e.preventDefault();
for( iFile = 0; iFile < nFile; iFile++ )
{
tFile = aFile[ iFile ];
2021-10-08 05:16:12 +00:00
if( tFile.size > MAX_DISK_SIZE )
2021-10-07 18:32:31 +00:00
{
2021-10-08 05:16:12 +00:00
alert( ".dsk images must be <= " + MAX_DISK_SIZE );
2021-10-07 18:32:31 +00:00
break;
}
reader = new FileReader();
reader.onload = onLoadFile;
reader.readAsArrayBuffer( tFile );
}
}
</script>
</head>
<body onload='main();'>
<input type="file" id='id_select_dsk' style='width:100%'>
<br>
<canvas id='canvas' width='560px' height='460px'>
</body>
</html>