diff --git a/.eslintrc.json b/.eslintrc.json index c5c60c8..c1a12c1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -26,5 +26,18 @@ "parserOptions": { "sourceType": "module" }, - "extends": "eslint:recommended" + "extends": "eslint:recommended", + "overrides": [ + { + "files": [ "bin/*", "webpack.config.js" ], + "rules": { + "no-console": 0 + }, + "env": { + "node": true, + "jquery": false, + "browser": false + } + } + ] } diff --git a/bin/dsk2json b/bin/dsk2json new file mode 100755 index 0000000..866c2b4 --- /dev/null +++ b/bin/dsk2json @@ -0,0 +1,133 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const argv = require('yargs').argv; + +function numToString(num) { + let result = ''; + for (let idx = 0; idx < 4; idx++) { + result += String.fromCharCode(num & 0xff); + num >>= 8; + } + return result; +} + +function read2MG(fileData) { + const dv = new DataView(fileData.buffer); + + // Magic + const magic = dv.getUint32(0, true); + console.error('Magic', numToString(magic)); + + // Creator + const creator = dv.getUint32(4, true); + console.error('Creator:', numToString(creator)); + + // Header Length + const headerLength = dv.getUint16(8, true); + console.error('Header length:', headerLength); + + // Version + const version = dv.getUint16(10, true); + console.error('Version:', version); + + // Image format + const format = dv.getUint32(12, true); + const type = format === 0 ? 'dsk' : 'po'; + console.error('Format:', format, type); + + // Flags + const flags = dv.getUint32(16, true); + const readOnly = flags & 0x80000000 ? true : undefined; + console.error('Flags:', flags.toString(2), readOnly); + + return { + diskData: fileData.slice(headerLength), + type: type, + readOnly: readOnly + }; +} + +function readTracks(type, diskData) { + const tracks = []; + + if (type === 'nib') { + let start = 0; + let end = 0x1A00; + while (start < diskData.length) { + const trackData = diskData.slice(start, end); + start += 0x1A00; + end += 0x1A00; + + tracks.push(trackData.toString('base64')); + } + } else { + let start = 0; + let end = 0x100; + const numSectors = type === 'd13' ? 13 : 16; + for (let track = 0; track < 35; track++) { + const sectors = []; + for (let sector = 0; sector < numSectors; sector++) { + const sectorData = diskData.slice(start, end); + start += 0x100; + end += 0x100; + + sectors.push(sectorData.toString('base64')); + } + tracks.push(sectors); + } + } + return tracks; +} + +const fileName = argv._[0]; + + +let readOnly = argv.r || argv.readOnly ? true : undefined; +const name = argv.n || argv.name; +const category = argv.c || argv.category; +const disk = argv.d || argv.disk; +const e = argv.e ? true : undefined; + +if (!name || !category || !fileName || argv.h || argv.help) { + console.error('dsk2json [-c category] [-n name] [-t type] imagefile'); + process.exit(0); +} + +let type = 'dsk'; +const match = /\.([a-z0-9]+)$/.exec(fileName); +if (match && match.length > 1) { + type = match[1]; +} + +type = argv.t || argv.type || type; + +fs.readFile(fileName, (err, fileData) => { + if (err) { + console.error('Unable to read disk image'); + process.exit(1); + } + let diskData; + + if (type === '2mg') { + ({diskData, readOnly, type} = read2MG(fileData)); + } else { + diskData = fileData; + } + + const entry = { + name, + type, + category, + encoding: 'base64', + readOnly, + disk, + '2e': e, + data: readTracks(type, diskData) + }; + + Object.keys(entry).forEach((key) => { + if (entry[key] === undefined) delete entry[key]; + }); + console.log(JSON.stringify(entry, null, 4)); +}); diff --git a/bin/index b/bin/index new file mode 100755 index 0000000..caf472f --- /dev/null +++ b/bin/index @@ -0,0 +1,32 @@ +#!/usr/bin/env node + + +const fs = require('fs'); +const path = require('path'); + +const diskPath = path.resolve('json/disks'); +const dir = fs.readdirSync(diskPath); + +const index = []; + +for (const fileName of dir.sort()) { + if (/\.json$/.test(fileName)) { + const json = fs.readFileSync(path.resolve(diskPath, fileName)); + const data = JSON.parse(json); + if (data.private) { continue; } + + const entry = { + filename: `json/disks/${fileName}`, + e: data['2e'], + name: data.name, + disk: data.disk, + category: data.category, + }; + index.push(entry); + } +} + +fs.writeFileSync( + path.resolve(diskPath, 'index.js'), + `disk_index = ${JSON.stringify(index, null, 2)};` +); diff --git a/json/disks/index.js b/json/disks/index.js index 81a9082..829d74f 100644 --- a/json/disks/index.js +++ b/json/disks/index.js @@ -1,12 +1,12 @@ disk_index = [ { - "filename": "json/disks/dos33master.json", - "name": "DOS 3.3 Master", - "category": "System" + "filename": "json/disks/dos33master.json", + "name": "DOS 3.3 Master", + "category": "System" }, { - "filename": "json/disks/prodos.json", - "name": "ProDOS", - "category": "System" + "filename": "json/disks/prodos.json", + "name": "ProDOS", + "category": "System" } -]; +]; \ No newline at end of file diff --git a/scripts/dsk2js.pl b/scripts/dsk2js.pl deleted file mode 100755 index ba13e54..0000000 --- a/scripts/dsk2js.pl +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/perl -w -# Copyright 2010-2016 Will Scullin -# -# Permission to use, copy, modify, distribute, and sell this software and its -# documentation for any purpose is hereby granted without fee, provided that -# the above copyright notice appear in all copies and that both that -# copyright notice and this permission notice appear in supporting -# documentation. No representations are made about the suitability of this -# software for any purpose. It is provided "as is" without express or -# implied warranty. - -use MIME::Base64 qw(encode_base64); -use Getopt::Std; - -$Getopt::Std::STANDARD_HELP_VERSION = 1; - -my %opts; -getopts('rn:c:t:', \%opts); - -sub HELP_MESSAGE() { - my $fh = shift; - print $fh "dsk2js.pl [-c category] [-n name] [-t type] imagefile\n" -}; -sub VERSION_MESSAGE() { my $fh = shift; print $fh "Version 1.0\n" }; - -open(DISK, $ARGV[0]) or die $!; -binmode(DISK); - -my ($name, $ext) = $ARGV[0] =~ /([^\/]+)\.(dsk|po|2mg)$/i; -my $rv; -my $buffer; -my $readOnly = 0; -my $volume = 0; - -my $category = "Misc"; - -$name = $opts{'n'} if ($opts{'n'}); -$category = $opts{'c'} if ($opts{'c'}); -$ext = $opts{'t'} if ($opts{'t'}); -if ($opts{'r'}) { - $readOnly = 1; -} - -$ext = lc($ext); - -my $tmp; -my $offset = 0; - -if ($ext eq '2mg') { -# $rv = read(DISK, $buffer, 0x40); - # Format - $offset += read(DISK, $buffer, 0x04); - $tmp = unpack("a[4]", $buffer); - if ($tmp ne '2IMG') { - print STDERR "Invalid format"; - exit(1); - } - - # Creator - $offset += read(DISK, $buffer, 0x04); - $tmp = unpack("a[4]", $buffer); - print STDERR "Creator: " . $tmp . "\n"; - - # Header Length - $offset += read(DISK, $buffer, 0x02); - my $header_length = unpack("v", $buffer); - - # Version Number - $offset += read(DISK, $buffer, 0x02); - my $version_number = unpack("v", $buffer); - if ($version_number != 1) { - print STDERR "Unknown version: " . $version_number . "\n"; - exit(1); - } - - # Image Format - $offset += read(DISK, $buffer, 0x04); - my $image_format = unpack("V", $buffer); - if ($image_format == 0) { - $ext = "dsk"; - } elsif ($image_format == 1) { - $ext = "po"; - } else { - print STDERR "Handled image format: " . $image_format; - exit(1); - } - print STDERR "Format: " . $ext . "\n"; - - # Flags - $offset += read(DISK, $buffer, 0x04); - my $flags = unpack("V", $buffer); - if ($flags & 0x80000000) { - $readOnly = 1; - } - if ($flags & 0x100) { - $volume = $flags & 0xff; - } - - $rv = read(DISK, $buffer, $header_length - $offset); -} - -my $sector = 0; -my $track = 0; - -print "{\n"; -print " \"name\": \"$name\",\n"; -print " \"type\": \"$ext\",\n"; -print " \"category\": \"$category\",\n"; -print " \"encoding\": \"base64\",\n"; -if ($readOnly) { - print " \"readOnly\": true,\n"; -} -if ($volume) { - print " \"volume\": \"$volume\",\n"; -} -print " \"data\":\n"; -print "[\n"; -for ($track = 0; $track < 0x23; $track++) { - print ",\n" if ($track != 0); - print " [\n"; - for ($sector = 0; $sector < 0x10; $sector++) { - print ",\n" if ($sector != 0); - $rv = read(DISK, $buffer, 0x100); - print " \""; - print encode_base64($buffer, ""); - print "\""; - } - print "\n ]"; -} -print "\n]}\n"; - -close(DISK); diff --git a/scripts/index.pl b/scripts/index.pl deleted file mode 100755 index 43c9741..0000000 --- a/scripts/index.pl +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env perl -w -# Copyright 2010-2016 Will Scullin -# -# Permission to use, copy, modify, distribute, and sell this software and its -# documentation for any purpose is hereby granted without fee, provided that -# the above copyright notice appear in all copies and that both that -# copyright notice and this permission notice appear in supporting -# documentation. No representations are made about the suitability of this -# software for any purpose. It is provided "as is" without express or -# implied warranty. - -use MIME::Base64 qw(encode_base64); -use JSON qw(from_json); -use Data::Dumper; -use Getopt::Std; - -my $disk; -my @disks = (); - -my %opts; -getopts('pe', \%opts); - -while () { - my $json; - my $fn = $_; - print STDERR "$fn\n"; - open(DISK, $fn) or die $!; - while () { - my $line = $_; - - $json .= $line; - } - close(DISK); - - $disk = from_json($json); - $disk->{'filename'} = $fn; - $disk->{'data'} = NULL; - - push(@disks, $disk); -} - -@disks = sort { $a->{'category'} . $a->{'name'} cmp $b->{'category'} . $b->{'name'} } @disks; - -my $first = 1; -print "disk_index = [\n"; -foreach $disk (@disks) { - next if $disk->{'private'} && !$opts{'p'}; - next if $disk->{'2e'} && !$opts{'e'}; - - print ",\n" unless ($first); - print " {\n"; - print " \"filename\": \"" . $disk->{'filename'} . "\",\n"; - print " \"name\": \"" . $disk->{'name'} . "\",\n"; - if ($disk->{'disk'}) { - print " \"disk\": \"" . $disk->{'disk'} . "\",\n"; - } - print " \"category\": \"" . $disk->{'category'} . "\"\n"; - print " }"; - $first = 0; -} -print "\n];\n";