mirror of
https://github.com/charlesthobe/chdman.git
synced 2024-11-24 07:25:31 +00:00
Copy only chdman.cpp from tools.
This commit is contained in:
parent
0ee0c559b3
commit
64883c012c
@ -1,45 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>AUEffectUtil</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>aueffect</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>AUEffect</string>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>AUEffectDocument</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>aupreset</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>AudioUnit Preset</string>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>AUEffectDocument</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.mamedev.aueffectutil</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>AUEffectUtil</string>
|
||||
</dict>
|
||||
</plist>
|
File diff suppressed because it is too large
Load Diff
@ -1,227 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Miodrag Milanovic
|
||||
/***************************************************************************
|
||||
|
||||
main.c
|
||||
|
||||
Castool command line front end
|
||||
|
||||
27/03/2009 Initial version by Miodrag Milanovic
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "formats/a26_cas.h"
|
||||
#include "formats/ace_tap.h"
|
||||
#include "formats/adam_cas.h"
|
||||
#include "formats/apf_apt.h"
|
||||
#include "formats/atom_tap.h"
|
||||
#include "formats/cbm_tap.h"
|
||||
#include "formats/cgen_cas.h"
|
||||
#include "formats/coco_cas.h"
|
||||
#include "formats/csw_cas.h"
|
||||
#include "formats/fm7_cas.h"
|
||||
#include "formats/fmsx_cas.h"
|
||||
#include "formats/gtp_cas.h"
|
||||
#include "formats/hect_tap.h"
|
||||
#include "formats/kc_cas.h"
|
||||
#include "formats/kim1_cas.h"
|
||||
#include "formats/lviv_lvt.h"
|
||||
#include "formats/mz_cas.h"
|
||||
#include "formats/orao_cas.h"
|
||||
#include "formats/oric_tap.h"
|
||||
#include "formats/p2000t_cas.h"
|
||||
#include "formats/p6001_cas.h"
|
||||
#include "formats/phc25_cas.h"
|
||||
#include "formats/pmd_cas.h"
|
||||
#include "formats/primoptp.h"
|
||||
#include "formats/rk_cas.h"
|
||||
#include "formats/sc3000_bit.h"
|
||||
#include "formats/sol_cas.h"
|
||||
#include "formats/sorc_cas.h"
|
||||
#include "formats/sord_cas.h"
|
||||
#include "formats/spc1000_cas.h"
|
||||
#include "formats/svi_cas.h"
|
||||
#include "formats/thom_cas.h"
|
||||
#include "formats/trs_cas.h"
|
||||
#include "formats/tvc_cas.h"
|
||||
#include "formats/tzx_cas.h"
|
||||
#include "formats/uef_cas.h"
|
||||
#include "formats/vg5k_cas.h"
|
||||
#include "formats/vt_cas.h"
|
||||
#include "formats/x07_cas.h"
|
||||
#include "formats/x1_tap.h"
|
||||
#include "formats/zx81_p.h"
|
||||
|
||||
#include "corestr.h"
|
||||
#include "ioprocs.h"
|
||||
#include "osdcomm.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
|
||||
struct SupportedCassetteFormats
|
||||
{
|
||||
const char *name;
|
||||
const cassette_image::Format * const *formats;
|
||||
const char *desc;
|
||||
};
|
||||
|
||||
const struct SupportedCassetteFormats formats[] = {
|
||||
{"a26", a26_cassette_formats ,"Atari 2600 SuperCharger"},
|
||||
{"apf", apf_cassette_formats ,"APF Imagination Machine"},
|
||||
{"atom", atom_cassette_formats ,"Acorn Atom"},
|
||||
{"bbc", bbc_cassette_formats ,"Acorn BBC & Electron"},
|
||||
{"cbm", cbm_cassette_formats ,"Commodore 8-bit series"},
|
||||
{"cdt", cdt_cassette_formats ,"Amstrad CPC"},
|
||||
{"cgenie", cgenie_cassette_formats ,"EACA Colour Genie"},
|
||||
{"coco", coco_cassette_formats ,"Tandy Radio Shack Color Computer"},
|
||||
{"csw", csw_cassette_formats ,"Compressed Square Wave"},
|
||||
{"ddp", coleco_adam_cassette_formats ,"Coleco ADAM"},
|
||||
{"fm7", fm7_cassette_formats ,"Fujitsu FM-7"},
|
||||
{"fmsx", fmsx_cassette_formats ,"MSX"},
|
||||
{"gtp", gtp_cassette_formats ,"Elektronika inzenjering Galaksija"},
|
||||
{"hector", hector_cassette_formats ,"Micronique Hector & Interact Family Computer"},
|
||||
{"jupiter", ace_cassette_formats ,"Jupiter Cantab Jupiter Ace"},
|
||||
{"kc85", kc_cassette_formats ,"VEB Mikroelektronik KC 85"},
|
||||
{"kim1", kim1_cassette_formats ,"MOS KIM-1"},
|
||||
{"lviv", lviv_lvt_format ,"PK-01 Lviv"},
|
||||
{"mo5", mo5_cassette_formats ,"Thomson MO-series"},
|
||||
{"mz", mz700_cassette_formats ,"Sharp MZ-700"},
|
||||
{"orao", orao_cassette_formats ,"PEL Varazdin Orao"},
|
||||
{"oric", oric_cassette_formats ,"Tangerine Oric"},
|
||||
{"p2000t", p2000t_cassette_formats ,"Philips P2000T"},
|
||||
{"pc6001", pc6001_cassette_formats ,"NEC PC-6001"},
|
||||
{"phc25", phc25_cassette_formats ,"Sanyo PHC-25"},
|
||||
{"pmd85", pmd85_cassette_formats ,"Tesla PMD-85"},
|
||||
{"primo", primo_ptp_format ,"Microkey Primo"},
|
||||
{"rku", rku_cassette_formats ,"UT-88"},
|
||||
{"rk8", rk8_cassette_formats ,"Mikro-80"},
|
||||
{"rks", rks_cassette_formats ,"Specialist"},
|
||||
{"rko", rko_cassette_formats ,"Orion"},
|
||||
{"rkr", rkr_cassette_formats ,"Radio-86RK"},
|
||||
{"rka", rka_cassette_formats ,"Zavod BRA Apogee BK-01"},
|
||||
{"rkm", rkm_cassette_formats ,"Mikrosha"},
|
||||
{"rkp", rkp_cassette_formats ,"SAM SKB VM Partner-01.01"},
|
||||
{"sc3000", sc3000_cassette_formats ,"Sega SC-3000"},
|
||||
{"sol20", sol20_cassette_formats ,"PTC SOL-20"},
|
||||
{"sorcerer", sorcerer_cassette_formats ,"Exidy Sorcerer"},
|
||||
{"sordm5", sordm5_cassette_formats ,"Sord M5"},
|
||||
{"spc1000", spc1000_cassette_formats ,"Samsung SPC-1000"},
|
||||
{"svi", svi_cassette_formats ,"Spectravideo SVI-318 & SVI-328"},
|
||||
{"to7", to7_cassette_formats ,"Thomson TO-series"},
|
||||
{"trs80l2", trs80l2_cassette_formats ,"TRS-80 Level 2"},
|
||||
{"tvc64", tvc64_cassette_formats ,"Videoton TVC 64"},
|
||||
{"tzx", tzx_cassette_formats ,"Sinclair ZX Spectrum"},
|
||||
{"vg5k", vg5k_cassette_formats ,"Philips VG 5000"},
|
||||
{"vtech1", vtech1_cassette_formats ,"Video Technology Laser 110-310"},
|
||||
{"vtech2", vtech2_cassette_formats ,"Video Technology Laser 350-700"},
|
||||
{"x07", x07_cassette_formats ,"Canon X-07"},
|
||||
{"x1", x1_cassette_formats ,"Sharp X1"},
|
||||
{"zx80_o", zx80_o_format ,"Sinclair ZX80"},
|
||||
{"zx81_p", zx81_p_format ,"Sinclair ZX81"},
|
||||
|
||||
|
||||
|
||||
{nullptr,nullptr,nullptr}
|
||||
};
|
||||
|
||||
|
||||
static std::string get_extension(const char *name)
|
||||
{
|
||||
const char *s;
|
||||
s = name;
|
||||
if (s != nullptr)
|
||||
s = strrchr(s, '.');
|
||||
return s ? std::string(s+1) : "";
|
||||
}
|
||||
|
||||
static void display_usage(const char *argv0)
|
||||
{
|
||||
fprintf(stderr, "Usage: \n");
|
||||
fprintf(stderr, " %s convert <format> <inputfile> <outputfile.wav>\n", argv0);
|
||||
}
|
||||
|
||||
static void display_formats(void)
|
||||
{
|
||||
int i,j;
|
||||
fprintf(stderr, "Supported formats:\n\n");
|
||||
for (i = 0; formats[i].name; i++) {
|
||||
fprintf(stderr, "%10s - %s\n",formats[i].name,formats[i].desc);
|
||||
for (j = 1; formats[i].formats[j]; j++) {
|
||||
fprintf(stderr, "%15s %s\n","",formats[i].formats[j]->extensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CLIB_DECL main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
int found =0;
|
||||
const cassette_image::Format * const *selected_formats = nullptr;
|
||||
cassette_image::ptr cassette;
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
if (!core_stricmp("convert", argv[1]))
|
||||
{
|
||||
// convert command
|
||||
if (argc!=5) {
|
||||
fprintf(stderr, "Wrong parameter number.\n\n");
|
||||
display_usage(argv[0]);
|
||||
return -1;
|
||||
} else {
|
||||
for (i = 0; formats[i].name; i++) {
|
||||
if (core_stricmp(formats[i].name,argv[2])==0) {
|
||||
selected_formats = formats[i].formats;
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
if (found==0) {
|
||||
fprintf(stderr, "Wrong format name.\n\n");
|
||||
display_usage(argv[0]);
|
||||
fprintf(stderr, "\n");
|
||||
display_formats();
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *f = fopen(argv[3], "rb");
|
||||
if (!f) {
|
||||
fprintf(stderr, "File %s not found.\n",argv[3]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto io = util::stdio_read_write(f, 0x00);
|
||||
f = nullptr;
|
||||
if (!io) {
|
||||
fprintf(stderr, "Out of memory.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cassette_image::open_choices(std::move(io), get_extension(argv[3]), selected_formats, cassette_image::FLAG_READONLY, cassette) != cassette_image::error::SUCCESS) {
|
||||
fprintf(stderr, "Invalid format of input file.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cassette->dump(argv[4]);
|
||||
cassette.reset();
|
||||
goto theend;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Usage */
|
||||
fprintf(stderr, "castool - Generic cassette manipulation tool for use with MAME\n\n");
|
||||
display_usage(argv[0]);
|
||||
fprintf(stderr, "\n");
|
||||
display_formats();
|
||||
fprintf(stderr, "\nExample usage:\n");
|
||||
fprintf(stderr, " %s convert tzx game.tzx game.wav\n\n", argv[0]);
|
||||
|
||||
theend :
|
||||
return 0;
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
##
|
||||
## license:BSD-3-Clause
|
||||
## copyright-holders:Zoe Blade
|
||||
|
||||
# Fix discrepancies in arcade ROM dump names, by Zoe Blade
|
||||
# For Python 2 and 3
|
||||
|
||||
import sys
|
||||
import xml.etree.ElementTree
|
||||
|
||||
|
||||
def fixPair(parentMachine, childMachine):
|
||||
changes = { }
|
||||
for childRom in childMachine.iter('rom'):
|
||||
for parentRom in parentMachine.iter('rom'):
|
||||
if parentRom.get('sha1') == childRom.get('sha1'):
|
||||
# ROM pair found
|
||||
if parentRom.get('name') != childRom.get('name'):
|
||||
# The names don't match
|
||||
changes[childRom.get('name')] = parentRom.get('name')
|
||||
|
||||
if changes:
|
||||
sourceFilename = childMachine.get('sourcefile')
|
||||
|
||||
try:
|
||||
input = open(sourceFilename, 'r')
|
||||
source = input.read()
|
||||
input.close()
|
||||
except Exception as e:
|
||||
sys.stderr.write('%s: error reading %s: %s\n' % (sys.argv[0], sourceFilename, e))
|
||||
return False
|
||||
|
||||
for oldRomFilename in changes:
|
||||
newRomFilename = '"%s"' % (changes[oldRomFilename])
|
||||
oldRomFilename = '"%s"' % (oldRomFilename)
|
||||
|
||||
paddedLen = max(len(oldRomFilename), len(newRomFilename))
|
||||
oldRomFilenamePadded = oldRomFilename.ljust(paddedLen, ' ')
|
||||
newRomFilenamePadded = newRomFilename.ljust(paddedLen, ' ')
|
||||
|
||||
source = source.replace(oldRomFilenamePadded, newRomFilenamePadded) # Try to preserve fancy spacing where possible
|
||||
source = source.replace(oldRomFilename, newRomFilename) # Fallback on just replacing the filename
|
||||
|
||||
sys.stdout.write('%s: %s -> %s\n' % (sourceFilename, oldRomFilename, newRomFilename))
|
||||
|
||||
output = open(sourceFilename, 'w')
|
||||
output.write(source)
|
||||
output.close()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) > 2:
|
||||
sys.stderr.write('Usage:\n%s [arcade.xml]\n' % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
filename = sys.argv[1]
|
||||
else:
|
||||
filename = 'arcade.xml'
|
||||
|
||||
sys.stderr.write('Loading XML file...')
|
||||
sys.stderr.flush()
|
||||
try:
|
||||
root = xml.etree.ElementTree.parse(filename).getroot()
|
||||
except Exception as e:
|
||||
sys.stderr.write('\n%s: error parsing %s: %s\n' % (sys.argv[0], filename, e))
|
||||
sys.exit(2)
|
||||
sys.stderr.write('done.\n')
|
||||
|
||||
errors = 0
|
||||
for childMachine in root.iter('machine'):
|
||||
if childMachine.get('cloneof'):
|
||||
for parentMachine in root.iter('machine'):
|
||||
if parentMachine.get('name') == childMachine.get('cloneof'):
|
||||
# Machine pair found
|
||||
if not fixPair(parentMachine, childMachine):
|
||||
errors += 1
|
||||
|
||||
sys.exit(0 if errors == 0 else 3)
|
@ -1,51 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
##
|
||||
## license:BSD-3-Clause
|
||||
## copyright-holders:Zoe Blade
|
||||
|
||||
# Find discrepancies in arcade ROM dump names, by Zoe Blade
|
||||
# For Python 2 and 3
|
||||
|
||||
import sys
|
||||
import xml.etree.ElementTree
|
||||
|
||||
|
||||
def checkPair(parentMachine, childMachine):
|
||||
for childRom in childMachine.iter('rom'):
|
||||
for parentRom in parentMachine.iter('rom'):
|
||||
if parentRom.get('sha1') == childRom.get('sha1'):
|
||||
# ROM pair found
|
||||
if parentRom.get('name') != childRom.get('name'):
|
||||
# The names don't match
|
||||
sys.stdout.write('%s %s: %s -> %s\n' % (childMachine.get('sourcefile'), childMachine.get('name'), childRom.get('name'), parentRom.get('name')))
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) > 2:
|
||||
sys.stderr.write('Usage:\n%s [arcade.xml]\n' % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
filename = sys.argv[1]
|
||||
else:
|
||||
filename = 'arcade.xml'
|
||||
|
||||
sys.stderr.write('Loading XML file...')
|
||||
sys.stderr.flush()
|
||||
try:
|
||||
root = xml.etree.ElementTree.parse(filename).getroot()
|
||||
except Exception as e:
|
||||
sys.stderr.write('\n%s: error parsing %s: %s\n' % (sys.argv[0], filename, e))
|
||||
sys.exit(2)
|
||||
sys.stderr.write('done.\n')
|
||||
|
||||
for childMachine in root.iter('machine'):
|
||||
if childMachine.get('cloneof'):
|
||||
for parentMachine in root.iter('machine'):
|
||||
if parentMachine.get('name') == childMachine.get('cloneof'):
|
||||
# Machine pair found
|
||||
checkPair(parentMachine, childMachine)
|
||||
|
||||
sys.exit(0)
|
@ -1,711 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Olivier Galibert
|
||||
/***************************************************************************
|
||||
|
||||
(Floppy) image command-line manager
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "image_handler.h"
|
||||
|
||||
#include "corestr.h"
|
||||
#include "ioprocs.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
|
||||
static formats_table formats;
|
||||
|
||||
static void display_usage()
|
||||
{
|
||||
fprintf(stderr, "Usage: \n");
|
||||
fprintf(stderr, " floptool.exe identify <inputfile> [<inputfile> ...] -- Identify an image format\n");
|
||||
fprintf(stderr, " floptool.exe flopconvert [input_format|auto] output_format <inputfile> <outputfile> -- Convert a floppy image\n");
|
||||
fprintf(stderr, " floptool.exe flopcreate output_format filesystem <outputfile> -- Create a preformatted floppy image\n");
|
||||
fprintf(stderr, " floptool.exe flopdir input_format filesystem <image> -- List the contents of a floppy image\n");
|
||||
fprintf(stderr, " floptool.exe flopread input_format filesystem <image> <path> <outputfile> -- Extract a file from a floppy image\n");
|
||||
fprintf(stderr, " floptool.exe flopwrite input_format filesystem <image> <inputfile> <path> -- Write a file into a floppy image\n");
|
||||
}
|
||||
|
||||
static void display_formats()
|
||||
{
|
||||
int sk = 0;
|
||||
for(const auto &e : formats.floppy_format_info_by_key) {
|
||||
int sz = e.first.size();
|
||||
if(sz > sk)
|
||||
sk = sz;
|
||||
}
|
||||
|
||||
for(const auto &e : formats.filesystem_format_by_key) {
|
||||
int sz = e.first.size();
|
||||
if(sz > sk)
|
||||
sk = sz;
|
||||
}
|
||||
|
||||
for(const auto &e : formats.floppy_create_info_by_key) {
|
||||
int sz = e.first.size();
|
||||
if(sz > sk)
|
||||
sk = sz;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Supported floppy formats:\n\n");
|
||||
for(const auto &e : formats.floppy_format_info_by_category)
|
||||
if(!e.second.empty()) {
|
||||
fprintf(stderr, "%s:\n", e.first.c_str());
|
||||
for(auto *fif : e.second)
|
||||
fprintf(stderr, " %-*s r%c - %s [%s]\n", sk, fif->m_format->name(), fif->m_format->supports_save() ? 'w' : '-', fif->m_format->description(), fif->m_format->extensions());
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n\n");
|
||||
fprintf(stderr, "Supported filesystems (with floppy formatting names):\n\n");
|
||||
for(const auto &e : formats.filesystem_format_by_category)
|
||||
if(!e.second.empty()) {
|
||||
fprintf(stderr, "%s:\n", e.first.c_str());
|
||||
for(const auto &f : e.second) {
|
||||
fprintf(stderr, " %-*s %c%c%c %c%c%c - %s\n",
|
||||
sk,
|
||||
f->m_manager->name(),
|
||||
f->m_floppy || f->m_floppy_raw ? 'F' : '-',
|
||||
f->m_hd ? 'H' : '-',
|
||||
f->m_cd ? 'C' : '-',
|
||||
f->m_manager->can_format() || f->m_floppy_raw ? 'f' : '-',
|
||||
f->m_manager->can_read() ? 'r' : '-',
|
||||
f->m_manager->can_write() ? 'w' : '-',
|
||||
f->m_manager->description());
|
||||
for(auto &f2 : f->m_floppy_create)
|
||||
fprintf(stderr, " %-*s - %s\n",
|
||||
sk,
|
||||
f2->m_name,
|
||||
f2->m_description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void display_full_usage()
|
||||
{
|
||||
/* Usage */
|
||||
fprintf(stderr, "floptool - Generic floppy image manipulation tool for use with MAME\n\n");
|
||||
display_usage();
|
||||
fprintf(stderr, "\n");
|
||||
display_formats();
|
||||
|
||||
}
|
||||
|
||||
static int identify(int argc, char *argv[])
|
||||
{
|
||||
// Need to detect CHDs too
|
||||
|
||||
if(argc<3) {
|
||||
fprintf(stderr, "Missing name of file to identify.\n\n");
|
||||
display_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sz = 0;
|
||||
for(int i=2; i<argc; i++) {
|
||||
int len = strlen(argv[i]);
|
||||
if(len > sz)
|
||||
sz = len;
|
||||
}
|
||||
|
||||
for(int i=2; i<argc; i++) {
|
||||
image_handler ih;
|
||||
ih.set_on_disk_path(argv[i]);
|
||||
auto scores = ih.identify(formats);
|
||||
if(scores.empty())
|
||||
printf("%-*s : Unknown format\n", sz, argv[i]);
|
||||
int sz2 = 0;
|
||||
for(const auto &e : scores) {
|
||||
int len = strlen(e.second->m_format->name());
|
||||
if(len > sz2)
|
||||
sz2 = len;
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
for(const auto &e : scores) {
|
||||
printf("%-*s %c %3d - %-*s %s\n", sz, first ? argv[i] : "", first ? ':' : ' ', e.first, sz2, e.second->m_format->name(), e.second->m_format->description());
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const floppy_format_info *find_floppy_source_format(const char *name, image_handler &ih)
|
||||
{
|
||||
const floppy_format_info *source_format;
|
||||
if(!core_stricmp(name, "auto")) {
|
||||
auto scores = ih.identify(formats);
|
||||
if(scores.empty()) {
|
||||
fprintf(stderr, "Error: Could not identify the format of file %s\n", ih.get_on_disk_path().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
if(scores.size() >= 2 && scores[0].first == scores[1].first) {
|
||||
fprintf(stderr, "Ambiguous source format. Possible formats:\n");
|
||||
int sz = 0;
|
||||
for(const auto &e : scores) {
|
||||
int len = strlen(e.second->m_format->name());
|
||||
if(len > sz)
|
||||
sz = len;
|
||||
}
|
||||
|
||||
for(const auto &e : scores)
|
||||
printf(" %3d - %-*s %s\n", e.first, sz, e.second->m_format->name(), e.second->m_format->description());
|
||||
return nullptr;
|
||||
}
|
||||
source_format = scores[0].second;
|
||||
|
||||
} else {
|
||||
source_format = formats.find_floppy_format_info_by_key(name);
|
||||
if(!source_format) {
|
||||
fprintf(stderr, "Error: Format '%s' unknown\n", name);
|
||||
return nullptr;
|
||||
|
||||
}
|
||||
}
|
||||
return source_format;
|
||||
}
|
||||
|
||||
static int flopconvert(int argc, char *argv[])
|
||||
{
|
||||
if(argc!=6) {
|
||||
fprintf(stderr, "Incorrect number of arguments.\n\n");
|
||||
display_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
image_handler ih;
|
||||
ih.set_on_disk_path(argv[4]);
|
||||
|
||||
const floppy_format_info *source_format = find_floppy_source_format(argv[2], ih);
|
||||
if(!source_format)
|
||||
return 1;
|
||||
|
||||
const floppy_format_info *dest_format = formats.find_floppy_format_info_by_key(argv[3]);
|
||||
if(!dest_format) {
|
||||
fprintf(stderr, "Error: Format '%s' unknown\n", argv[3]);
|
||||
return 1;
|
||||
}
|
||||
if(!dest_format->m_format->supports_save()) {
|
||||
fprintf(stderr, "Error: Aaving to format '%s' unsupported\n", argv[3]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(ih.floppy_load(source_format)) {
|
||||
fprintf(stderr, "Error: Loading as format '%s' failed\n", source_format->m_format->name());
|
||||
return 1;
|
||||
}
|
||||
|
||||
ih.set_on_disk_path(argv[5]);
|
||||
|
||||
if(ih.floppy_save(dest_format)) {
|
||||
fprintf(stderr, "Error: Saving as format '%s' failed\n", dest_format->m_format->name());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int flopcreate(int argc, char *argv[])
|
||||
{
|
||||
if(argc!=5) {
|
||||
fprintf(stderr, "Incorrect number of arguments.\n\n");
|
||||
display_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto dest_format = formats.find_floppy_format_info_by_key(argv[2]);
|
||||
if(!dest_format) {
|
||||
fprintf(stderr, "Error: Floppy format '%s' unknown\n", argv[3]);
|
||||
return 1;
|
||||
}
|
||||
if(!dest_format->m_format->supports_save()) {
|
||||
fprintf(stderr, "Error: Saving to format '%s' unsupported\n", argv[3]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto create_fs = formats.find_floppy_create_info_by_key(argv[3]);
|
||||
if(!create_fs) {
|
||||
fprintf(stderr, "Error: Floppy creation format '%s' unknown\n", argv[3]);
|
||||
return 1;
|
||||
}
|
||||
if(!create_fs->m_manager->can_format()) {
|
||||
fprintf(stderr, "Error: Floppy creation format '%s' does not support creating new images\n", argv[3]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fs_meta_data meta;
|
||||
image_handler ih;
|
||||
ih.set_on_disk_path(argv[4]);
|
||||
|
||||
ih.floppy_create(create_fs, meta);
|
||||
return ih.floppy_save(dest_format);
|
||||
}
|
||||
|
||||
static void dir_scan(u32 depth, filesystem_t::dir_t dir, std::vector<std::vector<std::string>> &entries, const std::unordered_map<fs_meta_name, size_t> &nmap, size_t nc, const std::vector<fs_meta_description> &dmetad, const std::vector<fs_meta_description> &fmetad)
|
||||
{
|
||||
std::string head;
|
||||
for(u32 i = 0; i != depth; i++)
|
||||
head += " ";
|
||||
auto contents = dir.contents();
|
||||
for(const auto &c : contents) {
|
||||
size_t id = entries.size();
|
||||
entries.resize(id+1);
|
||||
entries[id].resize(nc);
|
||||
switch(c.m_type) {
|
||||
case fs_dir_entry_type::dir: {
|
||||
auto subdir = dir.dir_get(c.m_key);
|
||||
auto meta = subdir.metadata();
|
||||
for(const auto &m : dmetad) {
|
||||
if(!meta.has(m.m_name))
|
||||
continue;
|
||||
size_t slot = nmap.find(m.m_name)->second;
|
||||
std::string val = fs_meta::to_string(m.m_type, meta.get(m.m_name));
|
||||
if(slot == 0)
|
||||
val = head + "dir " + val;
|
||||
entries[id][slot] = val;
|
||||
}
|
||||
dir_scan(depth+1, subdir, entries, nmap, nc, dmetad, fmetad);
|
||||
break;
|
||||
}
|
||||
case fs_dir_entry_type::file:
|
||||
case fs_dir_entry_type::system_file: {
|
||||
auto file = dir.file_get(c.m_key);
|
||||
auto meta = file.metadata();
|
||||
for(const auto &m : fmetad) {
|
||||
if(!meta.has(m.m_name))
|
||||
continue;
|
||||
size_t slot = nmap.find(m.m_name)->second;
|
||||
std::string val = fs_meta::to_string(m.m_type, meta.get(m.m_name));
|
||||
if(slot == 0)
|
||||
val = head + (c.m_type == fs_dir_entry_type::system_file ? "sys " : "file ") + val;
|
||||
entries[id][slot] = val;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int generic_dir(image_handler &ih)
|
||||
{
|
||||
auto [fsm, fs] = ih.get_fs();
|
||||
auto vmetad = fsm->volume_meta_description();
|
||||
auto fmetad = fsm->file_meta_description();
|
||||
auto dmetad = fsm->directory_meta_description();
|
||||
|
||||
auto vmeta = fs->metadata();
|
||||
if(!vmeta.empty()) {
|
||||
std::string vinf = "Volume:";
|
||||
for(const auto &e : vmetad)
|
||||
vinf += util::string_format(" %s=%s", fs_meta_data::entry_name(e.m_name), fs_meta::to_string(e.m_type, vmeta.get(e.m_name)));
|
||||
printf("%s\n\n", vinf.c_str());
|
||||
}
|
||||
|
||||
std::vector<fs_meta_name> names;
|
||||
names.push_back(fs_meta_name::name);
|
||||
for(const auto &e : fmetad)
|
||||
if(e.m_name != fs_meta_name::name)
|
||||
names.push_back(e.m_name);
|
||||
for(const auto &e : dmetad)
|
||||
if(std::find(names.begin(), names.end(), e.m_name) == names.end())
|
||||
names.push_back(e.m_name);
|
||||
|
||||
std::unordered_map<fs_meta_name, size_t> nmap;
|
||||
for(size_t i = 0; i != names.size(); i++)
|
||||
nmap[names[i]] = i;
|
||||
|
||||
auto root = fs->root();
|
||||
std::vector<std::vector<std::string>> entries;
|
||||
|
||||
entries.resize(1);
|
||||
for(fs_meta_name n : names)
|
||||
entries[0].push_back(fs_meta_data::entry_name(n));
|
||||
|
||||
dir_scan(0, root, entries, nmap, names.size(), dmetad, fmetad);
|
||||
|
||||
std::vector<u32> sizes(names.size());
|
||||
|
||||
for(const auto &e : entries)
|
||||
for(unsigned int i=0; i != names.size(); i++)
|
||||
sizes[i] = std::max<u32>(sizes[i], e[i].size());
|
||||
|
||||
for(const auto &e : entries) {
|
||||
std::string l;
|
||||
for(unsigned int i=0; i != names.size(); i++) {
|
||||
if(i)
|
||||
l += ' ';
|
||||
l += util::string_format("%-*s", sizes[i], e[i]);
|
||||
}
|
||||
printf("%s\n", l.c_str());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int flopdir(int argc, char *argv[])
|
||||
{
|
||||
if(argc!=5) {
|
||||
fprintf(stderr, "Incorrect number of arguments.\n\n");
|
||||
display_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
image_handler ih;
|
||||
ih.set_on_disk_path(argv[4]);
|
||||
|
||||
const floppy_format_info *source_format = find_floppy_source_format(argv[2], ih);
|
||||
if(!source_format)
|
||||
return 1;
|
||||
|
||||
auto fs = formats.find_filesystem_format_by_key(argv[3]);
|
||||
if(!fs) {
|
||||
fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[3]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(!fs->m_manager || !fs->m_manager->can_read()) {
|
||||
fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[3]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(ih.floppy_load(source_format)) {
|
||||
fprintf(stderr, "Error: Loading as format '%s' failed\n", source_format->m_format->name());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(ih.floppy_mount_fs(fs)) {
|
||||
fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return generic_dir(ih);
|
||||
}
|
||||
|
||||
static int hddir(int argc, char *argv[])
|
||||
{
|
||||
if(argc!=4) {
|
||||
fprintf(stderr, "Incorrect number of arguments.\n\n");
|
||||
display_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
image_handler ih;
|
||||
ih.set_on_disk_path(argv[3]);
|
||||
|
||||
auto fs = formats.find_filesystem_format_by_key(argv[2]);
|
||||
if(!fs) {
|
||||
fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(!fs->m_manager || !fs->m_manager->can_read()) {
|
||||
fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(ih.hd_mount_fs(fs)) {
|
||||
fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return generic_dir(ih);
|
||||
}
|
||||
|
||||
|
||||
static int generic_read(image_handler &ih, const char *srcpath, const char *dstpath)
|
||||
{
|
||||
auto [fsm, fs] = ih.get_fs();
|
||||
|
||||
std::vector<std::string> path = ih.path_split(srcpath);
|
||||
|
||||
auto dir = fs->root();
|
||||
std::string apath;
|
||||
for(unsigned int i = 0; i < path.size() - 1; i++) {
|
||||
auto c = dir.contents();
|
||||
unsigned int j;
|
||||
for(j = 0; j != c.size(); j++)
|
||||
if(c[j].m_name == path[i])
|
||||
break;
|
||||
if(j == c.size()) {
|
||||
fprintf(stderr, "Error: directory %s%c%s not found\n", apath.c_str(), fsm->directory_separator(), path[i].c_str());
|
||||
return 1;
|
||||
}
|
||||
if(c[j].m_type != fs_dir_entry_type::dir) {
|
||||
fprintf(stderr, "Error: %s%c%s is not a directory\n", apath.c_str(), fsm->directory_separator(), path[i].c_str());
|
||||
return 1;
|
||||
}
|
||||
dir = dir.dir_get(c[j].m_key);
|
||||
apath += fsm->directory_separator() + path[i];
|
||||
}
|
||||
|
||||
auto c = dir.contents();
|
||||
unsigned int j;
|
||||
for(j = 0; j != c.size(); j++)
|
||||
if(c[j].m_name == path.back())
|
||||
break;
|
||||
if(j == c.size()) {
|
||||
fprintf(stderr, "Error: file %s%c%s not found\n", apath.c_str(), fsm->directory_separator(), path.back().c_str());
|
||||
return 1;
|
||||
}
|
||||
auto file = dir.file_get(c[j].m_key);
|
||||
auto meta = file.metadata();
|
||||
|
||||
if(!meta.has(fs_meta_name::length)) {
|
||||
fprintf(stderr, "Error: %s%c%s is not a readable file\n", apath.c_str(), fsm->directory_separator(), path.back().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
image_handler::fsave(dstpath, file.read_all());
|
||||
|
||||
bool has_rsrc = fsm->has_rsrc() && meta.has(fs_meta_name::rsrc_length);
|
||||
|
||||
if(has_rsrc)
|
||||
image_handler::fsave_rsrc(image_handler::path_make_rsrc(dstpath), file.rsrc_read_all());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int flopread(int argc, char *argv[])
|
||||
{
|
||||
if(argc!=7) {
|
||||
fprintf(stderr, "Incorrect number of arguments.\n\n");
|
||||
display_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
image_handler ih;
|
||||
ih.set_on_disk_path(argv[4]);
|
||||
|
||||
const floppy_format_info *source_format = find_floppy_source_format(argv[2], ih);
|
||||
if(!source_format)
|
||||
return 1;
|
||||
|
||||
auto fs = formats.find_filesystem_format_by_key(argv[3]);
|
||||
if(!fs) {
|
||||
fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[3]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(!fs->m_manager || !fs->m_manager->can_read()) {
|
||||
fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[3]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(ih.floppy_load(source_format)) {
|
||||
fprintf(stderr, "Error: Loading as format '%s' failed\n", source_format->m_format->name());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(ih.floppy_mount_fs(fs)) {
|
||||
fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return generic_read(ih, argv[5], argv[6]);
|
||||
}
|
||||
|
||||
static int hdread(int argc, char *argv[])
|
||||
{
|
||||
if(argc!=6) {
|
||||
fprintf(stderr, "Incorrect number of arguments.\n\n");
|
||||
display_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
image_handler ih;
|
||||
ih.set_on_disk_path(argv[3]);
|
||||
|
||||
auto fs = formats.find_filesystem_format_by_key(argv[2]);
|
||||
if(!fs) {
|
||||
fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(!fs->m_manager || !fs->m_manager->can_read()) {
|
||||
fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(ih.hd_mount_fs(fs)) {
|
||||
fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return generic_read(ih, argv[4], argv[5]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int generic_write(image_handler &ih, const char *srcpath, const char *dstpath)
|
||||
{
|
||||
auto [fsm, fs] = ih.get_fs();
|
||||
|
||||
std::vector<std::string> path = ih.path_split(dstpath);
|
||||
|
||||
auto dir = fs->root();
|
||||
std::string apath;
|
||||
for(unsigned int i = 0; i < path.size() - 1; i++) {
|
||||
auto c = dir.contents();
|
||||
unsigned int j;
|
||||
for(j = 0; j != c.size(); j++)
|
||||
if(c[j].m_name == path[i])
|
||||
break;
|
||||
if(j == c.size()) {
|
||||
fprintf(stderr, "Error: directory %s%c%s not found\n", apath.c_str(), fsm->directory_separator(), path[i].c_str());
|
||||
return 1;
|
||||
}
|
||||
if(c[j].m_type != fs_dir_entry_type::dir) {
|
||||
fprintf(stderr, "Error: %s%c%s is not a directory\n", apath.c_str(), fsm->directory_separator(), path[i].c_str());
|
||||
return 1;
|
||||
}
|
||||
dir = dir.dir_get(c[j].m_key);
|
||||
apath += fsm->directory_separator() + path[i];
|
||||
}
|
||||
|
||||
|
||||
fs_meta_data meta;
|
||||
meta.set(fs_meta_name::name, path.back());
|
||||
|
||||
auto file = dir.file_create(meta);
|
||||
auto filedata = image_handler::fload(srcpath);
|
||||
file.replace(filedata);
|
||||
|
||||
bool has_rsrc = fsm->has_rsrc();
|
||||
|
||||
if(has_rsrc) {
|
||||
std::string rpath = image_handler::path_make_rsrc(dstpath);
|
||||
|
||||
if(image_handler::fexists(rpath)) {
|
||||
filedata = image_handler::fload_rsrc(rpath);
|
||||
if(!filedata.empty())
|
||||
file.rsrc_replace(filedata);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int flopwrite(int argc, char *argv[])
|
||||
{
|
||||
if(argc!=7) {
|
||||
fprintf(stderr, "Incorrect number of arguments.\n\n");
|
||||
display_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
image_handler ih;
|
||||
ih.set_on_disk_path(argv[4]);
|
||||
|
||||
const floppy_format_info *source_format = find_floppy_source_format(argv[2], ih);
|
||||
if(!source_format)
|
||||
return 1;
|
||||
|
||||
auto fs = formats.find_filesystem_format_by_key(argv[3]);
|
||||
if(!fs) {
|
||||
fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[3]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(!fs->m_manager || !fs->m_manager->can_read()) {
|
||||
fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[3]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(ih.floppy_load(source_format)) {
|
||||
fprintf(stderr, "Error: Loading as format '%s' failed\n", source_format->m_format->name());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(ih.floppy_mount_fs(fs)) {
|
||||
fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int err = generic_write(ih, argv[5], argv[6]);
|
||||
if(err)
|
||||
return err;
|
||||
|
||||
ih.fs_to_floppy();
|
||||
if(ih.floppy_save(source_format))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdwrite(int argc, char *argv[])
|
||||
{
|
||||
if(argc!=6) {
|
||||
fprintf(stderr, "Incorrect number of arguments.\n\n");
|
||||
display_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
image_handler ih;
|
||||
ih.set_on_disk_path(argv[3]);
|
||||
|
||||
auto fs = formats.find_filesystem_format_by_key(argv[2]);
|
||||
if(!fs) {
|
||||
fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(!fs->m_manager || !fs->m_manager->can_read()) {
|
||||
fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(ih.hd_mount_fs(fs)) {
|
||||
fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return generic_write(ih, argv[4], argv[5]);
|
||||
}
|
||||
|
||||
int CLIB_DECL main(int argc, char *argv[])
|
||||
{
|
||||
formats.init();
|
||||
|
||||
if(argc == 1) {
|
||||
display_full_usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
if(!core_stricmp("identify", argv[1]))
|
||||
return identify(argc, argv);
|
||||
else if(!core_stricmp("flopconvert", argv[1]))
|
||||
return flopconvert(argc, argv);
|
||||
else if(!core_stricmp("flopcreate", argv[1]))
|
||||
return flopcreate(argc, argv);
|
||||
else if(!core_stricmp("flopdir", argv[1]))
|
||||
return flopdir(argc, argv);
|
||||
else if(!core_stricmp("flopread", argv[1]))
|
||||
return flopread(argc, argv);
|
||||
else if(!core_stricmp("flopwrite", argv[1]))
|
||||
return flopwrite(argc, argv);
|
||||
else if(!core_stricmp("hddir", argv[1]))
|
||||
return hddir(argc, argv);
|
||||
else if(!core_stricmp("hdread", argv[1]))
|
||||
return hdread(argc, argv);
|
||||
else if(!core_stricmp("hdwrite", argv[1]))
|
||||
return hdwrite(argc, argv);
|
||||
else {
|
||||
fprintf(stderr, "Unknown command '%s'\n\n", argv[1]);
|
||||
display_usage();
|
||||
return 1;
|
||||
}
|
||||
} catch(const emu_fatalerror &err) {
|
||||
fprintf(stderr, "Error: %s", err.what());
|
||||
return 1;
|
||||
}
|
||||
}
|
@ -1,409 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Olivier Galibert
|
||||
|
||||
// Image generic handler class and helpers
|
||||
|
||||
#include "image_handler.h"
|
||||
|
||||
#include "formats/all.h"
|
||||
#include "formats/fsblk_vec.h"
|
||||
#include "formats/fs_unformatted.h"
|
||||
|
||||
#include "ioprocs.h"
|
||||
#include "ioprocsfill.h"
|
||||
#include "ioprocsvec.h"
|
||||
|
||||
|
||||
// Fatalerror implementation
|
||||
|
||||
emu_fatalerror::emu_fatalerror(util::format_argument_pack<std::ostream> const &args)
|
||||
: emu_fatalerror(0, args)
|
||||
{
|
||||
}
|
||||
|
||||
emu_fatalerror::emu_fatalerror(int _exitcode, util::format_argument_pack<std::ostream> const &args)
|
||||
: m_text(util::string_format(args))
|
||||
, m_code(_exitcode)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// Format enumeration
|
||||
|
||||
namespace {
|
||||
struct enumerator : public mame_formats_enumerator {
|
||||
formats_table *m_table;
|
||||
std::string m_category;
|
||||
|
||||
enumerator(formats_table *table) : mame_formats_enumerator(), m_table(table), m_category("None") {}
|
||||
|
||||
virtual ~enumerator() = default;
|
||||
virtual void add(const cassette_image::Format *const *formats) {}
|
||||
|
||||
virtual void category(const char *name) {
|
||||
m_category = name;
|
||||
}
|
||||
|
||||
virtual void add(floppy_format_type format) {
|
||||
m_table->floppy_format_infos.emplace_back(std::make_unique<floppy_format_info>(format(), m_category));
|
||||
}
|
||||
|
||||
virtual void add(const filesystem_manager_t &fs) {
|
||||
m_table->filesystem_formats.emplace_back(std::make_unique<filesystem_format>(&fs, m_category));
|
||||
}
|
||||
};
|
||||
|
||||
struct fs_enum : public filesystem_manager_t::floppy_enumerator {
|
||||
filesystem_format *m_format;
|
||||
|
||||
fs_enum(filesystem_format *format) : m_format(format) {}
|
||||
|
||||
virtual void add(floppy_format_type type, u32 image_size, const char *name, const char *description) override {
|
||||
m_format->m_floppy = true;
|
||||
m_format->m_floppy_create.emplace_back(std::make_unique<floppy_create_info>(m_format->m_manager, type, image_size, name, description));
|
||||
}
|
||||
|
||||
virtual void add_raw(const char *name, u32 key, const char *description) override {
|
||||
m_format->m_floppy_raw = true;
|
||||
m_format->m_floppy_create.emplace_back(std::make_unique<floppy_create_info>(name, key, description));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void formats_table::init()
|
||||
{
|
||||
std::vector<uint32_t> variants;
|
||||
|
||||
enumerator en(this);
|
||||
mame_formats_full_list(en);
|
||||
|
||||
for(auto &f : filesystem_formats) {
|
||||
fs_enum fen(f.get());
|
||||
f->m_manager->enumerate_f(fen, floppy_image::FF_UNKNOWN, variants);
|
||||
}
|
||||
|
||||
for(auto &f : floppy_format_infos) {
|
||||
auto *ff = f.get();
|
||||
std::string key = ff->m_format->name();
|
||||
auto i = floppy_format_info_by_key.find(key);
|
||||
if(i != floppy_format_info_by_key.end()) {
|
||||
fprintf(stderr, "Collision on floppy format name %s between \"%s\" and \"%s\".\n",
|
||||
ff->m_format->name(),
|
||||
ff->m_format->description(),
|
||||
i->second->m_format->description());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
floppy_format_info_by_key[key] = ff;
|
||||
floppy_format_info_by_category[ff->m_category].push_back(ff);
|
||||
}
|
||||
|
||||
for(auto &f : filesystem_formats) {
|
||||
auto *ff = f.get();
|
||||
std::string key = ff->m_manager->name();
|
||||
auto i = filesystem_format_by_key.find(key);
|
||||
if(i != filesystem_format_by_key.end()) {
|
||||
fprintf(stderr, "Collision on filesystem name %s between \"%s\" and \"%s\".\n",
|
||||
ff->m_manager->name(),
|
||||
ff->m_manager->description(),
|
||||
i->second->m_manager->description());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
filesystem_format_by_key[key] = ff;
|
||||
filesystem_format_by_category[ff->m_category].push_back(ff);
|
||||
|
||||
for(auto &f2 : ff->m_floppy_create) {
|
||||
auto *ff2 = f2.get();
|
||||
key = ff2->m_name;
|
||||
auto i = floppy_create_info_by_key.find(key);
|
||||
if(i != floppy_create_info_by_key.end()) {
|
||||
fprintf(stderr, "Collision on floppy create name %s between \"%s\" and \"%s\".\n",
|
||||
ff2->m_name,
|
||||
ff2->m_description,
|
||||
i->second->m_description);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
floppy_create_info_by_key[key] = ff2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const floppy_format_info *formats_table::find_floppy_format_info_by_key(const std::string &key) const
|
||||
{
|
||||
auto i = floppy_format_info_by_key.find(key);
|
||||
return i == floppy_format_info_by_key.end() ? nullptr : i->second;
|
||||
}
|
||||
|
||||
const filesystem_format *formats_table::find_filesystem_format_by_key(const std::string &key) const
|
||||
{
|
||||
auto i = filesystem_format_by_key.find(key);
|
||||
return i == filesystem_format_by_key.end() ? nullptr : i->second;
|
||||
}
|
||||
|
||||
const floppy_create_info *formats_table::find_floppy_create_info_by_key(const std::string &key) const
|
||||
{
|
||||
auto i = floppy_create_info_by_key.find(key);
|
||||
return i == floppy_create_info_by_key.end() ? nullptr : i->second;
|
||||
}
|
||||
|
||||
|
||||
// Image handling
|
||||
|
||||
std::vector<u8> image_handler::fload(std::string path)
|
||||
{
|
||||
char msg[4096];
|
||||
sprintf(msg, "Error opening %s for reading", path.c_str());
|
||||
auto fi = fopen(path.c_str(), "rb");
|
||||
if(!fi) {
|
||||
perror(msg);
|
||||
exit(1);
|
||||
}
|
||||
fseek(fi, 0, SEEK_END);
|
||||
long size = ftell(fi);
|
||||
std::vector<u8> filedata(size);
|
||||
fseek(fi, 0, SEEK_SET);
|
||||
fread(filedata.data(), filedata.size(), 1, fi);
|
||||
fclose(fi);
|
||||
|
||||
return filedata;
|
||||
}
|
||||
|
||||
std::vector<u8> image_handler::fload_rsrc(std::string path)
|
||||
{
|
||||
auto filedata = fload(path);
|
||||
const u8 *head = filedata.data();
|
||||
|
||||
if(filesystem_t::r32b(head+0x00) == 0x00051607 &&
|
||||
filesystem_t::r32b(head+0x04) == 0x00020000) {
|
||||
u16 nent = filesystem_t::r16b(head+0x18);
|
||||
for(u16 i=0; i != nent; i++) {
|
||||
const u8 *e = head + 12*i;
|
||||
if(filesystem_t::r32b(e+0) == 2) {
|
||||
u32 start = filesystem_t::r32b(e+4);
|
||||
u32 len = filesystem_t::r32b(e+8);
|
||||
filedata.erase(filedata.begin(), filedata.begin() + start);
|
||||
filedata.erase(filedata.begin() + len, filedata.end());
|
||||
return filedata;
|
||||
}
|
||||
}
|
||||
}
|
||||
filedata.clear();
|
||||
return filedata;
|
||||
}
|
||||
|
||||
void image_handler::fsave(std::string path, const std::vector<u8> &data)
|
||||
{
|
||||
char msg[4096];
|
||||
sprintf(msg, "Error opening %s for writing", path.c_str());
|
||||
auto fo = fopen(path.c_str(), "wb");
|
||||
if(!fo) {
|
||||
perror(msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fwrite(data.data(), data.size(), 1, fo);
|
||||
fclose(fo);
|
||||
}
|
||||
|
||||
void image_handler::fsave_rsrc(std::string path, const std::vector<u8> &data)
|
||||
{
|
||||
u8 head[0x2a];
|
||||
|
||||
filesystem_t::w32b(head+0x00, 0x00051607); // Magic
|
||||
filesystem_t::w32b(head+0x04, 0x00020000); // Version
|
||||
filesystem_t::fill(head+0x08, 0, 16); // Filler
|
||||
filesystem_t::w16b(head+0x18, 1); // Number of entries
|
||||
filesystem_t::w32b(head+0x1a, 2); // Resource fork
|
||||
filesystem_t::w32b(head+0x22, 0x2a); // Offset in the file
|
||||
filesystem_t::w32b(head+0x26, data.size()); // Length
|
||||
|
||||
char msg[4096];
|
||||
sprintf(msg, "Error opening %s for writing", path.c_str());
|
||||
auto fo = fopen(path.c_str(), "wb");
|
||||
if(!fo) {
|
||||
perror(msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fwrite(head, sizeof(head), 1, fo);
|
||||
fwrite(data.data(), data.size(), 1, fo);
|
||||
fclose(fo);
|
||||
}
|
||||
|
||||
image_handler::image_handler() : m_floppy_image(84, 2, floppy_image::FF_UNKNOWN)
|
||||
{
|
||||
}
|
||||
|
||||
void image_handler::set_on_disk_path(std::string path)
|
||||
{
|
||||
m_on_disk_path = path;
|
||||
}
|
||||
|
||||
std::vector<std::pair<u8, const floppy_format_info *>> image_handler::identify(const formats_table &formats)
|
||||
{
|
||||
std::vector<std::pair<u8, const floppy_format_info *>> res;
|
||||
std::vector<uint32_t> variants;
|
||||
|
||||
FILE *f = fopen(m_on_disk_path.c_str(), "rb");
|
||||
if (!f) {
|
||||
std::string msg = util::string_format("Error opening %s for reading", m_on_disk_path);
|
||||
perror(msg.c_str());
|
||||
return res;
|
||||
}
|
||||
|
||||
auto io = util::stdio_read(f, 0xff);
|
||||
|
||||
for(const auto &e : formats.floppy_format_info_by_key) {
|
||||
u8 score = e.second->m_format->identify(*io, floppy_image::FF_UNKNOWN, variants);
|
||||
if(score)
|
||||
res.emplace_back(std::make_pair(score, e.second));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool image_handler::floppy_load(const floppy_format_info *format)
|
||||
{
|
||||
std::vector<uint32_t> variants;
|
||||
FILE *f = fopen(m_on_disk_path.c_str(), "rb");
|
||||
if (!f) {
|
||||
std::string msg = util::string_format("Error opening %s for reading", m_on_disk_path);
|
||||
perror(msg.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto io = util::stdio_read(f, 0xff);
|
||||
|
||||
return !format->m_format->load(*io, floppy_image::FF_UNKNOWN, variants, &m_floppy_image);
|
||||
}
|
||||
|
||||
bool image_handler::floppy_save(const floppy_format_info *format)
|
||||
{
|
||||
std::vector<uint32_t> variants;
|
||||
std::string msg = util::string_format("Error opening %s for writing", m_on_disk_path);
|
||||
FILE *f = fopen(m_on_disk_path.c_str(), "wb");
|
||||
if (!f) {
|
||||
perror(msg.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto io = util::stdio_read_write(f, 0xff);
|
||||
|
||||
return !format->m_format->save(*io, variants, &m_floppy_image);
|
||||
}
|
||||
|
||||
void image_handler::floppy_create(const floppy_create_info *format, fs_meta_data meta)
|
||||
{
|
||||
if(format->m_type) {
|
||||
std::vector<uint32_t> variants;
|
||||
std::vector<u8> img(format->m_image_size);
|
||||
fsblk_vec_t blockdev(img);
|
||||
auto fs = format->m_manager->mount(blockdev);
|
||||
fs->format(meta);
|
||||
|
||||
auto source_format = format->m_type();
|
||||
auto io = util::ram_read(img.data(), img.size(), 0xff);
|
||||
source_format->load(*io, floppy_image::FF_UNKNOWN, variants, &m_floppy_image);
|
||||
delete source_format;
|
||||
} else {
|
||||
fs_unformatted::format(format->m_key, &m_floppy_image);
|
||||
}
|
||||
}
|
||||
|
||||
bool image_handler::floppy_mount_fs(const filesystem_format *format)
|
||||
{
|
||||
m_floppy_fs_converter = nullptr;
|
||||
for(const auto &ci : format->m_floppy_create) {
|
||||
if(ci->m_type != m_floppy_fs_converter) {
|
||||
std::vector<uint32_t> variants;
|
||||
m_floppy_fs_converter = ci->m_type;
|
||||
m_sector_image.clear();
|
||||
auto load_format = m_floppy_fs_converter();
|
||||
util::random_read_write_fill_wrapper<util::vector_read_write_adapter<u8>, 0xff> io(m_sector_image);
|
||||
load_format->save(io, variants, &m_floppy_image);
|
||||
delete load_format;
|
||||
}
|
||||
|
||||
if(ci->m_image_size == m_sector_image.size())
|
||||
goto success;
|
||||
}
|
||||
m_floppy_fs_converter = nullptr;
|
||||
m_sector_image.clear();
|
||||
return true;
|
||||
|
||||
success:
|
||||
m_fsblk.reset(new fsblk_vec_t(m_sector_image));
|
||||
m_fsm = format->m_manager;
|
||||
m_fs = m_fsm->mount(*m_fsblk);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool image_handler::hd_mount_fs(const filesystem_format *format)
|
||||
{
|
||||
// Should use the chd mechanisms, one thing at a time...
|
||||
|
||||
m_sector_image = fload(m_on_disk_path);
|
||||
m_fsblk.reset(new fsblk_vec_t(m_sector_image));
|
||||
m_fsm = format->m_manager;
|
||||
m_fs = m_fsm->mount(*m_fsblk);
|
||||
return false;
|
||||
}
|
||||
|
||||
void image_handler::fs_to_floppy()
|
||||
{
|
||||
std::vector<uint32_t> variants;
|
||||
auto io = util::ram_read(m_sector_image.data(), m_sector_image.size(), 0xff);
|
||||
auto format = m_floppy_fs_converter();
|
||||
format->load(*io, floppy_image::FF_UNKNOWN, variants, &m_floppy_image);
|
||||
delete format;
|
||||
}
|
||||
|
||||
std::vector<std::string> image_handler::path_split(std::string path) const
|
||||
{
|
||||
std::string opath = path;
|
||||
std::vector<std::string> rpath;
|
||||
if(m_fsm->has_subdirectories()) {
|
||||
std::string element;
|
||||
char sep = m_fsm->directory_separator();
|
||||
for(char c : opath) {
|
||||
if(c == sep) {
|
||||
if(!element.empty()) {
|
||||
rpath.push_back(element);
|
||||
element.clear();
|
||||
}
|
||||
} else
|
||||
element += c;
|
||||
}
|
||||
if(!element.empty())
|
||||
rpath.push_back(element);
|
||||
|
||||
} else
|
||||
rpath.push_back(opath);
|
||||
|
||||
return rpath;
|
||||
}
|
||||
|
||||
bool image_handler::fexists(std::string path)
|
||||
{
|
||||
auto f = fopen(path.c_str(), "rb");
|
||||
if(f != nullptr) {
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
std::string image_handler::path_make_rsrc(std::string path)
|
||||
{
|
||||
auto p = path.end();
|
||||
while(p != path.begin() && p[-1] != '/')
|
||||
p--;
|
||||
std::string rpath(path.begin(), p);
|
||||
rpath += "._";
|
||||
rpath += std::string(p, path.end());
|
||||
return rpath;
|
||||
}
|
||||
|
@ -1,119 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Olivier Galibert
|
||||
|
||||
// Image generic handler class and helpers
|
||||
|
||||
#ifndef MAME_TOOLS_IMAGE_HANDLER_H
|
||||
#define MAME_TOOLS_IMAGE_HANDLER_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../emu/emucore.h"
|
||||
|
||||
#include "formats/fsmgr.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
using u8 = uint8_t;
|
||||
using u16 = uint16_t;
|
||||
using u32 = uint32_t;
|
||||
|
||||
struct floppy_format_info {
|
||||
floppy_image_format_t *m_format;
|
||||
std::string m_category;
|
||||
|
||||
floppy_format_info(floppy_image_format_t *format, std::string category) : m_format(format), m_category(category) {}
|
||||
};
|
||||
|
||||
struct floppy_create_info {
|
||||
const filesystem_manager_t *m_manager;
|
||||
|
||||
floppy_format_type m_type;
|
||||
u32 m_image_size;
|
||||
u32 m_key;
|
||||
const char *m_name;
|
||||
const char *m_description;
|
||||
|
||||
floppy_create_info(const filesystem_manager_t *manager, floppy_format_type type, u32 image_size, const char *name, const char *description) :
|
||||
m_manager(manager), m_type(type), m_image_size(image_size), m_key(0), m_name(name), m_description(description)
|
||||
{ }
|
||||
|
||||
floppy_create_info(const char *name, u32 key, const char *description) :
|
||||
m_manager(nullptr), m_type(nullptr), m_image_size(0), m_key(key), m_name(name), m_description(description)
|
||||
{ }
|
||||
};
|
||||
|
||||
struct filesystem_format {
|
||||
const filesystem_manager_t *m_manager;
|
||||
std::vector<std::unique_ptr<floppy_create_info>> m_floppy_create;
|
||||
std::string m_category;
|
||||
bool m_floppy, m_floppy_raw, m_hd, m_cd;
|
||||
|
||||
filesystem_format(const filesystem_manager_t *manager, std::string category) : m_manager(manager), m_category(category), m_floppy(false), m_floppy_raw(false), m_hd(false), m_cd(false) {}
|
||||
};
|
||||
|
||||
struct formats_table {
|
||||
std::vector<std::unique_ptr<floppy_format_info>> floppy_format_infos;
|
||||
std::vector<std::unique_ptr<filesystem_format>> filesystem_formats;
|
||||
|
||||
std::map<std::string, const floppy_format_info *> floppy_format_info_by_key;
|
||||
std::map<std::string, const filesystem_format *> filesystem_format_by_key;
|
||||
std::map<std::string, const floppy_create_info *> floppy_create_info_by_key;
|
||||
|
||||
std::map<std::string, std::vector<const floppy_format_info *>> floppy_format_info_by_category;
|
||||
std::map<std::string, std::vector<const filesystem_format *>> filesystem_format_by_category;
|
||||
|
||||
void init();
|
||||
|
||||
const floppy_format_info *find_floppy_format_info_by_key(const std::string &key) const;
|
||||
const filesystem_format *find_filesystem_format_by_key(const std::string &key) const;
|
||||
const floppy_create_info *find_floppy_create_info_by_key(const std::string &key) const;
|
||||
};
|
||||
|
||||
class image_handler {
|
||||
public:
|
||||
image_handler();
|
||||
|
||||
void set_on_disk_path(std::string path);
|
||||
const std::string &get_on_disk_path() const { return m_on_disk_path; }
|
||||
|
||||
std::vector<std::pair<u8, const floppy_format_info *>> identify(const formats_table &formats);
|
||||
|
||||
bool floppy_load(const floppy_format_info *format);
|
||||
bool floppy_save(const floppy_format_info *format);
|
||||
|
||||
void floppy_create(const floppy_create_info *format, fs_meta_data meta);
|
||||
bool floppy_mount_fs(const filesystem_format *format);
|
||||
bool hd_mount_fs(const filesystem_format *format);
|
||||
void fs_to_floppy();
|
||||
|
||||
std::pair<const filesystem_manager_t *, filesystem_t *> get_fs() const { return std::make_pair(m_fsm, m_fs.get()); }
|
||||
|
||||
std::vector<std::string> path_split(std::string path) const;
|
||||
|
||||
static std::vector<u8> fload(std::string path);
|
||||
static std::vector<u8> fload_rsrc(std::string path);
|
||||
static void fsave(std::string path, const std::vector<u8> &data);
|
||||
static void fsave_rsrc(std::string path, const std::vector<u8> &data);
|
||||
static bool fexists(std::string path);
|
||||
static std::string path_make_rsrc(std::string path);
|
||||
|
||||
private:
|
||||
std::string m_on_disk_path;
|
||||
|
||||
floppy_image m_floppy_image;
|
||||
|
||||
floppy_format_type m_floppy_fs_converter;
|
||||
std::vector<u8> m_sector_image;
|
||||
std::unique_ptr<fsblk_t> m_fsblk;
|
||||
const filesystem_manager_t *m_fsm;
|
||||
std::unique_ptr<filesystem_t> m_fs;
|
||||
|
||||
};
|
||||
|
||||
#endif // MAME_TOOLS_IMAGE_HANDLER_H
|
@ -1,107 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/***************************************************************************
|
||||
|
||||
charconv.cpp
|
||||
|
||||
Imgtool character set conversion routines.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "charconv.h"
|
||||
|
||||
#include "corestr.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
imgtool::simple_charconverter imgtool::charconverter_iso_8859_1(nullptr, nullptr);
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// simple_charconverter::simple_charconverter
|
||||
//-------------------------------------------------
|
||||
|
||||
imgtool::simple_charconverter::simple_charconverter(const char32_t lowpage[0x80], const char32_t highpage[0x80], unicode_normalization_form norm)
|
||||
: m_norm(norm), m_lowpage(lowpage), m_highpage(highpage)
|
||||
{
|
||||
// build the reverse lookup table
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
const char32_t *page = i >= 128 ? m_highpage : m_lowpage;
|
||||
char32_t unicode_char = page ? page[i % 128] : i;
|
||||
m_reverse_lookup.emplace_back(unicode_char, (char)i);
|
||||
}
|
||||
|
||||
// and sort it
|
||||
std::sort(m_reverse_lookup.begin(), m_reverse_lookup.end(), [](const std::pair<char32_t, char> &a, const std::pair<char32_t, char> &b)
|
||||
{
|
||||
return b.first > a.first;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// from_utf8
|
||||
//-------------------------------------------------
|
||||
|
||||
void imgtool::simple_charconverter::from_utf8(std::ostream &dest, std::string_view src) const
|
||||
{
|
||||
// normalize the incoming unicode
|
||||
std::string const normalized_src = normalize_unicode(src, m_norm);
|
||||
|
||||
auto nsrc = std::string_view(normalized_src);
|
||||
while (!nsrc.empty())
|
||||
{
|
||||
// get the next character
|
||||
char32_t ch;
|
||||
int rc = uchar_from_utf8(&ch, nsrc);
|
||||
if (rc < 0)
|
||||
{
|
||||
ch = 0xFFFD;
|
||||
rc = 1;
|
||||
}
|
||||
nsrc.remove_prefix(rc);
|
||||
|
||||
// do the reverse lookup
|
||||
auto lookup = std::lower_bound(m_reverse_lookup.begin(), m_reverse_lookup.end(), ch, [](const std::pair<char32_t, char> &a, const char32_t &b)
|
||||
{
|
||||
return a.first < b;
|
||||
});
|
||||
if (lookup == m_reverse_lookup.end())
|
||||
throw charconverter_exception();
|
||||
|
||||
// and output the results
|
||||
dest << lookup->second;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// to_utf8
|
||||
//-------------------------------------------------
|
||||
|
||||
void imgtool::simple_charconverter::to_utf8(std::ostream &dest, std::string_view src) const
|
||||
{
|
||||
for (uint8_t c : src)
|
||||
{
|
||||
// which page is this in?
|
||||
const char32_t *page = ((c & 0x80) == 0) ? m_lowpage : m_highpage;
|
||||
|
||||
// is this page present?
|
||||
if ((c & 0x80) == 0)
|
||||
{
|
||||
// no - pass it on
|
||||
dest << c;
|
||||
}
|
||||
else
|
||||
{
|
||||
// yes - we need to do a lookup
|
||||
size_t base = ((c & 0x80) == 0) ? 0x00 : 0x80;
|
||||
char32_t ch = page[((unsigned char)(c)) - base];
|
||||
if (ch == 0)
|
||||
throw charconverter_exception();
|
||||
|
||||
dest << utf8_from_uchar(ch);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/***************************************************************************
|
||||
|
||||
charconv.h
|
||||
|
||||
Imgtool character set conversion routines.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef IMGTOOL_CHARCONV_H
|
||||
#define IMGTOOL_CHARCONV_H
|
||||
|
||||
#include "unicode.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace imgtool
|
||||
{
|
||||
// ======================> charconverter
|
||||
|
||||
// abstract base class for character conversions
|
||||
class charconverter
|
||||
{
|
||||
public:
|
||||
virtual void from_utf8(std::ostream &dest, std::string_view src) const = 0;
|
||||
virtual void to_utf8(std::ostream &dest, std::string_view src) const = 0;
|
||||
|
||||
std::string from_utf8(const std::string &src) const
|
||||
{
|
||||
// inlining so that the return value can potentially be removed by return value optimization
|
||||
std::ostringstream stream;
|
||||
from_utf8(stream, src);
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string to_utf8(const std::string &src) const
|
||||
{
|
||||
// inlining so that the return value can potentially be removed by return value optimization
|
||||
std::ostringstream stream;
|
||||
to_utf8(stream, src);
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// ======================> simple_charconverter
|
||||
|
||||
// a simple implementation of charconverter that simply defines a code page for 0x80-0xFF
|
||||
class simple_charconverter : public charconverter
|
||||
{
|
||||
public:
|
||||
simple_charconverter(const char32_t highpage[0x80], unicode_normalization_form norm = unicode_normalization_form::C)
|
||||
: simple_charconverter(nullptr, highpage, norm)
|
||||
{
|
||||
}
|
||||
|
||||
simple_charconverter(const char32_t lowpage[0x80], const char32_t highpage[0x80], unicode_normalization_form norm = unicode_normalization_form::C);
|
||||
|
||||
virtual void from_utf8(std::ostream &dest, std::string_view src) const override;
|
||||
virtual void to_utf8(std::ostream &dest, std::string_view src) const override;
|
||||
|
||||
private:
|
||||
std::vector<std::pair<char32_t, char> > m_reverse_lookup;
|
||||
unicode_normalization_form m_norm;
|
||||
const char32_t *m_lowpage;
|
||||
const char32_t *m_highpage;
|
||||
};
|
||||
|
||||
// exception that can be thrown from charconverter::from_utf8() if a character is illegal (charconverter::to_utf8() should never throw this)
|
||||
class charconverter_exception
|
||||
{
|
||||
};
|
||||
|
||||
extern simple_charconverter charconverter_iso_8859_1;
|
||||
};
|
||||
|
||||
|
||||
#endif // IMGTOOL_CHARCONV_H
|
File diff suppressed because it is too large
Load Diff
@ -1,108 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/****************************************************************************
|
||||
|
||||
filteoln.c
|
||||
|
||||
Native end-of-line filter
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include "imgtool.h"
|
||||
#include "filter.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#define EOLN (CRLF == 1 ? "\r" : (CRLF == 2 ? "\n" : (CRLF == 3 ? "\r\n" : NULL)))
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t convert_stream_eolns(imgtool::stream &source, imgtool::stream &dest, const char *eoln)
|
||||
{
|
||||
size_t len, i, pos;
|
||||
char buffer[2000];
|
||||
int hit_cr = false;
|
||||
|
||||
while((len = source.read(buffer, sizeof(buffer))) > 0)
|
||||
{
|
||||
pos = 0;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
switch(buffer[i])
|
||||
{
|
||||
case '\r':
|
||||
case '\n':
|
||||
if (!hit_cr || (buffer[i] != '\n'))
|
||||
{
|
||||
if (i > pos)
|
||||
dest.write(buffer + pos, i - pos);
|
||||
dest.write(eoln, strlen(eoln));
|
||||
}
|
||||
pos = i + 1;
|
||||
break;
|
||||
}
|
||||
hit_cr = (buffer[i] == '\r');
|
||||
}
|
||||
|
||||
if (i > pos)
|
||||
dest.write(buffer + pos, i - pos);
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t ascii_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
imgtool::stream::ptr mem_stream;
|
||||
|
||||
mem_stream = imgtool::stream::open_mem(nullptr, 0);
|
||||
if (!mem_stream)
|
||||
return IMGTOOLERR_OUTOFMEMORY;
|
||||
|
||||
err = partition.read_file(filename, fork, *mem_stream, nullptr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mem_stream->seek(SEEK_SET, 0);
|
||||
return convert_stream_eolns(*mem_stream, destf, EOLN);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t ascii_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
imgtool::stream::ptr mem_stream;
|
||||
const char *eoln;
|
||||
|
||||
/* create a stream */
|
||||
mem_stream = imgtool::stream::open_mem(nullptr, 0);
|
||||
if (!mem_stream)
|
||||
return IMGTOOLERR_OUTOFMEMORY;
|
||||
|
||||
eoln = partition.get_info_string(IMGTOOLINFO_STR_EOLN);
|
||||
|
||||
err = convert_stream_eolns(sourcef, *mem_stream, eoln);
|
||||
if (err)
|
||||
return err;
|
||||
mem_stream->seek(SEEK_SET, 0);
|
||||
|
||||
return partition.write_file(filename, fork, *mem_stream, opts, nullptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void filter_eoln_getinfo(uint32_t state, union filterinfo *info)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case FILTINFO_STR_NAME: info->s = "ascii"; break;
|
||||
case FILTINFO_STR_HUMANNAME: info->s = "Ascii"; break;
|
||||
case FILTINFO_PTR_READFILE: info->read_file = ascii_readfile; break;
|
||||
case FILTINFO_PTR_WRITEFILE: info->write_file = ascii_writefile; break;
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/***************************************************************************
|
||||
|
||||
filter.c
|
||||
|
||||
Imgtool filters
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "filter.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
int64_t filter_get_info_int(filter_getinfoproc get_info, uint32_t state)
|
||||
{
|
||||
union filterinfo info;
|
||||
info.i = 0;
|
||||
get_info(state, &info);
|
||||
return info.i;
|
||||
}
|
||||
|
||||
void *filter_get_info_ptr(filter_getinfoproc get_info, uint32_t state)
|
||||
{
|
||||
union filterinfo info;
|
||||
info.p = nullptr;
|
||||
get_info(state, &info);
|
||||
return info.p;
|
||||
}
|
||||
|
||||
void *filter_get_info_fct(filter_getinfoproc get_info, uint32_t state)
|
||||
{
|
||||
union filterinfo info;
|
||||
info.f = nullptr;
|
||||
get_info(state, &info);
|
||||
return info.f;
|
||||
}
|
||||
|
||||
const char *filter_get_info_string(filter_getinfoproc get_info, uint32_t state)
|
||||
{
|
||||
union filterinfo info;
|
||||
info.s = nullptr;
|
||||
get_info(state, &info);
|
||||
return info.s;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
const filter_getinfoproc filters[] =
|
||||
{
|
||||
filter_eoln_getinfo,
|
||||
filter_cocobas_getinfo,
|
||||
filter_dragonbas_getinfo,
|
||||
filter_macbinary_getinfo,
|
||||
filter_vzsnapshot_getinfo,
|
||||
filter_vzbas_getinfo,
|
||||
filter_thombas5_getinfo,
|
||||
filter_thombas7_getinfo,
|
||||
filter_thombas128_getinfo,
|
||||
filter_thomcrypt_getinfo,
|
||||
filter_bml3bas_getinfo,
|
||||
filter_hp9845data_getinfo,
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
||||
|
||||
filter_getinfoproc filter_lookup(const char *name)
|
||||
{
|
||||
int i;
|
||||
const char *filter_name;
|
||||
|
||||
for (i = 0; filters[i]; i++)
|
||||
{
|
||||
filter_name = filter_get_info_string(filters[i], FILTINFO_STR_NAME);
|
||||
if (!strcmp(name, filter_name))
|
||||
return filters[i];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/***************************************************************************
|
||||
|
||||
filter.h
|
||||
|
||||
Imgtool filters
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef FILTER_H
|
||||
#define FILTER_H
|
||||
|
||||
#include "library.h"
|
||||
|
||||
struct imgtool_filter;
|
||||
|
||||
enum
|
||||
{
|
||||
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
||||
FILTINFO_INT_FIRST = 0x00000,
|
||||
FILTINFO_INT_STATESIZE,
|
||||
|
||||
/* --- the following bits of info are returned as pointers to data or functions --- */
|
||||
FILTINFO_PTR_FIRST = 0x10000,
|
||||
FILTINFO_PTR_READFILE,
|
||||
FILTINFO_PTR_WRITEFILE,
|
||||
FILTINFO_PTR_CHECKSTREAM,
|
||||
|
||||
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
||||
FILTINFO_STR_FIRST = 0x20000,
|
||||
FILTINFO_STR_NAME,
|
||||
FILTINFO_STR_HUMANNAME,
|
||||
FILTINFO_STR_EXTENSION
|
||||
};
|
||||
|
||||
extern const filter_getinfoproc filters[];
|
||||
|
||||
filter_getinfoproc filter_lookup(const char *name);
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
int64_t filter_get_info_int(filter_getinfoproc get_info, uint32_t state);
|
||||
void *filter_get_info_ptr(filter_getinfoproc get_info, uint32_t state);
|
||||
void *filter_get_info_fct(filter_getinfoproc get_info, uint32_t state);
|
||||
const char *filter_get_info_string(filter_getinfoproc get_info, uint32_t state);
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
extern void filter_eoln_getinfo(uint32_t state, union filterinfo *info);
|
||||
extern void filter_cocobas_getinfo(uint32_t state, union filterinfo *info);
|
||||
extern void filter_dragonbas_getinfo(uint32_t state, union filterinfo *info);
|
||||
extern void filter_macbinary_getinfo(uint32_t state, union filterinfo *info);
|
||||
extern void filter_vzsnapshot_getinfo(uint32_t state, union filterinfo *info);
|
||||
extern void filter_vzbas_getinfo(uint32_t state, union filterinfo *info);
|
||||
extern void filter_thombas5_getinfo(uint32_t state, union filterinfo *info);
|
||||
extern void filter_thombas7_getinfo(uint32_t state, union filterinfo *info);
|
||||
extern void filter_thombas128_getinfo(uint32_t state, union filterinfo *info);
|
||||
extern void filter_thomcrypt_getinfo(uint32_t state, union filterinfo *info);
|
||||
extern void filter_bml3bas_getinfo(uint32_t state, union filterinfo *info);
|
||||
extern void filter_hp9845data_getinfo(uint32_t state, union filterinfo *info);
|
||||
|
||||
#endif /* FILTER_H */
|
File diff suppressed because it is too large
Load Diff
@ -1,24 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/*********************************************************************
|
||||
|
||||
formats/coco_dsk.h
|
||||
|
||||
Tandy Color Computer / Dragon disk images
|
||||
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef COCO_DSK_H
|
||||
#define COCO_DSK_H
|
||||
|
||||
#include "formats/flopimg_legacy.h"
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
LEGACY_FLOPPY_OPTIONS_EXTERN(coco);
|
||||
|
||||
FLOPPY_IDENTIFY(coco_dmk_identify);
|
||||
FLOPPY_CONSTRUCT(coco_dmk_construct);
|
||||
|
||||
#endif /* COCO_DSK_H */
|
@ -1,140 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/*********************************************************************
|
||||
|
||||
formats/pc_dsk_legacy.cpp
|
||||
|
||||
PC disk images (legacy support for imgtool)
|
||||
|
||||
*********************************************************************/
|
||||
|
||||
#include "formats/pc_dsk_legacy.h"
|
||||
#include "formats/basicdsk.h"
|
||||
|
||||
#include "opresolv.h"
|
||||
|
||||
struct pc_disk_sizes
|
||||
{
|
||||
uint32_t image_size;
|
||||
int sectors;
|
||||
int heads;
|
||||
};
|
||||
|
||||
|
||||
|
||||
static const struct pc_disk_sizes disk_sizes[] =
|
||||
{
|
||||
{ 8*1*40*512, 8, 1}, /* 5 1/4 inch double density single sided */
|
||||
{ 8*2*40*512, 8, 2}, /* 5 1/4 inch double density */
|
||||
{ 9*1*40*512, 9, 1}, /* 5 1/4 inch double density single sided */
|
||||
{ 9*2*40*512, 9, 2}, /* 5 1/4 inch double density */
|
||||
{10*2*40*512, 10, 2}, /* 5 1/4 inch double density single sided */
|
||||
{ 9*2*80*512, 9, 2}, /* 80 tracks 5 1/4 inch drives rare in PCs */
|
||||
{ 9*2*80*512, 9, 2}, /* 3 1/2 inch double density */
|
||||
{15*2*80*512, 15, 2}, /* 5 1/4 inch high density (or japanese 3 1/2 inch high density) */
|
||||
{18*2*80*512, 18, 2}, /* 3 1/2 inch high density */
|
||||
{21*2*80*512, 21, 2}, /* 3 1/2 inch high density DMF */
|
||||
{36*2*80*512, 36, 2} /* 3 1/2 inch enhanced density */
|
||||
};
|
||||
|
||||
|
||||
|
||||
static floperr_t pc_dsk_compute_geometry(floppy_image_legacy *floppy, struct basicdsk_geometry *geometry)
|
||||
{
|
||||
int i;
|
||||
uint64_t size;
|
||||
|
||||
memset(geometry, 0, sizeof(*geometry));
|
||||
size = floppy_image_size(floppy);
|
||||
|
||||
for (i = 0; i < std::size(disk_sizes); i++)
|
||||
{
|
||||
if (disk_sizes[i].image_size == size)
|
||||
{
|
||||
geometry->sectors = disk_sizes[i].sectors;
|
||||
geometry->heads = disk_sizes[i].heads;
|
||||
geometry->sector_length = 512;
|
||||
geometry->first_sector_id = 1;
|
||||
geometry->tracks = (int) (size / disk_sizes[i].sectors / disk_sizes[i].heads / geometry->sector_length);
|
||||
return FLOPPY_ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
if (size >= 0x1a)
|
||||
{
|
||||
/*
|
||||
* get info from boot sector.
|
||||
* not correct on all disks
|
||||
*/
|
||||
uint8_t scl, spt, heads;
|
||||
floppy_image_read(floppy, &scl, 0x0c, 1);
|
||||
floppy_image_read(floppy, &spt, 0x18, 1);
|
||||
floppy_image_read(floppy, &heads, 0x1A, 1);
|
||||
|
||||
if (size == ((uint64_t) scl) * spt * heads * 0x200)
|
||||
{
|
||||
geometry->sectors = spt;
|
||||
geometry->heads = heads;
|
||||
geometry->sector_length = 512;
|
||||
geometry->first_sector_id = 1;
|
||||
geometry->tracks = scl;
|
||||
return FLOPPY_ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return FLOPPY_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static FLOPPY_IDENTIFY(pc_dsk_identify)
|
||||
{
|
||||
floperr_t err;
|
||||
struct basicdsk_geometry geometry;
|
||||
|
||||
err = pc_dsk_compute_geometry(floppy, &geometry);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*vote = geometry.heads ? 100 : 0;
|
||||
return FLOPPY_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static FLOPPY_CONSTRUCT(pc_dsk_construct)
|
||||
{
|
||||
floperr_t err;
|
||||
struct basicdsk_geometry geometry;
|
||||
|
||||
if (params)
|
||||
{
|
||||
/* create */
|
||||
memset(&geometry, 0, sizeof(geometry));
|
||||
geometry.heads = params->lookup_int(PARAM_HEADS);
|
||||
geometry.tracks = params->lookup_int(PARAM_TRACKS);
|
||||
geometry.sectors = params->lookup_int(PARAM_SECTORS);
|
||||
geometry.first_sector_id = 1;
|
||||
geometry.sector_length = 512;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* open */
|
||||
err = pc_dsk_compute_geometry(floppy, &geometry);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return basicdsk_construct(floppy, &geometry);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
LEGACY_FLOPPY_OPTIONS_START( pc )
|
||||
LEGACY_FLOPPY_OPTION( pc_dsk, "dsk,ima,img,ufi,360", "PC floppy disk image", pc_dsk_identify, pc_dsk_construct, nullptr,
|
||||
HEADS([1]-2)
|
||||
TRACKS(40/[80])
|
||||
SECTORS(8/[9]/10/15/18/36))
|
||||
LEGACY_FLOPPY_OPTIONS_END
|
@ -1,21 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/*********************************************************************
|
||||
|
||||
formats/pc_dsk_legacy.h
|
||||
|
||||
PC disk images (legacy support for imgtool)
|
||||
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef PC_DSK_LEGACY_H
|
||||
#define PC_DSK_LEGACY_H
|
||||
|
||||
#include "formats/flopimg_legacy.h"
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
LEGACY_FLOPPY_OPTIONS_EXTERN(pc);
|
||||
|
||||
#endif /* PC_DSK_LEGACY_H */
|
@ -1,58 +0,0 @@
|
||||
// license:GPL-2.0+
|
||||
// copyright-holders:Juergen Buchmueller
|
||||
/*********************************************************************
|
||||
|
||||
formats/vt_dsk_legacy.cpp
|
||||
|
||||
VTech Laser/VZ disk images (legacy support)
|
||||
|
||||
*********************************************************************/
|
||||
|
||||
#include "formats/vt_dsk_legacy.h"
|
||||
#include "formats/basicdsk.h"
|
||||
|
||||
#include "opresolv.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
static FLOPPY_IDENTIFY(vz_identify)
|
||||
{
|
||||
uint64_t size = floppy_image_size(floppy);
|
||||
*vote = ((size == 98560) || (size == 99200) || (size == 99184)) ? 100 : 0;
|
||||
return FLOPPY_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static FLOPPY_CONSTRUCT(vz_construct)
|
||||
{
|
||||
struct basicdsk_geometry geometry;
|
||||
memset(&geometry, 0, sizeof(geometry));
|
||||
|
||||
if (params)
|
||||
{
|
||||
geometry.heads = params->lookup_int(PARAM_HEADS);
|
||||
geometry.tracks = params->lookup_int(PARAM_TRACKS);
|
||||
geometry.sectors = params->lookup_int(PARAM_SECTORS);
|
||||
geometry.first_sector_id = params->lookup_int(PARAM_FIRST_SECTOR_ID);
|
||||
geometry.sector_length = params->lookup_int(PARAM_SECTOR_LENGTH);
|
||||
}
|
||||
else
|
||||
{
|
||||
geometry.heads = 1;
|
||||
geometry.tracks = 40;
|
||||
geometry.sectors = 16;
|
||||
geometry.first_sector_id = 0;
|
||||
geometry.sector_length = floppy_image_size(floppy)/geometry.tracks/geometry.sectors;
|
||||
}
|
||||
|
||||
return basicdsk_construct(floppy, &geometry);
|
||||
}
|
||||
|
||||
|
||||
LEGACY_FLOPPY_OPTIONS_START(vz)
|
||||
LEGACY_FLOPPY_OPTION(vtech1, "dsk", "Laser/VZ disk image", vz_identify, vz_construct, NULL,
|
||||
HEADS([1])
|
||||
TRACKS([40])
|
||||
SECTORS([16])
|
||||
SECTOR_LENGTH([154])
|
||||
FIRST_SECTOR_ID([0]))
|
||||
LEGACY_FLOPPY_OPTIONS_END
|
@ -1,18 +0,0 @@
|
||||
// license:GPL-2.0+
|
||||
// copyright-holders:Juergen Buchmueller
|
||||
/*********************************************************************
|
||||
|
||||
formats/vt_dsk_legacy.h
|
||||
|
||||
VTech Laser/VZ disk images (legacy support)
|
||||
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef VT_DSK_LEGACY_H
|
||||
#define VT_DSK_LEGACY_H
|
||||
|
||||
#include "formats/flopimg_legacy.h"
|
||||
|
||||
LEGACY_FLOPPY_OPTIONS_EXTERN(vz);
|
||||
|
||||
#endif /* VT_DSK_LEGACY_H */
|
@ -1,324 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/*********************************************************************
|
||||
|
||||
iflopimg.c
|
||||
|
||||
Bridge code for Imgtool into the standard floppy code
|
||||
|
||||
*********************************************************************/
|
||||
|
||||
#include "iflopimg.h"
|
||||
|
||||
#include "imgtool.h"
|
||||
#include "library.h"
|
||||
|
||||
#include "ioprocs.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
|
||||
imgtoolerr_t imgtool_floppy_error(floperr_t err)
|
||||
{
|
||||
switch(err)
|
||||
{
|
||||
case FLOPPY_ERROR_SUCCESS:
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
|
||||
case FLOPPY_ERROR_OUTOFMEMORY:
|
||||
return IMGTOOLERR_OUTOFMEMORY;
|
||||
|
||||
case FLOPPY_ERROR_INVALIDIMAGE:
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
|
||||
case FLOPPY_ERROR_SEEKERROR:
|
||||
return IMGTOOLERR_SEEKERROR;
|
||||
|
||||
case FLOPPY_ERROR_UNSUPPORTED:
|
||||
return IMGTOOLERR_UNIMPLEMENTED;
|
||||
|
||||
default:
|
||||
return IMGTOOLERR_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
Imgtool handlers
|
||||
*********************************************************************/
|
||||
|
||||
struct imgtool_floppy_image
|
||||
{
|
||||
floppy_image_legacy *floppy;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t imgtool_floppy_open_internal(imgtool::image &image, imgtool::stream::ptr &&stream)
|
||||
{
|
||||
floperr_t ferr;
|
||||
imgtoolerr_t err = IMGTOOLERR_SUCCESS;
|
||||
struct imgtool_floppy_image *fimg;
|
||||
const imgtool_class *imgclass;
|
||||
const struct FloppyFormat *format;
|
||||
imgtoolerr_t (*open)(imgtool::image &image, imgtool::stream *f);
|
||||
|
||||
fimg = (struct imgtool_floppy_image *) image.extra_bytes();
|
||||
imgclass = &image.module().imgclass;
|
||||
format = (const struct FloppyFormat *) imgclass->derived_param;
|
||||
open = (imgtoolerr_t (*)(imgtool::image &, imgtool::stream *)) imgtool_get_info_ptr(imgclass, IMGTOOLINFO_PTR_FLOPPY_OPEN);
|
||||
|
||||
// open up the floppy
|
||||
ferr = floppy_open(imgtool::stream_read_write(std::move(stream), 0xff), "", format, FLOPPY_FLAGS_READWRITE, &fimg->floppy);
|
||||
if (ferr)
|
||||
{
|
||||
err = imgtool_floppy_error(ferr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (open)
|
||||
{
|
||||
err = open(image, nullptr);
|
||||
if (err)
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if (err && fimg->floppy)
|
||||
{
|
||||
floppy_close(fimg->floppy);
|
||||
fimg->floppy = nullptr;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t imgtool_floppy_open(imgtool::image &image, imgtool::stream::ptr &&stream)
|
||||
{
|
||||
return imgtool_floppy_open_internal(image, std::move(stream));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t imgtool_floppy_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts)
|
||||
{
|
||||
floperr_t ferr;
|
||||
imgtoolerr_t err = IMGTOOLERR_SUCCESS;
|
||||
struct imgtool_floppy_image *fimg;
|
||||
const imgtool_class *imgclass;
|
||||
const struct FloppyFormat *format;
|
||||
imgtoolerr_t (*create)(imgtool::image &, imgtool::stream *, util::option_resolution *);
|
||||
imgtoolerr_t (*open)(imgtool::image &, imgtool::stream *f);
|
||||
|
||||
fimg = (struct imgtool_floppy_image *) image.extra_bytes();
|
||||
imgclass = &image.module().imgclass;
|
||||
format = (const struct FloppyFormat *) imgclass->derived_param;
|
||||
create = (imgtoolerr_t (*)(imgtool::image &, imgtool::stream *, util::option_resolution *)) imgtool_get_info_ptr(imgclass, IMGTOOLINFO_PTR_FLOPPY_CREATE);
|
||||
open = (imgtoolerr_t (*)(imgtool::image &, imgtool::stream *)) imgtool_get_info_ptr(imgclass, IMGTOOLINFO_PTR_FLOPPY_OPEN);
|
||||
|
||||
// open up the floppy
|
||||
ferr = floppy_create(imgtool::stream_read_write(std::move(stream), 0xff), format, opts, &fimg->floppy);
|
||||
if (ferr)
|
||||
{
|
||||
err = imgtool_floppy_error(ferr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
// do we have to do extra stuff when creating the image?
|
||||
if (create)
|
||||
{
|
||||
err = create(image, nullptr, opts);
|
||||
if (err)
|
||||
goto done;
|
||||
}
|
||||
|
||||
// do we have to do extra stuff when opening the image?
|
||||
if (open)
|
||||
{
|
||||
err = open(image, nullptr);
|
||||
if (err)
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if (err && fimg->floppy)
|
||||
{
|
||||
floppy_close(fimg->floppy);
|
||||
fimg->floppy = nullptr;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void imgtool_floppy_close(imgtool::image &img)
|
||||
{
|
||||
floppy_close(imgtool_floppy(img));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t imgtool_floppy_read_sector(imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, std::vector<uint8_t> &buffer)
|
||||
{
|
||||
floperr_t ferr;
|
||||
uint32_t sector_size;
|
||||
|
||||
// get the sector length
|
||||
ferr = floppy_get_sector_length(imgtool_floppy(image), head, track, sector, §or_size);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
// resize the buffer accordingly
|
||||
try { buffer.resize(sector_size); }
|
||||
catch (std::bad_alloc const &) { return IMGTOOLERR_OUTOFMEMORY; }
|
||||
|
||||
// and read the sector
|
||||
ferr = floppy_read_sector(imgtool_floppy(image), head, track, sector, 0, &buffer[0], sector_size);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t imgtool_floppy_write_sector(imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, const void *buffer, size_t len, int ddam)
|
||||
{
|
||||
floperr_t ferr;
|
||||
|
||||
ferr = floppy_write_sector(imgtool_floppy(image), head, track, sector, 0, buffer, len, ddam);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void imgtool_floppy_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
|
||||
{
|
||||
const struct FloppyFormat *format;
|
||||
imgtool_class derived_class;
|
||||
|
||||
format = (const struct FloppyFormat *) imgclass->derived_param;
|
||||
memset(&derived_class, 0, sizeof(derived_class));
|
||||
derived_class.get_info = imgclass->derived_get_info;
|
||||
|
||||
switch(state)
|
||||
{
|
||||
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
||||
case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES:
|
||||
info->i = sizeof(struct imgtool_floppy_image) +
|
||||
imgtool_get_info_int(&derived_class, IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES);
|
||||
break;
|
||||
|
||||
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
||||
case IMGTOOLINFO_STR_NAME:
|
||||
sprintf(info->s = imgtool_temp_str(), "%s_%s", format->name,
|
||||
imgtool_get_info_string(&derived_class, IMGTOOLINFO_STR_NAME));
|
||||
break;
|
||||
case IMGTOOLINFO_STR_DESCRIPTION:
|
||||
sprintf(info->s = imgtool_temp_str(), "%s (%s)", format->description,
|
||||
imgtool_get_info_string(&derived_class, IMGTOOLINFO_STR_DESCRIPTION));
|
||||
break;
|
||||
case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), format->extensions); break;
|
||||
case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: info->p = (void*)format->param_guidelines; break;
|
||||
|
||||
/* --- the following bits of info are returned as pointers to data or functions --- */
|
||||
case IMGTOOLINFO_PTR_OPEN: info->open = imgtool_floppy_open; break;
|
||||
case IMGTOOLINFO_PTR_CREATE: info->create = imgtool_floppy_create; break;
|
||||
case IMGTOOLINFO_PTR_CLOSE: info->close = imgtool_floppy_close; break;
|
||||
case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: info->createimage_optguide = format->param_guidelines ? &floppy_option_guide() : nullptr; break;
|
||||
case IMGTOOLINFO_PTR_READ_SECTOR: info->read_sector = imgtool_floppy_read_sector; break;
|
||||
case IMGTOOLINFO_PTR_WRITE_SECTOR: info->write_sector = imgtool_floppy_write_sector; break;
|
||||
|
||||
default: imgclass->derived_get_info(imgclass, state, info); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int imgtool_floppy_make_class(int index, imgtool_class *imgclass)
|
||||
{
|
||||
const struct FloppyFormat *format;
|
||||
|
||||
/* get the format */
|
||||
format = (const struct FloppyFormat *)
|
||||
imgtool_get_info_ptr(imgclass, IMGTOOLINFO_PTR_FLOPPY_FORMAT);
|
||||
assert(format);
|
||||
if (!format[index].construct)
|
||||
return false;
|
||||
|
||||
imgclass->derived_get_info = imgclass->get_info;
|
||||
imgclass->get_info = imgtool_floppy_get_info;
|
||||
imgclass->derived_param = (void *) &format[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
floppy_image_legacy *imgtool_floppy(imgtool::image &img)
|
||||
{
|
||||
struct imgtool_floppy_image *fimg;
|
||||
fimg = (struct imgtool_floppy_image *) img.extra_bytes();
|
||||
return fimg->floppy;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t imgtool_floppy_transfer_sector_tofrom_stream(imgtool::image &img, int head, int track, int sector, int offset, size_t length, imgtool::stream &f, int direction)
|
||||
{
|
||||
floperr_t err;
|
||||
floppy_image_legacy *floppy;
|
||||
std::vector<uint8_t> buffer;
|
||||
|
||||
floppy = imgtool_floppy(img);
|
||||
|
||||
buffer.resize(length);
|
||||
|
||||
if (direction)
|
||||
{
|
||||
err = floppy_read_sector(floppy, head, track, sector, offset, &buffer[0], length);
|
||||
if (err)
|
||||
goto done;
|
||||
f.write(&buffer[0], length);
|
||||
}
|
||||
else
|
||||
{
|
||||
f.read(&buffer[0], length);
|
||||
err = floppy_write_sector(floppy, head, track, sector, offset, &buffer[0], length, 0); /* TODO: pass ddam argument from imgtool */
|
||||
if (err)
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = FLOPPY_ERROR_SUCCESS;
|
||||
|
||||
done:
|
||||
return imgtool_floppy_error(err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
imgtoolerr_t imgtool_floppy_read_sector_to_stream(imgtool::image &img, int head, int track, int sector, int offset, size_t length, imgtool::stream &f)
|
||||
{
|
||||
return imgtool_floppy_transfer_sector_tofrom_stream(img, head, track, sector, offset, length, f, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
imgtoolerr_t imgtool_floppy_write_sector_from_stream(imgtool::image &img, int head, int track, int sector, int offset, size_t length, imgtool::stream &f)
|
||||
{
|
||||
return imgtool_floppy_transfer_sector_tofrom_stream(img, head, track, sector, offset, length, f, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void *imgtool_floppy_extrabytes(imgtool::image &img)
|
||||
{
|
||||
struct imgtool_floppy_image *fimg;
|
||||
fimg = (struct imgtool_floppy_image *) img.extra_bytes();
|
||||
return fimg + 1;
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/*********************************************************************
|
||||
|
||||
iflopimg.h
|
||||
|
||||
Bridge code for Imgtool into the standard floppy code
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef MAME_TOOLS_IMGTOOL_IFLOPIMG_H
|
||||
#define MAME_TOOLS_IMGTOOL_IFLOPIMG_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "library.h"
|
||||
|
||||
#include "formats/flopimg_legacy.h"
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
|
||||
Prototypes
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
enum
|
||||
{
|
||||
IMGTOOLINFO_PTR_FLOPPY_FORMAT = IMGTOOLINFO_PTR_CLASS_SPECIFIC,
|
||||
IMGTOOLINFO_PTR_FLOPPY_OPEN,
|
||||
IMGTOOLINFO_PTR_FLOPPY_CREATE
|
||||
};
|
||||
|
||||
int imgtool_floppy_make_class(int index, imgtool_class *imgclass);
|
||||
|
||||
floppy_image_legacy *imgtool_floppy(imgtool::image &img);
|
||||
imgtoolerr_t imgtool_floppy_error(floperr_t err);
|
||||
|
||||
imgtoolerr_t imgtool_floppy_read_sector_to_stream(imgtool::image &img, int head, int track, int sector, int offset, size_t length, imgtool::stream &f);
|
||||
imgtoolerr_t imgtool_floppy_write_sector_from_stream(imgtool::image &img, int head, int track, int sector, int offset, size_t length, imgtool::stream &f);
|
||||
|
||||
|
||||
void *imgtool_floppy_extrabytes(imgtool::image &img);
|
||||
|
||||
#endif // MAME_TOOLS_IMGTOOL_IFLOPIMG_H
|
@ -1,250 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods, Raphael Nabet
|
||||
/*
|
||||
Code to interface the MESS image code with MAME's harddisk core.
|
||||
|
||||
We do not support diff files as it will involve some changes in the MESS
|
||||
image code.
|
||||
|
||||
Raphael Nabet 2003
|
||||
*/
|
||||
|
||||
#include "imghd.h"
|
||||
#include "library.h"
|
||||
#include "stream.h"
|
||||
|
||||
#include "harddisk.h"
|
||||
#include "opresolv.h"
|
||||
|
||||
|
||||
static imgtoolerr_t map_chd_error(std::error_condition chderr)
|
||||
{
|
||||
if (!chderr)
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
else if (std::errc::not_enough_memory == chderr)
|
||||
return IMGTOOLERR_OUTOFMEMORY;
|
||||
else if (chd_file::error::FILE_NOT_WRITEABLE == chderr)
|
||||
return IMGTOOLERR_READONLY;
|
||||
else
|
||||
return IMGTOOLERR_UNEXPECTED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
imghd_create()
|
||||
|
||||
Create a MAME HD image
|
||||
*/
|
||||
imgtoolerr_t imghd_create(imgtool::stream &stream, uint32_t hunksize, uint32_t cylinders, uint32_t heads, uint32_t sectors, uint32_t seclen)
|
||||
{
|
||||
imgtoolerr_t err = IMGTOOLERR_SUCCESS;
|
||||
chd_file chd;
|
||||
std::error_condition rc;
|
||||
chd_codec_type compression[4] = { CHD_CODEC_NONE };
|
||||
|
||||
/* sanity check args -- see parse_hunk_size() in src/lib/util/chd.cpp */
|
||||
if (hunksize > (1024 * 1024))
|
||||
{
|
||||
err = IMGTOOLERR_PARAMCORRUPT;
|
||||
return err;
|
||||
}
|
||||
if (hunksize <= 0)
|
||||
hunksize = 4096; /* default value */
|
||||
|
||||
/* bail if we are read only */
|
||||
if (stream.is_read_only())
|
||||
{
|
||||
err = IMGTOOLERR_READONLY;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* calculations */
|
||||
const uint64_t logicalbytes = (uint64_t)cylinders * heads * sectors * seclen;
|
||||
|
||||
/* create the new hard drive */
|
||||
rc = chd.create(stream_read_write(stream, 0), logicalbytes, hunksize, seclen, compression);
|
||||
if (rc)
|
||||
{
|
||||
err = map_chd_error(rc);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* write the metadata */
|
||||
const std::string metadata = util::string_format(HARD_DISK_METADATA_FORMAT, cylinders, heads, sectors, seclen);
|
||||
rc = chd.write_metadata(HARD_DISK_METADATA_TAG, 0, metadata);
|
||||
if (rc)
|
||||
{
|
||||
err = map_chd_error(rc);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* alloc and zero buffer */
|
||||
std::vector<uint8_t> cache;
|
||||
cache.resize(hunksize);
|
||||
memset(&cache[0], 0, hunksize);
|
||||
|
||||
/* zero out every hunk */
|
||||
const int totalhunks = (logicalbytes + hunksize - 1) / hunksize;
|
||||
for (int hunknum = 0; hunknum < totalhunks; hunknum++)
|
||||
{
|
||||
rc = chd.write_units(hunknum, &cache[0]);
|
||||
if (rc)
|
||||
{
|
||||
err = IMGTOOLERR_WRITEERROR;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
imghd_open()
|
||||
|
||||
Open stream as a MAME HD image
|
||||
*/
|
||||
imgtoolerr_t imghd_open(imgtool::stream &stream, struct mess_hard_disk_file *hard_disk)
|
||||
{
|
||||
std::error_condition chderr;
|
||||
imgtoolerr_t err = IMGTOOLERR_SUCCESS;
|
||||
|
||||
hard_disk->hard_disk = nullptr;
|
||||
|
||||
chderr = hard_disk->chd.open(stream_read_write(stream, 0), !stream.is_read_only());
|
||||
if (chderr)
|
||||
{
|
||||
err = map_chd_error(chderr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
hard_disk->hard_disk = hard_disk_open(&hard_disk->chd);
|
||||
if (!hard_disk->hard_disk)
|
||||
{
|
||||
err = IMGTOOLERR_UNEXPECTED;
|
||||
goto done;
|
||||
}
|
||||
hard_disk->stream = &stream;
|
||||
|
||||
done:
|
||||
if (err)
|
||||
imghd_close(hard_disk);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
imghd_close()
|
||||
|
||||
Close MAME HD image
|
||||
*/
|
||||
void imghd_close(struct mess_hard_disk_file *disk)
|
||||
{
|
||||
if (disk->hard_disk)
|
||||
{
|
||||
hard_disk_close(disk->hard_disk);
|
||||
disk->hard_disk = nullptr;
|
||||
}
|
||||
if (disk->stream)
|
||||
{
|
||||
delete disk->stream;
|
||||
disk->stream = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
imghd_read()
|
||||
|
||||
Read sector(s) from MAME HD image
|
||||
*/
|
||||
imgtoolerr_t imghd_read(struct mess_hard_disk_file *disk, uint32_t lbasector, void *buffer)
|
||||
{
|
||||
uint32_t reply = hard_disk_read(disk->hard_disk, lbasector, buffer);
|
||||
return reply ? IMGTOOLERR_SUCCESS : IMGTOOLERR_READERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
imghd_write()
|
||||
|
||||
Write sector(s) from MAME HD image
|
||||
*/
|
||||
imgtoolerr_t imghd_write(struct mess_hard_disk_file *disk, uint32_t lbasector, const void *buffer)
|
||||
{
|
||||
uint32_t reply = hard_disk_write(disk->hard_disk, lbasector, buffer);
|
||||
return reply ? IMGTOOLERR_SUCCESS : IMGTOOLERR_WRITEERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
imghd_get_header()
|
||||
|
||||
Return pointer to the header of MAME HD image
|
||||
*/
|
||||
const hard_disk_info *imghd_get_header(struct mess_hard_disk_file *disk)
|
||||
{
|
||||
const hard_disk_info *reply;
|
||||
reply = hard_disk_get_info(disk->hard_disk);
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
static imgtoolerr_t mess_hd_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions);
|
||||
|
||||
enum
|
||||
{
|
||||
mess_hd_createopts_blocksize = 'B',
|
||||
mess_hd_createopts_cylinders = 'C',
|
||||
mess_hd_createopts_heads = 'D',
|
||||
mess_hd_createopts_sectors = 'E',
|
||||
mess_hd_createopts_seclen = 'F'
|
||||
};
|
||||
|
||||
OPTION_GUIDE_START( mess_hd_create_optionguide )
|
||||
OPTION_INT(mess_hd_createopts_blocksize, "blocksize", "Sectors Per Block" )
|
||||
OPTION_INT(mess_hd_createopts_cylinders, "cylinders", "Cylinders" )
|
||||
OPTION_INT(mess_hd_createopts_heads, "heads", "Heads" )
|
||||
OPTION_INT(mess_hd_createopts_sectors, "sectors", "Total Sectors" )
|
||||
OPTION_INT(mess_hd_createopts_seclen, "seclen", "Sector Bytes" )
|
||||
OPTION_GUIDE_END
|
||||
|
||||
#define mess_hd_create_optionspecs "B1-[4]-2048;C1-[32]-65536;D1-[8]-64;E1-[128]-4096;F128/256/[512]/1024/2048/4096/8192/16384/32768/65536"
|
||||
|
||||
|
||||
void hd_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "mess_hd"); break;
|
||||
case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "MESS hard disk image"); break;
|
||||
case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "hd"); break;
|
||||
|
||||
case IMGTOOLINFO_PTR_CREATE: info->create = mess_hd_image_create; break;
|
||||
|
||||
case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: info->createimage_optguide = &mess_hd_create_optionguide; break;
|
||||
case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), mess_hd_create_optionspecs); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t mess_hd_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions)
|
||||
{
|
||||
uint32_t blocksize, cylinders, heads, sectors, seclen;
|
||||
|
||||
/* read options */
|
||||
blocksize = createoptions->lookup_int(mess_hd_createopts_blocksize);
|
||||
cylinders = createoptions->lookup_int(mess_hd_createopts_cylinders);
|
||||
heads = createoptions->lookup_int(mess_hd_createopts_heads);
|
||||
sectors = createoptions->lookup_int(mess_hd_createopts_sectors);
|
||||
seclen = createoptions->lookup_int(mess_hd_createopts_seclen);
|
||||
|
||||
return imghd_create(*stream.get(), blocksize * seclen, cylinders, heads, sectors, seclen);
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/****************************************************************************
|
||||
|
||||
imghd.h
|
||||
|
||||
Bridge between Imgtool and CHD hard disk images
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef IMGHD_H
|
||||
#define IMGHD_H
|
||||
|
||||
#include "imgterrs.h"
|
||||
|
||||
#include "harddisk.h"
|
||||
|
||||
|
||||
namespace imgtool
|
||||
{
|
||||
class stream;
|
||||
}
|
||||
|
||||
struct mess_hard_disk_file
|
||||
{
|
||||
imgtool::stream *stream;
|
||||
hard_disk_file *hard_disk;
|
||||
chd_file chd;
|
||||
};
|
||||
|
||||
|
||||
/* create a new hard disk */
|
||||
imgtoolerr_t imghd_create(imgtool::stream &stream, uint32_t blocksize, uint32_t cylinders, uint32_t heads, uint32_t sectors, uint32_t seclen);
|
||||
|
||||
/* opens a hard disk given an Imgtool stream */
|
||||
imgtoolerr_t imghd_open(imgtool::stream &stream, mess_hard_disk_file *hard_disk);
|
||||
|
||||
/* close a hard disk */
|
||||
void imghd_close(struct mess_hard_disk_file *disk);
|
||||
|
||||
/* reads data from a hard disk */
|
||||
imgtoolerr_t imghd_read(struct mess_hard_disk_file *disk, uint32_t lbasector, void *buffer);
|
||||
|
||||
/* writes data to a hard disk */
|
||||
imgtoolerr_t imghd_write(struct mess_hard_disk_file *disk, uint32_t lbasector, const void *buffer);
|
||||
|
||||
/* gets the header from a hard disk */
|
||||
const hard_disk_info *imghd_get_header(struct mess_hard_disk_file *disk);
|
||||
|
||||
#endif /* IMGHD_H */
|
@ -1,56 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/***************************************************************************
|
||||
|
||||
imgterrs.c
|
||||
|
||||
Imgtool errors
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "imgterrs.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
|
||||
static const char *const msgs[] =
|
||||
{
|
||||
"Out of memory",
|
||||
"Unexpected error",
|
||||
"Argument too long",
|
||||
"Read error",
|
||||
"Write error",
|
||||
"Image is read only",
|
||||
"Corrupt image",
|
||||
"Corrupt file",
|
||||
"Corrupt directory",
|
||||
"File not found",
|
||||
"Unrecognized format",
|
||||
"Not implemented",
|
||||
"Parameter too small",
|
||||
"Parameter too large",
|
||||
"Missing parameter not found",
|
||||
"Inappropriate parameter",
|
||||
"Invalid parameter",
|
||||
"Bad file name",
|
||||
"Out of space on image",
|
||||
"Input past end of file",
|
||||
"Cannot specify path",
|
||||
"Invalid path",
|
||||
"Path not found",
|
||||
"Directory not empty",
|
||||
"Seek error",
|
||||
"File system does not support forks",
|
||||
"Fork not found",
|
||||
"Invalid partition"
|
||||
};
|
||||
|
||||
|
||||
|
||||
const char *imgtool_error(imgtoolerr_t err)
|
||||
{
|
||||
err = (imgtoolerr_t)(ERRORCODE(err) - 1);
|
||||
assert(err >= 0);
|
||||
assert(err < std::size(msgs));
|
||||
return msgs[err];
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/***************************************************************************
|
||||
|
||||
imgterrs.h
|
||||
|
||||
Imgtool errors
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef IMGTERRS_H
|
||||
#define IMGTERRS_H
|
||||
|
||||
/* Error codes */
|
||||
enum imgtoolerr_t
|
||||
{
|
||||
IMGTOOLERR_SUCCESS,
|
||||
IMGTOOLERR_OUTOFMEMORY,
|
||||
IMGTOOLERR_UNEXPECTED,
|
||||
IMGTOOLERR_BUFFERTOOSMALL,
|
||||
IMGTOOLERR_READERROR,
|
||||
IMGTOOLERR_WRITEERROR,
|
||||
IMGTOOLERR_READONLY,
|
||||
IMGTOOLERR_CORRUPTIMAGE,
|
||||
IMGTOOLERR_CORRUPTFILE,
|
||||
IMGTOOLERR_CORRUPTDIR,
|
||||
IMGTOOLERR_FILENOTFOUND,
|
||||
IMGTOOLERR_MODULENOTFOUND,
|
||||
IMGTOOLERR_UNIMPLEMENTED,
|
||||
IMGTOOLERR_PARAMTOOSMALL,
|
||||
IMGTOOLERR_PARAMTOOLARGE,
|
||||
IMGTOOLERR_PARAMNEEDED,
|
||||
IMGTOOLERR_PARAMNOTNEEDED,
|
||||
IMGTOOLERR_PARAMCORRUPT,
|
||||
IMGTOOLERR_BADFILENAME,
|
||||
IMGTOOLERR_NOSPACE,
|
||||
IMGTOOLERR_INPUTPASTEND,
|
||||
IMGTOOLERR_CANNOTUSEPATH,
|
||||
IMGTOOLERR_INVALIDPATH,
|
||||
IMGTOOLERR_PATHNOTFOUND,
|
||||
IMGTOOLERR_DIRNOTEMPTY,
|
||||
IMGTOOLERR_SEEKERROR,
|
||||
IMGTOOLERR_NOFORKS,
|
||||
IMGTOOLERR_FORKNOTFOUND,
|
||||
IMGTOOLERR_INVALIDPARTITION
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* These error codes are actually modifiers that make it easier to distinguish
|
||||
* the cause of an error
|
||||
*
|
||||
* Note - drivers should not use these modifiers
|
||||
*/
|
||||
#define IMGTOOLERR_SRC_MODULE 0x1000
|
||||
#define IMGTOOLERR_SRC_FUNCTIONALITY 0x2000
|
||||
#define IMGTOOLERR_SRC_IMAGEFILE 0x3000
|
||||
#define IMGTOOLERR_SRC_FILEONIMAGE 0x4000
|
||||
#define IMGTOOLERR_SRC_NATIVEFILE 0x5000
|
||||
|
||||
#define ERRORCODE(err) ((err) & 0x0fff)
|
||||
#define ERRORSOURCE(err) ((err) & 0xf000)
|
||||
#define ERRORPARAM(err) (((err) & 0xf0000) / 0x10000)
|
||||
|
||||
#define PARAM_TO_ERROR(errcode, param) ((errcode) | ((param) * 0x10000))
|
||||
|
||||
|
||||
const char *imgtool_error(imgtoolerr_t err);
|
||||
|
||||
#endif /* IMGTERRS_H */
|
File diff suppressed because it is too large
Load Diff
@ -1,265 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/***************************************************************************
|
||||
|
||||
imgtool.h
|
||||
|
||||
Main headers for Imgtool core
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef IMGTOOL_H
|
||||
#define IMGTOOL_H
|
||||
|
||||
#include "library.h"
|
||||
#include "stream.h"
|
||||
|
||||
#include "osdcomm.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
#define EOLN_CR "\x0d"
|
||||
#define EOLN_LF "\x0a"
|
||||
#define EOLN_CRLF "\x0d\x0a"
|
||||
|
||||
#define FILENAME_BOOTBLOCK ((const char *) 1)
|
||||
|
||||
enum
|
||||
{
|
||||
OSD_FOPEN_READ,
|
||||
OSD_FOPEN_WRITE,
|
||||
OSD_FOPEN_RW,
|
||||
OSD_FOPEN_RW_CREATE
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* Image calls
|
||||
*
|
||||
* These are the calls that front ends should use for manipulating images. You
|
||||
* should never call the module functions directly because they may not be
|
||||
* implemented (i.e. - the function pointers are NULL). The img_* functions are
|
||||
* aware of these issues and will make the appropriate checks as well as
|
||||
* marking up return codes with the source. In addition, some of the img_*
|
||||
* calls are high level calls that simply image manipulation
|
||||
*
|
||||
* Calls that return 'int' that are not explicitly noted otherwise return
|
||||
* imgtool error codes
|
||||
* ---------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
struct imgtool_module_features
|
||||
{
|
||||
unsigned int supports_create : 1;
|
||||
unsigned int supports_open : 1;
|
||||
unsigned int supports_readsector : 1;
|
||||
unsigned int supports_writesector : 1;
|
||||
unsigned int is_read_only : 1;
|
||||
};
|
||||
|
||||
struct imgtool_partition_features
|
||||
{
|
||||
unsigned int supports_reading : 1;
|
||||
unsigned int supports_writing : 1;
|
||||
unsigned int supports_deletefile : 1;
|
||||
unsigned int supports_directories : 1;
|
||||
unsigned int supports_freespace : 1;
|
||||
unsigned int supports_createdir : 1;
|
||||
unsigned int supports_deletedir : 1;
|
||||
unsigned int supports_creation_time : 1;
|
||||
unsigned int supports_lastmodified_time : 1;
|
||||
unsigned int supports_forks : 1;
|
||||
unsigned int supports_geticoninfo : 1;
|
||||
unsigned int is_read_only : 1;
|
||||
};
|
||||
|
||||
/* ----- initialization and basics ----- */
|
||||
void imgtool_init(bool omit_untested, void (*warning)(const char *message));
|
||||
void imgtool_exit(void);
|
||||
const imgtool_module *imgtool_find_module(const std::string &modulename);
|
||||
const imgtool::library::modulelist &imgtool_get_modules();
|
||||
imgtool_module_features imgtool_get_module_features(const imgtool_module *module);
|
||||
void imgtool_warn(const char *format, ...) ATTR_PRINTF(1,2);
|
||||
|
||||
// ----- image management -----
|
||||
namespace imgtool
|
||||
{
|
||||
class image
|
||||
{
|
||||
public:
|
||||
typedef std::unique_ptr<image> ptr;
|
||||
|
||||
image(const imgtool_module &module);
|
||||
~image();
|
||||
|
||||
static imgtoolerr_t identify_file(const char *filename, imgtool_module **modules, size_t count);
|
||||
static imgtoolerr_t open(const imgtool_module *module, const std::string &filename, int read_or_write, ptr &outimg);
|
||||
static imgtoolerr_t open(const std::string &modulename, const std::string &filename, int read_or_write, ptr &outimg);
|
||||
static imgtoolerr_t create(const imgtool_module *module, const std::string &filename, util::option_resolution *opts, ptr &image);
|
||||
static imgtoolerr_t create(const std::string &modulename, const std::string &filename, util::option_resolution *opts, ptr &image);
|
||||
static imgtoolerr_t create(const imgtool_module *module, const std::string &filename, util::option_resolution *opts);
|
||||
static imgtoolerr_t create(const std::string &modulename, const std::string &filename, util::option_resolution *opts);
|
||||
|
||||
std::string info();
|
||||
imgtoolerr_t get_geometry(uint32_t *tracks, uint32_t *heads, uint32_t *sectors);
|
||||
imgtoolerr_t read_sector(uint32_t track, uint32_t head, uint32_t sector, std::vector<uint8_t> &buffer);
|
||||
imgtoolerr_t write_sector(uint32_t track, uint32_t head, uint32_t sector, const void *buffer, size_t len);
|
||||
imgtoolerr_t get_block_size(uint32_t &length);
|
||||
imgtoolerr_t read_block(uint64_t block, void *buffer);
|
||||
imgtoolerr_t write_block(uint64_t block, const void *buffer);
|
||||
imgtoolerr_t clear_block(uint64_t block, uint8_t data);
|
||||
imgtoolerr_t list_partitions(std::vector<imgtool::partition_info> &partitions);
|
||||
const imgtool_module &module() { return m_module; }
|
||||
void *extra_bytes() { return m_extra_bytes.get(); }
|
||||
|
||||
private:
|
||||
const imgtool_module &m_module;
|
||||
std::unique_ptr<uint8_t[]> m_extra_bytes;
|
||||
|
||||
// because of an idiosyncrasy of how imgtool::image::internal_open() works, we are only "okay to close"
|
||||
// by invoking the module's close function once internal_open() succeeds. the long term solution is
|
||||
// better C++ adoption (e.g. - std::unique_ptr<>, std:move() etc)
|
||||
bool m_okay_to_close;
|
||||
|
||||
static imgtoolerr_t internal_open(const imgtool_module *module, const std::string &filename,
|
||||
int read_or_write, util::option_resolution *createopts, imgtool::image::ptr &outimg);
|
||||
};
|
||||
}
|
||||
|
||||
namespace imgtool
|
||||
{
|
||||
// ----- partition management -----
|
||||
class partition
|
||||
{
|
||||
friend class directory;
|
||||
public:
|
||||
typedef std::unique_ptr<partition> ptr;
|
||||
|
||||
// ctor/dtor
|
||||
partition(imgtool::image &image, const imgtool_class &imgclass, int partition_index, uint64_t base_block, uint64_t block_count);
|
||||
~partition();
|
||||
|
||||
static imgtoolerr_t open(imgtool::image &image, int partition_index, ptr &partition);
|
||||
imgtool::image &image() { return m_image; }
|
||||
|
||||
// ----- partition operations -----
|
||||
imgtoolerr_t get_directory_entry(const char *path, int index, imgtool_dirent &ent);
|
||||
imgtoolerr_t get_file_size(const char *filename, uint64_t &filesize);
|
||||
imgtoolerr_t get_free_space(uint64_t &sz);
|
||||
imgtoolerr_t read_file(const char *filename, const char *fork, imgtool::stream &destf, filter_getinfoproc filter);
|
||||
imgtoolerr_t write_file(const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *resolution, filter_getinfoproc filter);
|
||||
imgtoolerr_t get_file(const char *filename, const char *fork, const char *dest, filter_getinfoproc filter);
|
||||
imgtoolerr_t put_file(const char *newfname, const char *fork, const char *source, util::option_resolution *opts, filter_getinfoproc filter);
|
||||
imgtoolerr_t delete_file(const char *fname);
|
||||
imgtoolerr_t list_file_forks(const char *path, std::vector<imgtool::fork_entry> &forks);
|
||||
imgtoolerr_t create_directory(const char *path);
|
||||
imgtoolerr_t delete_directory(const char *path);
|
||||
imgtoolerr_t list_file_attributes(const char *path, uint32_t *attrs, size_t len);
|
||||
imgtoolerr_t get_file_attributes(const char *path, const uint32_t *attrs, imgtool_attribute *values);
|
||||
imgtoolerr_t put_file_attributes(const char *path, const uint32_t *attrs, const imgtool_attribute *values);
|
||||
imgtoolerr_t get_file_attribute(const char *path, uint32_t attr, imgtool_attribute &value);
|
||||
imgtoolerr_t put_file_attribute(const char *path, uint32_t attr, const imgtool_attribute &value);
|
||||
void get_attribute_name(uint32_t attribute, const imgtool_attribute *attr_value, char *buffer, size_t buffer_len);
|
||||
imgtoolerr_t get_icon_info(const char *path, imgtool_iconinfo *iconinfo);
|
||||
imgtoolerr_t suggest_file_filters(const char *path, imgtool::stream *stream, imgtool_transfer_suggestion *suggestions, size_t suggestions_length);
|
||||
imgtoolerr_t get_block_size(uint32_t &length);
|
||||
imgtoolerr_t read_block(uint64_t block, void *buffer);
|
||||
imgtoolerr_t write_block(uint64_t block, const void *buffer);
|
||||
imgtoolerr_t get_chain(const char *path, imgtool_chainent *chain, size_t chain_size);
|
||||
imgtoolerr_t get_chain_string(const char *path, char *buffer, size_t buffer_len);
|
||||
imgtool_partition_features get_features() const;
|
||||
void * get_info_ptr(uint32_t state);
|
||||
const char * get_info_string(uint32_t state);
|
||||
uint64_t get_info_int(uint32_t state);
|
||||
void * extra_bytes();
|
||||
|
||||
// ----- path management -----
|
||||
const char * get_root_path();
|
||||
const char * path_concatenate(const char *path1, const char *path2);
|
||||
const char * get_base_name(const char *path);
|
||||
|
||||
private:
|
||||
imgtool::image &m_image;
|
||||
//int m_partition_index;
|
||||
uint64_t m_base_block;
|
||||
uint64_t m_block_count;
|
||||
|
||||
imgtool_class m_imgclass;
|
||||
size_t m_directory_extra_bytes;
|
||||
|
||||
char m_path_separator;
|
||||
char m_alternate_path_separator;
|
||||
|
||||
unsigned int m_prefer_ucase : 1;
|
||||
unsigned int m_supports_creation_time : 1;
|
||||
unsigned int m_supports_lastmodified_time : 1;
|
||||
unsigned int m_supports_bootblock : 1; /* this module supports loading/storing the boot block */
|
||||
|
||||
std::function<imgtoolerr_t(imgtool::directory &enumeration, const char *path)> m_begin_enum;
|
||||
std::function<imgtoolerr_t(imgtool::directory &enumeration, imgtool_dirent &ent)> m_next_enum;
|
||||
std::function<void(imgtool::directory &enumeration)> m_close_enum;
|
||||
std::function<imgtoolerr_t(imgtool::partition &partition, uint64_t *size)> m_free_space;
|
||||
std::function<imgtoolerr_t(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)> m_read_file;
|
||||
std::function<imgtoolerr_t(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts)> m_write_file;
|
||||
std::function<imgtoolerr_t(imgtool::partition &partition, const char *filename)> m_delete_file;
|
||||
std::function<imgtoolerr_t(imgtool::partition &partition, const char *path, std::vector<imgtool::fork_entry> &forks)> m_list_forks;
|
||||
std::function<imgtoolerr_t(imgtool::partition &partition, const char *path)> m_create_dir;
|
||||
std::function<imgtoolerr_t(imgtool::partition &partition, const char *path)> m_delete_dir;
|
||||
std::function<imgtoolerr_t(imgtool::partition &partition, const char *path, uint32_t *attrs, size_t len)> m_list_attrs;
|
||||
std::function<imgtoolerr_t(imgtool::partition &partition, const char *path, const uint32_t *attrs, imgtool_attribute *values)> m_get_attrs;
|
||||
std::function<imgtoolerr_t(imgtool::partition &partition, const char *path, const uint32_t *attrs, const imgtool_attribute *values)> m_set_attrs;
|
||||
std::function<imgtoolerr_t(uint32_t attribute, const imgtool_attribute *attr, char *buffer, size_t buffer_len)> m_attr_name;
|
||||
std::function<imgtoolerr_t(imgtool::partition &partition, const char *path, imgtool_iconinfo *iconinfo)> m_get_iconinfo;
|
||||
std::function<imgtoolerr_t(imgtool::partition &partition, const char *path, imgtool_transfer_suggestion *suggestions, size_t suggestions_length)> m_suggest_transfer;
|
||||
std::function<imgtoolerr_t(imgtool::partition &partition, const char *path, imgtool_chainent *chain, size_t chain_size)> m_get_chain;
|
||||
|
||||
const util::option_guide *m_writefile_optguide;
|
||||
std::string m_writefile_optspec;
|
||||
|
||||
std::unique_ptr<uint8_t[]> m_extra_bytes;
|
||||
|
||||
// methods
|
||||
imgtoolerr_t canonicalize_path(uint32_t flags, const char *path, std::string &result);
|
||||
imgtoolerr_t canonicalize_fork(const char **fork);
|
||||
imgtoolerr_t map_block_to_image_block(uint64_t partition_block, uint64_t &image_block) const;
|
||||
};
|
||||
|
||||
// ----- directory management -----
|
||||
class directory
|
||||
{
|
||||
public:
|
||||
typedef std::unique_ptr<directory> ptr;
|
||||
|
||||
// ctor/dtor
|
||||
directory(imgtool::partition &partition);
|
||||
~directory();
|
||||
|
||||
// methods
|
||||
static imgtoolerr_t open(imgtool::partition &partition, const std::string &path, ptr &outenum);
|
||||
imgtoolerr_t get_next(imgtool_dirent &ent);
|
||||
|
||||
// accessors
|
||||
imgtool::partition &partition() { return m_partition; }
|
||||
imgtool::image &image() { return partition().image(); }
|
||||
const imgtool_module &module() { return partition().image().module(); }
|
||||
void *extra_bytes() { assert(m_extra_bytes); return m_extra_bytes.get(); }
|
||||
|
||||
private:
|
||||
imgtool::partition &m_partition;
|
||||
std::unique_ptr<uint8_t[]> m_extra_bytes;
|
||||
bool m_okay_to_close; // similar wart as what is on imgtool::image
|
||||
};
|
||||
};
|
||||
|
||||
/* ----- special ----- */
|
||||
bool imgtool_validitychecks(void);
|
||||
void unknown_partition_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info);
|
||||
|
||||
char *strncpyz(char *dest, const char *source, size_t len);
|
||||
void rtrim(char *buf);
|
||||
std::string extract_padded_filename(const char *source, size_t filename_length, size_t extension_length, char pad = ' ');
|
||||
|
||||
#endif /* IMGTOOL_H */
|
@ -1,314 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/****************************************************************************
|
||||
|
||||
library.cpp
|
||||
|
||||
Code relevant to the Imgtool library; analogous to the MESS/MAME driver
|
||||
list.
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "library.h"
|
||||
|
||||
#include "corestr.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace imgtool {
|
||||
|
||||
datetime::imgtool_clock::duration datetime::s_gmt_offset = datetime::calculate_gmt_offset();
|
||||
|
||||
//-------------------------------------------------
|
||||
// datetime ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
datetime::datetime(datetime_type type, std::chrono::time_point<std::chrono::system_clock> tp)
|
||||
: m_type(type)
|
||||
, m_time_point(imgtool_clock::from_system_clock(tp))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// datetime ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
datetime::datetime(datetime_type type, time_t t)
|
||||
: datetime(type, std::chrono::system_clock::from_time_t(t))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// datetime ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
datetime::datetime(datetime_type type, const util::arbitrary_datetime &dt, bool clamp)
|
||||
: m_type(type)
|
||||
, m_time_point(imgtool_clock::from_arbitrary_datetime(dt, clamp))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// datetime::now
|
||||
//-------------------------------------------------
|
||||
|
||||
datetime datetime::now(datetime_type type)
|
||||
{
|
||||
return imgtool::datetime(
|
||||
type,
|
||||
std::chrono::system_clock::now());
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// datetime::localtime
|
||||
//-------------------------------------------------
|
||||
|
||||
std::tm datetime::localtime() const
|
||||
{
|
||||
imgtool_clock::time_point tp;
|
||||
|
||||
switch (type())
|
||||
{
|
||||
case datetime_type::LOCAL:
|
||||
tp = time_point();
|
||||
break;
|
||||
case datetime_type::GMT:
|
||||
tp = time_point() + s_gmt_offset;
|
||||
break;
|
||||
default:
|
||||
tp = imgtool_clock::time_point();
|
||||
break;
|
||||
}
|
||||
return imgtool_clock::to_tm(tp);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// datetime::gmtime
|
||||
//-------------------------------------------------
|
||||
|
||||
std::tm datetime::gmtime() const
|
||||
{
|
||||
imgtool_clock::time_point tp;
|
||||
|
||||
switch (type())
|
||||
{
|
||||
case datetime_type::GMT:
|
||||
tp = time_point();
|
||||
break;
|
||||
case datetime_type::LOCAL:
|
||||
tp = time_point() - s_gmt_offset;
|
||||
break;
|
||||
default:
|
||||
tp = imgtool_clock::time_point();
|
||||
break;
|
||||
}
|
||||
return imgtool_clock::to_tm(tp);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// datetime::calculate_gmt_offset
|
||||
//-------------------------------------------------
|
||||
|
||||
datetime::imgtool_clock::duration datetime::calculate_gmt_offset()
|
||||
{
|
||||
time_t t = time(nullptr);
|
||||
std::tm utc_tm = *std::gmtime(&t);
|
||||
time_t utc = mktime(&utc_tm);
|
||||
std::tm local_tm = *std::localtime(&t);
|
||||
time_t local = mktime(&local_tm);
|
||||
double d = difftime(local, utc) * imgtool_clock::period::den / imgtool_clock::period::num;
|
||||
return imgtool_clock::duration((std::int64_t) d);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// datetime::to_time_t
|
||||
//-------------------------------------------------
|
||||
|
||||
time_t datetime::to_time_t() const
|
||||
{
|
||||
auto system_clock_tp = imgtool_clock::to_system_clock(time_point());
|
||||
return std::chrono::system_clock::to_time_t(system_clock_tp);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
library::library()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// dtor
|
||||
//-------------------------------------------------
|
||||
|
||||
library::~library()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// add_class
|
||||
//-------------------------------------------------
|
||||
|
||||
void library::add_class(const imgtool_class *imgclass)
|
||||
{
|
||||
char const *temp;
|
||||
|
||||
// allocate the module and place it in the chain
|
||||
m_modules.emplace_back(std::make_unique<imgtool_module>());
|
||||
imgtool_module *module = m_modules.back().get();
|
||||
|
||||
module->imgclass = *imgclass;
|
||||
module->name = imgtool_get_info_string(imgclass, IMGTOOLINFO_STR_NAME);
|
||||
module->description = imgtool_get_info_string(imgclass, IMGTOOLINFO_STR_DESCRIPTION);
|
||||
module->extensions = imgtool_get_info_string(imgclass, IMGTOOLINFO_STR_FILE_EXTENSIONS);
|
||||
temp = imgtool_get_info_string(imgclass, IMGTOOLINFO_STR_EOLN);
|
||||
module->eoln = (temp != nullptr) ? temp : "";
|
||||
module->initial_path_separator = imgtool_get_info_int(imgclass, IMGTOOLINFO_INT_INITIAL_PATH_SEPARATOR) ? true : false;
|
||||
module->open_is_strict = imgtool_get_info_int(imgclass, IMGTOOLINFO_INT_OPEN_IS_STRICT) ? true : false;
|
||||
module->tracks_are_called_cylinders = imgtool_get_info_int(imgclass, IMGTOOLINFO_INT_TRACKS_ARE_CALLED_CYLINDERS) ? true : false;
|
||||
module->writing_untested = imgtool_get_info_int(imgclass, IMGTOOLINFO_INT_WRITING_UNTESTED) ? true : false;
|
||||
module->creation_untested = imgtool_get_info_int(imgclass, IMGTOOLINFO_INT_CREATION_UNTESTED) ? true : false;
|
||||
module->open = (imgtoolerr_t (*)(imgtool::image &, std::unique_ptr<imgtool::stream> &&)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_OPEN);
|
||||
module->create = (imgtoolerr_t (*)(imgtool::image &, std::unique_ptr<imgtool::stream> &&, util::option_resolution *)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_CREATE);
|
||||
module->close = (void (*)(imgtool::image &)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_CLOSE);
|
||||
module->info = (void (*)(imgtool::image &, std::ostream &)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_INFO);
|
||||
module->read_sector = (imgtoolerr_t (*)(imgtool::image &, uint32_t, uint32_t, uint32_t, std::vector<uint8_t> &)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_READ_SECTOR);
|
||||
module->write_sector = (imgtoolerr_t (*)(imgtool::image &, uint32_t, uint32_t, uint32_t, const void *, size_t)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_WRITE_SECTOR);
|
||||
module->get_geometry = (imgtoolerr_t (*)(imgtool::image &, uint32_t *, uint32_t *, uint32_t *))imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_GET_GEOMETRY);
|
||||
module->read_block = (imgtoolerr_t (*)(imgtool::image &, void *, uint64_t)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_READ_BLOCK);
|
||||
module->write_block = (imgtoolerr_t (*)(imgtool::image &, const void *, uint64_t)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_WRITE_BLOCK);
|
||||
module->list_partitions = (imgtoolerr_t (*)(imgtool::image &, std::vector<imgtool::partition_info> &)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_LIST_PARTITIONS);
|
||||
module->block_size = imgtool_get_info_int(imgclass, IMGTOOLINFO_INT_BLOCK_SIZE);
|
||||
module->createimage_optguide = (const util::option_guide *) imgtool_get_info_ptr(imgclass, IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE);
|
||||
temp = (char const *)imgtool_get_info_ptr(imgclass, IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC);
|
||||
module->createimage_optspec = (temp != nullptr) ? temp : "";
|
||||
module->image_extra_bytes += imgtool_get_info_int(imgclass, IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// add
|
||||
//-------------------------------------------------
|
||||
|
||||
void library::add(imgtool_get_info get_info)
|
||||
{
|
||||
int (*make_class)(int index, imgtool_class *imgclass);
|
||||
imgtool_class imgclass;
|
||||
int i, result;
|
||||
|
||||
// try this class
|
||||
memset(&imgclass, 0, sizeof(imgclass));
|
||||
imgclass.get_info = get_info;
|
||||
|
||||
// do we have derived getinfo functions?
|
||||
make_class = (int (*)(int index, imgtool_class *imgclass))
|
||||
imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_MAKE_CLASS);
|
||||
|
||||
if (make_class)
|
||||
{
|
||||
i = 0;
|
||||
do
|
||||
{
|
||||
// clear out the class
|
||||
memset(&imgclass, 0, sizeof(imgclass));
|
||||
imgclass.get_info = get_info;
|
||||
|
||||
// make the class
|
||||
result = make_class(i++, &imgclass);
|
||||
if (result)
|
||||
add_class(&imgclass);
|
||||
}
|
||||
while(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
add_class(&imgclass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// unlink
|
||||
//-------------------------------------------------
|
||||
|
||||
void library::unlink(const std::string &module_name)
|
||||
{
|
||||
const modulelist::iterator iter = find(module_name);
|
||||
if (iter != m_modules.end())
|
||||
m_modules.erase(iter);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// module_compare
|
||||
//-------------------------------------------------
|
||||
|
||||
int library::module_compare(const imgtool_module *m1, const imgtool_module *m2, sort_type sort)
|
||||
{
|
||||
int rc = 0;
|
||||
switch(sort)
|
||||
{
|
||||
case sort_type::NAME:
|
||||
rc = strcmp(m1->name.c_str(), m2->name.c_str());
|
||||
break;
|
||||
case sort_type::DESCRIPTION:
|
||||
rc = core_stricmp(m1->description.c_str(), m2->description.c_str());
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// sort
|
||||
//-------------------------------------------------
|
||||
|
||||
void library::sort(sort_type sort)
|
||||
{
|
||||
auto compare = [this, sort](const std::unique_ptr<imgtool_module> &a, const std::unique_ptr<imgtool_module> &b)
|
||||
{
|
||||
return module_compare(a.get(), b.get(), sort) < 0;
|
||||
};
|
||||
m_modules.sort(compare);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// find
|
||||
//-------------------------------------------------
|
||||
|
||||
library::modulelist::iterator library::find(const std::string &module_name)
|
||||
{
|
||||
return std::find_if(
|
||||
m_modules.begin(),
|
||||
m_modules.end(),
|
||||
[module_name](std::unique_ptr<imgtool_module> &module) { return !module_name.compare(module->name); });
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// findmodule
|
||||
//-------------------------------------------------
|
||||
|
||||
const imgtool_module *library::findmodule(const std::string &module_name)
|
||||
{
|
||||
modulelist::iterator iter = find(module_name);
|
||||
return iter != m_modules.end()
|
||||
? iter->get()
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
|
||||
} // namespace imgtool
|
@ -1,551 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/****************************************************************************
|
||||
|
||||
library.h
|
||||
|
||||
Code relevant to the Imgtool library; analogous to the MAME driver list.
|
||||
|
||||
Unlike MAME which has a static driver lists, Imgtool has a concept of a
|
||||
library and this library is built at startup time.
|
||||
dynamic for which modules are added to. This makes "dynamic" modules
|
||||
much easier
|
||||
|
||||
****************************************************************************/
|
||||
#ifndef MAME_TOOLS_IMGTOOL_LIBRARY_H
|
||||
#define MAME_TOOLS_IMGTOOL_LIBRARY_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imgterrs.h"
|
||||
|
||||
#include "timeconv.h"
|
||||
#include "utilfwd.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <iosfwd>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace imgtool
|
||||
{
|
||||
class image;
|
||||
class partition;
|
||||
class directory;
|
||||
class charconverter;
|
||||
class stream;
|
||||
};
|
||||
|
||||
enum imgtool_suggestion_viability_t
|
||||
{
|
||||
SUGGESTION_END,
|
||||
SUGGESTION_POSSIBLE,
|
||||
SUGGESTION_RECOMMENDED
|
||||
};
|
||||
|
||||
union filterinfo
|
||||
{
|
||||
int64_t i; /* generic integers */
|
||||
void * p; /* generic pointers */
|
||||
void * f; /* generic function pointers */
|
||||
const char *s; /* generic strings */
|
||||
|
||||
imgtoolerr_t (*read_file)(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf);
|
||||
imgtoolerr_t (*write_file)(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts);
|
||||
imgtoolerr_t (*check_stream)(imgtool::stream &stream, imgtool_suggestion_viability_t *viability);
|
||||
};
|
||||
|
||||
typedef void (*filter_getinfoproc)(uint32_t state, union filterinfo *info);
|
||||
|
||||
namespace imgtool
|
||||
{
|
||||
class datetime
|
||||
{
|
||||
public:
|
||||
typedef util::arbitrary_clock<std::int64_t, 1600, 1, 1, 0, 0, 0, std::ratio<1, 10000000> > imgtool_clock;
|
||||
|
||||
enum datetime_type
|
||||
{
|
||||
NONE,
|
||||
LOCAL,
|
||||
GMT
|
||||
};
|
||||
|
||||
datetime()
|
||||
: m_type(datetime_type::NONE)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template<typename Rep, int Y, int M, int D, int H, int N, int S, typename Ratio>
|
||||
datetime(datetime_type type, std::chrono::time_point<util::arbitrary_clock<Rep, Y, M, D, H, N, S, Ratio> > tp)
|
||||
: m_type(type)
|
||||
, m_time_point(imgtool_clock::from_arbitrary_time_point(tp))
|
||||
{
|
||||
}
|
||||
|
||||
datetime(datetime_type type, std::chrono::time_point<std::chrono::system_clock> tp);
|
||||
datetime(datetime_type type, time_t t);
|
||||
datetime(datetime_type type, const util::arbitrary_datetime &dt, bool clamp = true);
|
||||
datetime(const datetime &that) = default;
|
||||
datetime(datetime &&that) = default;
|
||||
|
||||
// accessors
|
||||
datetime_type type() const { return m_type; }
|
||||
bool empty() const { return type() == datetime_type::NONE; }
|
||||
std::chrono::time_point<imgtool_clock> time_point() const { return m_time_point; }
|
||||
|
||||
// operators
|
||||
datetime &operator =(const datetime &that)
|
||||
{
|
||||
m_type = that.m_type;
|
||||
m_time_point = that.m_time_point;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// returns the current time
|
||||
static datetime now(datetime_type type);
|
||||
|
||||
// returns time structures
|
||||
std::tm localtime() const;
|
||||
std::tm gmtime() const;
|
||||
time_t to_time_t() const;
|
||||
|
||||
private:
|
||||
static imgtool_clock::duration s_gmt_offset;
|
||||
datetime_type m_type;
|
||||
std::chrono::time_point<imgtool_clock> m_time_point;
|
||||
|
||||
static imgtool_clock::duration calculate_gmt_offset();
|
||||
};
|
||||
};
|
||||
|
||||
struct imgtool_dirent
|
||||
{
|
||||
char filename[1024];
|
||||
char attr[64];
|
||||
uint64_t filesize;
|
||||
|
||||
imgtool::datetime creation_time;
|
||||
imgtool::datetime lastmodified_time;
|
||||
imgtool::datetime lastaccess_time;
|
||||
|
||||
char softlink[1024];
|
||||
char comment[256];
|
||||
|
||||
/* flags */
|
||||
unsigned int eof : 1;
|
||||
unsigned int corrupt : 1;
|
||||
unsigned int directory : 1;
|
||||
unsigned int hardlink : 1;
|
||||
};
|
||||
|
||||
struct imgtool_chainent
|
||||
{
|
||||
uint8_t level;
|
||||
uint64_t block;
|
||||
};
|
||||
|
||||
namespace imgtool
|
||||
{
|
||||
class fork_entry
|
||||
{
|
||||
public:
|
||||
enum class type_t
|
||||
{
|
||||
DATA,
|
||||
RESOURCE,
|
||||
ALT
|
||||
};
|
||||
|
||||
fork_entry(uint64_t size, type_t type = type_t::DATA)
|
||||
: m_size(size)
|
||||
, m_type(type)
|
||||
, m_name(default_name(type))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
fork_entry(uint64_t size, std::string &&name)
|
||||
: m_size(size)
|
||||
, m_type(fork_entry::type_t::ALT)
|
||||
, m_name(std::move(name))
|
||||
{
|
||||
}
|
||||
|
||||
fork_entry(const fork_entry &that) = default;
|
||||
fork_entry(fork_entry &&that) = default;
|
||||
|
||||
uint64_t size() const { return m_size; }
|
||||
type_t type() const { return m_type; }
|
||||
const std::string &name() const { return m_name; }
|
||||
|
||||
private:
|
||||
static std::string default_name(type_t type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case type_t::DATA:
|
||||
return std::string("");
|
||||
case type_t::RESOURCE:
|
||||
return std::string("RESOURCE_FORK");
|
||||
default:
|
||||
throw false;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t m_size;
|
||||
type_t m_type;
|
||||
std::string m_name;
|
||||
};
|
||||
}
|
||||
|
||||
struct imgtool_transfer_suggestion
|
||||
{
|
||||
imgtool_suggestion_viability_t viability;
|
||||
filter_getinfoproc filter;
|
||||
const char *fork;
|
||||
const char *description;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
||||
IMGTOOLATTR_INT_FIRST = 0x00000,
|
||||
IMGTOOLATTR_INT_MAC_TYPE,
|
||||
IMGTOOLATTR_INT_MAC_CREATOR,
|
||||
IMGTOOLATTR_INT_MAC_FINDERFLAGS,
|
||||
IMGTOOLATTR_INT_MAC_COORDX,
|
||||
IMGTOOLATTR_INT_MAC_COORDY,
|
||||
IMGTOOLATTR_INT_MAC_FINDERFOLDER,
|
||||
IMGTOOLATTR_INT_MAC_ICONID,
|
||||
IMGTOOLATTR_INT_MAC_SCRIPTCODE,
|
||||
IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS,
|
||||
IMGTOOLATTR_INT_MAC_COMMENTID,
|
||||
IMGTOOLATTR_INT_MAC_PUTAWAYDIRECTORY,
|
||||
|
||||
/* --- the following bits of info are returned as pointers to data or functions --- */
|
||||
IMGTOOLATTR_PTR_FIRST = 0x10000,
|
||||
|
||||
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
||||
IMGTOOLATTR_STR_FIRST = 0x20000,
|
||||
|
||||
/* --- the following bits of info are returned as time_t values --- */
|
||||
IMGTOOLATTR_TIME_FIRST = 0x30000,
|
||||
IMGTOOLATTR_TIME_CREATED,
|
||||
IMGTOOLATTR_TIME_LASTMODIFIED
|
||||
};
|
||||
|
||||
union imgtool_attribute
|
||||
{
|
||||
int64_t i;
|
||||
time_t t;
|
||||
};
|
||||
|
||||
struct imgtool_iconinfo
|
||||
{
|
||||
unsigned icon16x16_specified : 1;
|
||||
unsigned icon32x32_specified : 1;
|
||||
uint32_t icon16x16[16][16];
|
||||
uint32_t icon32x32[32][32];
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
||||
IMGTOOLINFO_INT_FIRST = 0x00000,
|
||||
IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES,
|
||||
IMGTOOLINFO_INT_PARTITION_EXTRA_BYTES,
|
||||
IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES,
|
||||
IMGTOOLINFO_INT_PATH_SEPARATOR,
|
||||
IMGTOOLINFO_INT_ALTERNATE_PATH_SEPARATOR,
|
||||
IMGTOOLINFO_INT_PREFER_UCASE,
|
||||
IMGTOOLINFO_INT_INITIAL_PATH_SEPARATOR,
|
||||
IMGTOOLINFO_INT_OPEN_IS_STRICT,
|
||||
IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME,
|
||||
IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME,
|
||||
IMGTOOLINFO_INT_TRACKS_ARE_CALLED_CYLINDERS,
|
||||
IMGTOOLINFO_INT_WRITING_UNTESTED,
|
||||
IMGTOOLINFO_INT_CREATION_UNTESTED,
|
||||
IMGTOOLINFO_INT_SUPPORTS_BOOTBLOCK,
|
||||
IMGTOOLINFO_INT_BLOCK_SIZE,
|
||||
|
||||
IMGTOOLINFO_INT_CLASS_SPECIFIC = 0x08000,
|
||||
|
||||
/* --- the following bits of info are returned as pointers to data or functions --- */
|
||||
IMGTOOLINFO_PTR_FIRST = 0x10000,
|
||||
|
||||
IMGTOOLINFO_PTR_OPEN,
|
||||
IMGTOOLINFO_PTR_CREATE,
|
||||
IMGTOOLINFO_PTR_CLOSE,
|
||||
IMGTOOLINFO_PTR_OPEN_PARTITION,
|
||||
IMGTOOLINFO_PTR_CREATE_PARTITION,
|
||||
IMGTOOLINFO_PTR_INFO,
|
||||
IMGTOOLINFO_PTR_BEGIN_ENUM,
|
||||
IMGTOOLINFO_PTR_NEXT_ENUM,
|
||||
IMGTOOLINFO_PTR_CLOSE_ENUM,
|
||||
IMGTOOLINFO_PTR_FREE_SPACE,
|
||||
IMGTOOLINFO_PTR_READ_FILE,
|
||||
IMGTOOLINFO_PTR_WRITE_FILE,
|
||||
IMGTOOLINFO_PTR_DELETE_FILE,
|
||||
IMGTOOLINFO_PTR_LIST_FORKS,
|
||||
IMGTOOLINFO_PTR_CREATE_DIR,
|
||||
IMGTOOLINFO_PTR_DELETE_DIR,
|
||||
IMGTOOLINFO_PTR_LIST_ATTRS,
|
||||
IMGTOOLINFO_PTR_GET_ATTRS,
|
||||
IMGTOOLINFO_PTR_SET_ATTRS,
|
||||
IMGTOOLINFO_PTR_ATTR_NAME,
|
||||
IMGTOOLINFO_PTR_GET_ICON_INFO,
|
||||
IMGTOOLINFO_PTR_SUGGEST_TRANSFER,
|
||||
IMGTOOLINFO_PTR_GET_CHAIN,
|
||||
IMGTOOLINFO_PTR_GET_GEOMETRY,
|
||||
IMGTOOLINFO_PTR_READ_SECTOR,
|
||||
IMGTOOLINFO_PTR_WRITE_SECTOR,
|
||||
IMGTOOLINFO_PTR_READ_BLOCK,
|
||||
IMGTOOLINFO_PTR_WRITE_BLOCK,
|
||||
IMGTOOLINFO_PTR_APPROVE_FILENAME_CHAR,
|
||||
IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE,
|
||||
IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE,
|
||||
IMGTOOLINFO_PTR_MAKE_CLASS,
|
||||
IMGTOOLINFO_PTR_LIST_PARTITIONS,
|
||||
IMGTOOLINFO_PTR_CHARCONVERTER,
|
||||
|
||||
IMGTOOLINFO_PTR_CLASS_SPECIFIC = 0x18000,
|
||||
|
||||
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
||||
IMGTOOLINFO_STR_FIRST = 0x20000,
|
||||
|
||||
IMGTOOLINFO_STR_NAME,
|
||||
IMGTOOLINFO_STR_DESCRIPTION,
|
||||
IMGTOOLINFO_STR_FILE,
|
||||
IMGTOOLINFO_STR_FILE_EXTENSIONS,
|
||||
IMGTOOLINFO_STR_EOLN,
|
||||
IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC,
|
||||
IMGTOOLINFO_STR_WRITEFILE_OPTSPEC,
|
||||
|
||||
IMGTOOLINFO_STR_CLASS_SPECIFIC = 0x28000
|
||||
};
|
||||
|
||||
|
||||
|
||||
union imgtoolinfo;
|
||||
|
||||
struct imgtool_class;
|
||||
typedef void (*imgtool_get_info)(const imgtool_class *, uint32_t, union imgtoolinfo *);
|
||||
|
||||
struct imgtool_class
|
||||
{
|
||||
imgtool_get_info get_info;
|
||||
imgtool_get_info derived_get_info;
|
||||
void *derived_param;
|
||||
};
|
||||
|
||||
|
||||
|
||||
namespace imgtool
|
||||
{
|
||||
class partition_info
|
||||
{
|
||||
public:
|
||||
partition_info(imgtool_get_info get_info, uint64_t base_block, uint64_t block_count)
|
||||
: m_base_block(base_block)
|
||||
, m_block_count(block_count)
|
||||
{
|
||||
memset(&m_imgclass, 0, sizeof(m_imgclass));
|
||||
m_imgclass.get_info = get_info;
|
||||
}
|
||||
|
||||
partition_info(imgtool_class imgclass, uint64_t base_block, uint64_t block_count)
|
||||
: m_imgclass(imgclass)
|
||||
, m_base_block(base_block)
|
||||
, m_block_count(block_count)
|
||||
{
|
||||
}
|
||||
|
||||
const imgtool_class &imgclass() const { return m_imgclass; }
|
||||
uint64_t base_block() const { return m_base_block; }
|
||||
uint64_t block_count() const { return m_block_count; }
|
||||
|
||||
private:
|
||||
imgtool_class m_imgclass;
|
||||
uint64_t m_base_block;
|
||||
uint64_t m_block_count;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
union imgtoolinfo
|
||||
{
|
||||
int64_t i; /* generic integers */
|
||||
void * p; /* generic pointers */
|
||||
void * f; /* generic function pointers */
|
||||
char * s; /* generic strings */
|
||||
|
||||
imgtoolerr_t (*open) (imgtool::image &image, std::unique_ptr<imgtool::stream> &&stream);
|
||||
void (*close) (imgtool::image &image);
|
||||
imgtoolerr_t (*create) (imgtool::image &image, std::unique_ptr<imgtool::stream> &&stream, util::option_resolution *opts);
|
||||
imgtoolerr_t (*create_partition) (imgtool::image &image, uint64_t first_block, uint64_t block_count);
|
||||
void (*info) (imgtool::image &image, std::ostream &stream);
|
||||
imgtoolerr_t (*begin_enum) (imgtool::directory &enumeration, const char *path);
|
||||
imgtoolerr_t (*next_enum) (imgtool::directory &enumeration, imgtool_dirent &ent);
|
||||
void (*close_enum) (imgtool::directory &enumeration);
|
||||
imgtoolerr_t (*open_partition) (imgtool::partition &partition, uint64_t first_block, uint64_t block_count);
|
||||
imgtoolerr_t (*free_space) (imgtool::partition &partition, uint64_t *size);
|
||||
imgtoolerr_t (*read_file) (imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf);
|
||||
imgtoolerr_t (*write_file) (imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts);
|
||||
imgtoolerr_t (*delete_file) (imgtool::partition &partition, const char *filename);
|
||||
imgtoolerr_t (*list_forks) (imgtool::partition &partition, const char *path, std::vector<imgtool::fork_entry> &forks);
|
||||
imgtoolerr_t (*create_dir) (imgtool::partition &partition, const char *path);
|
||||
imgtoolerr_t (*delete_dir) (imgtool::partition &partition, const char *path);
|
||||
imgtoolerr_t (*list_attrs) (imgtool::partition &partition, const char *path, uint32_t *attrs, size_t len);
|
||||
imgtoolerr_t (*get_attrs) (imgtool::partition &partition, const char *path, const uint32_t *attrs, imgtool_attribute *values);
|
||||
imgtoolerr_t (*set_attrs) (imgtool::partition &partition, const char *path, const uint32_t *attrs, const imgtool_attribute *values);
|
||||
imgtoolerr_t (*attr_name) (uint32_t attribute, const imgtool_attribute *attr, char *buffer, size_t buffer_len);
|
||||
imgtoolerr_t (*get_iconinfo) (imgtool::partition &partition, const char *path, imgtool_iconinfo *iconinfo);
|
||||
imgtoolerr_t (*suggest_transfer) (imgtool::partition &partition, const char *path, imgtool_transfer_suggestion *suggestions, size_t suggestions_length);
|
||||
imgtoolerr_t (*get_chain) (imgtool::partition &partition, const char *path, imgtool_chainent *chain, size_t chain_size);
|
||||
imgtoolerr_t (*get_geometry) (imgtool::image &image, uint32_t *tracks, uint32_t *heads, uint32_t *sectors);
|
||||
imgtoolerr_t (*read_sector) (imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, std::vector<uint8_t> &buffer);
|
||||
imgtoolerr_t (*write_sector) (imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, const void *buffer, size_t len, int ddam);
|
||||
imgtoolerr_t (*read_block) (imgtool::image &image, void *buffer, uint64_t block);
|
||||
imgtoolerr_t (*write_block) (imgtool::image &image, const void *buffer, uint64_t block);
|
||||
imgtoolerr_t (*list_partitions) (imgtool::image &image, std::vector<imgtool::partition_info> &partitions);
|
||||
int (*approve_filename_char)(char32_t ch);
|
||||
int (*make_class)(int index, imgtool_class *imgclass);
|
||||
|
||||
const util::option_guide *createimage_optguide;
|
||||
const util::option_guide *writefile_optguide;
|
||||
const imgtool::charconverter *charconverter;
|
||||
};
|
||||
|
||||
|
||||
|
||||
static inline int64_t imgtool_get_info_int(const imgtool_class *imgclass, uint32_t state)
|
||||
{
|
||||
union imgtoolinfo info;
|
||||
info.i = 0;
|
||||
imgclass->get_info(imgclass, state, &info);
|
||||
return info.i;
|
||||
}
|
||||
|
||||
static inline void *imgtool_get_info_ptr(const imgtool_class *imgclass, uint32_t state)
|
||||
{
|
||||
union imgtoolinfo info;
|
||||
info.p = nullptr;
|
||||
imgclass->get_info(imgclass, state, &info);
|
||||
return info.p;
|
||||
}
|
||||
|
||||
static inline void *imgtool_get_info_fct(const imgtool_class *imgclass, uint32_t state)
|
||||
{
|
||||
union imgtoolinfo info;
|
||||
info.f = nullptr;
|
||||
imgclass->get_info(imgclass, state, &info);
|
||||
return info.f;
|
||||
}
|
||||
|
||||
static inline char *imgtool_get_info_string(const imgtool_class *imgclass, uint32_t state)
|
||||
{
|
||||
union imgtoolinfo info;
|
||||
info.s = nullptr;
|
||||
imgclass->get_info(imgclass, state, &info);
|
||||
return info.s;
|
||||
}
|
||||
|
||||
/* circular string buffer */
|
||||
char *imgtool_temp_str(void);
|
||||
|
||||
|
||||
|
||||
struct imgtool_module
|
||||
{
|
||||
imgtool_class imgclass = { 0 };
|
||||
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string extensions;
|
||||
std::string eoln;
|
||||
|
||||
size_t image_extra_bytes = 0;
|
||||
|
||||
/* flags */
|
||||
bool initial_path_separator = false;
|
||||
bool open_is_strict = false;
|
||||
bool tracks_are_called_cylinders = false; /* used for hard drivers */
|
||||
bool writing_untested = false; /* used when we support writing, but not in main build */
|
||||
bool creation_untested = false; /* used when we support creation, but not in main build */
|
||||
|
||||
imgtoolerr_t (*open) (imgtool::image &image, std::unique_ptr<imgtool::stream> &&stream) = nullptr;
|
||||
void (*close) (imgtool::image &image) = nullptr;
|
||||
void (*info) (imgtool::image &image, std::ostream &stream) = nullptr;
|
||||
imgtoolerr_t (*create) (imgtool::image &image, std::unique_ptr<imgtool::stream> &&stream, util::option_resolution *opts) = nullptr;
|
||||
imgtoolerr_t (*get_geometry) (imgtool::image &image, uint32_t *track, uint32_t *heads, uint32_t *sectors) = nullptr;
|
||||
imgtoolerr_t (*read_sector) (imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, std::vector<uint8_t> &buffer) = nullptr;
|
||||
imgtoolerr_t (*write_sector) (imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, const void *buffer, size_t len) = nullptr;
|
||||
imgtoolerr_t (*read_block) (imgtool::image &image, void *buffer, uint64_t block) = nullptr;
|
||||
imgtoolerr_t (*write_block) (imgtool::image &image, const void *buffer, uint64_t block) = nullptr;
|
||||
imgtoolerr_t (*list_partitions)(imgtool::image &image, std::vector<imgtool::partition_info> &partitions) = nullptr;
|
||||
|
||||
uint32_t block_size = 0;
|
||||
|
||||
const util::option_guide *createimage_optguide = nullptr;
|
||||
std::string createimage_optspec;
|
||||
|
||||
const void *extra = nullptr;
|
||||
};
|
||||
|
||||
namespace imgtool {
|
||||
|
||||
//**************************************************************************
|
||||
// TYPE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
// imgtool "library" - equivalent to the MAME driver list
|
||||
class library
|
||||
{
|
||||
public:
|
||||
typedef std::list<std::unique_ptr<imgtool_module> > modulelist;
|
||||
|
||||
enum class sort_type
|
||||
{
|
||||
NAME,
|
||||
DESCRIPTION
|
||||
};
|
||||
|
||||
library();
|
||||
~library();
|
||||
|
||||
// adds a module to an imgtool library
|
||||
void add(imgtool_get_info get_info);
|
||||
|
||||
// seeks out and removes a module from an imgtool library
|
||||
void unlink(const std::string &module_name);
|
||||
|
||||
// sorts an imgtool library
|
||||
void sort(sort_type sort);
|
||||
|
||||
// finds a module
|
||||
const imgtool_module *findmodule(const std::string &module_name);
|
||||
|
||||
// module iteration
|
||||
const modulelist &modules() { return m_modules; }
|
||||
|
||||
private:
|
||||
modulelist m_modules;
|
||||
|
||||
// internal lookup and iteration
|
||||
modulelist::iterator find(const std::string &module_name);
|
||||
|
||||
// helpers
|
||||
void add_class(const imgtool_class *imgclass);
|
||||
int module_compare(const imgtool_module *m1, const imgtool_module *m2, sort_type sort);
|
||||
};
|
||||
|
||||
} // namespace imgtool
|
||||
|
||||
#endif // MAME_TOOLS_IMGTOOL_LIBRARY_H
|
@ -1,966 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/***************************************************************************
|
||||
|
||||
main.cpp
|
||||
|
||||
Imgtool command line front end
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "imgtool.h"
|
||||
#include "filter.h"
|
||||
#include "main.h"
|
||||
#include "modules.h"
|
||||
|
||||
#include "corefile.h"
|
||||
#include "corestr.h"
|
||||
#include "opresolv.h"
|
||||
#include "strformat.h"
|
||||
#include "unicode.h"
|
||||
|
||||
#include "osdcore.h" // osd_get_command_line
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
static void writeusage(std::wostream &output, bool write_word_usage, const struct command *c, char *argv[])
|
||||
{
|
||||
std::string cmdname(core_filename_extract_base(argv[0]));
|
||||
|
||||
util::stream_format(output,
|
||||
L"%s %s %s %s\n",
|
||||
(write_word_usage ? L"Usage:" : L" "),
|
||||
wstring_from_utf8(cmdname),
|
||||
wstring_from_utf8(c->name),
|
||||
c->usage ? wstring_from_utf8(c->usage) : std::wstring());
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
static int parse_options(int argc, char *argv[], int minunnamed, int maxunnamed,
|
||||
util::option_resolution *resolution, filter_getinfoproc *filter, const char **fork)
|
||||
{
|
||||
int i;
|
||||
int lastunnamed = 0;
|
||||
char *s;
|
||||
char *name = nullptr;
|
||||
char *value = nullptr;
|
||||
static char buf[256];
|
||||
|
||||
if (filter)
|
||||
*filter = nullptr;
|
||||
if (fork)
|
||||
*fork = nullptr;
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
{
|
||||
/* Named or unamed arg */
|
||||
if ((argv[i][0] != '-') || (argv[i][1] != '-'))
|
||||
{
|
||||
/* Unnamed */
|
||||
if (i >= maxunnamed)
|
||||
goto error; /* Too many unnamed */
|
||||
lastunnamed = i + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Named */
|
||||
name = argv[i] + 2;
|
||||
s = strchr(name, '=');
|
||||
if (!s)
|
||||
goto error;
|
||||
*s = 0;
|
||||
value = s + 1;
|
||||
|
||||
if (!strcmp(name, "filter"))
|
||||
{
|
||||
/* filter option */
|
||||
if (!filter)
|
||||
goto error; /* this command doesn't use filters */
|
||||
if (*filter)
|
||||
goto optionalreadyspecified;
|
||||
*filter = filter_lookup(value);
|
||||
if (!(*filter))
|
||||
goto filternotfound;
|
||||
|
||||
}
|
||||
else if (!strcmp(name, "fork"))
|
||||
{
|
||||
/* fork option */
|
||||
if (!fork)
|
||||
goto error; /* this command doesn't use filters */
|
||||
if (*fork)
|
||||
goto optionalreadyspecified;
|
||||
|
||||
snprintf(buf, std::size(buf), "%s", value);
|
||||
*fork = buf;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Other named option */
|
||||
if (i < minunnamed)
|
||||
goto error; /* Too few unnamed */
|
||||
|
||||
util::option_resolution::entry *entry = resolution->find(name);
|
||||
if (entry->option_type() == util::option_guide::entry::option_type::ENUM_BEGIN)
|
||||
{
|
||||
const util::option_guide::entry *enum_value;
|
||||
for (enum_value = entry->enum_value_begin(); enum_value != entry->enum_value_end(); enum_value++)
|
||||
{
|
||||
if (!strcmp (enum_value->identifier(), value))
|
||||
{
|
||||
entry->set_value(enum_value->parameter());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (enum_value == entry->enum_value_end())
|
||||
goto error;
|
||||
}
|
||||
else
|
||||
entry->set_value(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return lastunnamed;
|
||||
|
||||
filternotfound:
|
||||
util::stream_format(std::wcerr, L"%s: Unknown filter type\n", wstring_from_utf8(value));
|
||||
return -1;
|
||||
|
||||
optionalreadyspecified:
|
||||
util::stream_format(std::wcerr, L"Cannot specify multiple %ss\n", wstring_from_utf8(name));
|
||||
return -1;
|
||||
|
||||
error:
|
||||
util::stream_format(std::wcerr, L"%s: Unrecognized option\n", wstring_from_utf8(argv[i]));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void reporterror(imgtoolerr_t err, const struct command *c, const char *format, const char *imagename,
|
||||
const char *filename, const char *newname, util::option_resolution *opts)
|
||||
{
|
||||
const char *src = "imgtool";
|
||||
const char *err_name;
|
||||
|
||||
err_name = imgtool_error(err);
|
||||
|
||||
switch(ERRORSOURCE(err)) {
|
||||
case IMGTOOLERR_SRC_MODULE:
|
||||
src = format;
|
||||
break;
|
||||
case IMGTOOLERR_SRC_FUNCTIONALITY:
|
||||
src = c->name;
|
||||
break;
|
||||
case IMGTOOLERR_SRC_IMAGEFILE:
|
||||
src = imagename;
|
||||
break;
|
||||
case IMGTOOLERR_SRC_FILEONIMAGE:
|
||||
src = filename;
|
||||
break;
|
||||
case IMGTOOLERR_SRC_NATIVEFILE:
|
||||
src = newname ? newname : filename;
|
||||
break;
|
||||
}
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
if (!src)
|
||||
src = c->name;
|
||||
util::stream_format(std::wcerr, L"%s: %s\n", wstring_from_utf8(src), wstring_from_utf8(err_name));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char *interpret_filename(const char *filename)
|
||||
{
|
||||
if (!strcmp(filename, "??BOOT??")
|
||||
|| !strcmp(filename, "\'??BOOT??\'")
|
||||
|| !strcmp(filename, "\"??BOOT??\""))
|
||||
filename = FILENAME_BOOTBLOCK;
|
||||
return filename;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
static int cmd_dir(const struct command *c, int argc, char *argv[])
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
int total_count, total_size, freespace_err;
|
||||
uint64_t freespace;
|
||||
imgtool::image::ptr image;
|
||||
imgtool::partition::ptr partition;
|
||||
imgtool::directory::ptr imgenum;
|
||||
imgtool_dirent ent;
|
||||
char last_modified[19];
|
||||
std::string path;
|
||||
int partition_index = 0;
|
||||
std::string info;
|
||||
|
||||
// build the separator
|
||||
const int columnwidth_filename = 30;
|
||||
const int columnwidth_filesize = 8;
|
||||
const int columnwidth_attributes = 15;
|
||||
const int columnwidth_lastmodified = 18;
|
||||
std::string separator = std::string(columnwidth_filename, '-') + " "
|
||||
+ std::string(columnwidth_filesize, '-') + " "
|
||||
+ std::string(columnwidth_attributes, '-') + " "
|
||||
+ std::string(columnwidth_lastmodified, '-');
|
||||
|
||||
// attempt to open image
|
||||
err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_READ, image);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
/* attempt to open partition */
|
||||
err = imgtool::partition::open(*image, partition_index, partition);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
path = argc > 2 ? argv[2] : "";
|
||||
|
||||
err = imgtool::directory::open(*partition, path, imgenum);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
memset(&ent, 0, sizeof(ent));
|
||||
last_modified[0] = '\0';
|
||||
total_count = 0;
|
||||
total_size = 0;
|
||||
|
||||
util::stream_format(std::wcout, L"Contents of %s:%s\n", wstring_from_utf8(argv[1]), wstring_from_utf8(path));
|
||||
|
||||
info = image->info();
|
||||
if (!info.empty())
|
||||
util::stream_format(std::wcout, L"%s\n", wstring_from_utf8(info));
|
||||
|
||||
util::stream_format(std::wcout, L"%s\n", wstring_from_utf8(separator));
|
||||
|
||||
while (((err = imgenum->get_next(ent)) == 0) && !ent.eof)
|
||||
{
|
||||
std::string filesize_string = ent.directory
|
||||
? "<DIR>"
|
||||
: util::string_format("%u", (unsigned int) ent.filesize);
|
||||
|
||||
if (!ent.lastmodified_time.empty())
|
||||
{
|
||||
std::tm t = ent.lastmodified_time.localtime();
|
||||
strftime(last_modified, sizeof(last_modified), "%d-%b-%y %H:%M:%S", &t);
|
||||
}
|
||||
|
||||
if (ent.hardlink)
|
||||
strcat(ent.filename, " <hl>");
|
||||
|
||||
util::stream_format(std::wcout,
|
||||
L"%*s %*s %*s %*s\n",
|
||||
-columnwidth_filename, wstring_from_utf8(ent.filename),
|
||||
columnwidth_filesize, wstring_from_utf8(filesize_string),
|
||||
columnwidth_attributes, wstring_from_utf8(ent.attr),
|
||||
columnwidth_lastmodified, wstring_from_utf8(last_modified));
|
||||
|
||||
if (ent.softlink[0] != '\0')
|
||||
util::stream_format(std::wcout, L"-> %s\n", wstring_from_utf8(ent.softlink));
|
||||
|
||||
if (ent.comment[0] != '\0')
|
||||
util::stream_format(std::wcout, L": %s\n", wstring_from_utf8(ent.comment));
|
||||
|
||||
total_count++;
|
||||
total_size += ent.filesize;
|
||||
|
||||
memset(&ent, 0, sizeof(ent));
|
||||
}
|
||||
|
||||
freespace_err = partition->get_free_space(freespace);
|
||||
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
util::stream_format(std::wcout, L"%s\n", wstring_from_utf8(separator));
|
||||
util::stream_format(std::wcout, L"%8i File(s) %8i bytes", total_count, total_size);
|
||||
if (!freespace_err)
|
||||
util::stream_format(std::wcout, L" %8u bytes free\n", (unsigned int)freespace);
|
||||
else
|
||||
util::stream_format(std::wcout, L"\n");
|
||||
|
||||
done:
|
||||
if (err)
|
||||
reporterror(err, c, argv[0], argv[1], nullptr, nullptr, nullptr);
|
||||
return err ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cmd_get(const struct command *c, int argc, char *argv[])
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
imgtool::image::ptr image;
|
||||
imgtool::partition::ptr partition;
|
||||
const char *filename;
|
||||
char *new_filename;
|
||||
int unnamedargs = 0;
|
||||
filter_getinfoproc filter;
|
||||
const char *fork;
|
||||
int partition_index = 0;
|
||||
|
||||
err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_READ, image);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = imgtool::partition::open(*image, partition_index, partition);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
filename = interpret_filename(argv[2]);
|
||||
|
||||
unnamedargs = parse_options(argc, argv, 3, 4, nullptr, &filter, &fork);
|
||||
if (unnamedargs < 0)
|
||||
goto done;
|
||||
|
||||
new_filename = (unnamedargs == 4) ? argv[3] : nullptr;
|
||||
|
||||
err = partition->get_file(filename, fork, new_filename, filter);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = IMGTOOLERR_SUCCESS;
|
||||
|
||||
done:
|
||||
if (err)
|
||||
reporterror(err, c, argv[0], argv[1], argv[2], argv[3], nullptr);
|
||||
return (err || (unnamedargs < 0)) ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cmd_put(const struct command *c, int argc, char *argv[])
|
||||
{
|
||||
imgtoolerr_t err = IMGTOOLERR_SUCCESS;
|
||||
int i;
|
||||
imgtool::image::ptr image;
|
||||
imgtool::partition::ptr partition;
|
||||
const char *filename = nullptr;
|
||||
int unnamedargs;
|
||||
filter_getinfoproc filter;
|
||||
const imgtool_module *module;
|
||||
std::unique_ptr<util::option_resolution> resolution;
|
||||
const char *fork;
|
||||
const char *new_filename;
|
||||
char **filename_list;
|
||||
int filename_count;
|
||||
int partition_index = 0;
|
||||
const util::option_guide *writefile_optguide;
|
||||
const char *writefile_optspec;
|
||||
|
||||
module = imgtool_find_module(argv[0]);
|
||||
if (!module)
|
||||
{
|
||||
err = (imgtoolerr_t)(IMGTOOLERR_MODULENOTFOUND | IMGTOOLERR_SRC_MODULE);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* ugh I hate the way this function is set up, this is because the
|
||||
* arguments depend on the partition; something that requires some
|
||||
* rudimentary parsing */
|
||||
if (argc >= 2)
|
||||
{
|
||||
/* open up the image */
|
||||
err = imgtool::image::open(module, argv[1], OSD_FOPEN_RW, image);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
/* open up the partition */
|
||||
err = imgtool::partition::open(*image, partition_index, partition);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
writefile_optguide = (const util::option_guide *) partition->get_info_ptr(IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE);
|
||||
writefile_optspec = (const char *)partition->get_info_ptr(IMGTOOLINFO_STR_WRITEFILE_OPTSPEC);
|
||||
|
||||
if (writefile_optguide && writefile_optspec)
|
||||
{
|
||||
try { resolution.reset(new util::option_resolution(*writefile_optguide)); }
|
||||
catch (...)
|
||||
{
|
||||
err = IMGTOOLERR_OUTOFMEMORY;
|
||||
goto done;
|
||||
}
|
||||
resolution->set_specification(writefile_optspec);
|
||||
}
|
||||
}
|
||||
|
||||
unnamedargs = parse_options(argc, argv, 4, 0xffff, resolution.get(), &filter, &fork);
|
||||
if (unnamedargs < 0)
|
||||
return -1;
|
||||
|
||||
/* pick out which args are filenames, and which one is the destination */
|
||||
new_filename = interpret_filename(argv[unnamedargs - 1]);
|
||||
filename_list = &argv[2];
|
||||
filename_count = unnamedargs - 3;
|
||||
|
||||
/* loop through the filenames, and put them */
|
||||
for (i = 0; i < filename_count; i++)
|
||||
{
|
||||
filename = filename_list[i];
|
||||
util::stream_format(std::wcout, L"Putting file '%s'...\n", wstring_from_utf8(filename));
|
||||
err = partition->put_file(new_filename, fork, filename, resolution.get(), filter);
|
||||
if (err)
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if (err)
|
||||
reporterror(err, c, argv[0], argv[1], filename, nullptr, resolution.get());
|
||||
return err ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cmd_getall(const struct command *c, int argc, char *argv[])
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
imgtool::image::ptr image;
|
||||
imgtool::partition::ptr partition;
|
||||
imgtool::directory::ptr imgenum;
|
||||
imgtool_dirent ent;
|
||||
filter_getinfoproc filter;
|
||||
int unnamedargs;
|
||||
const char *path = "";
|
||||
int arg;
|
||||
int partition_index = 0;
|
||||
|
||||
err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_READ, image);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = imgtool::partition::open(*image, partition_index, partition);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
arg = 2;
|
||||
if ((argc > 2) && (argv[2][0] != '-'))
|
||||
{
|
||||
path = argv[arg++];
|
||||
}
|
||||
|
||||
unnamedargs = parse_options(argc, argv, arg, arg, nullptr, &filter, nullptr);
|
||||
if (unnamedargs < 0)
|
||||
goto done;
|
||||
|
||||
err = imgtool::directory::open(*partition, path, imgenum);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
memset(&ent, 0, sizeof(ent));
|
||||
|
||||
while (((err = imgenum->get_next(ent)) == 0) && !ent.eof)
|
||||
{
|
||||
util::stream_format(std::wcout, L"Retrieving %s (%u bytes)\n", wstring_from_utf8(ent.filename), (unsigned int)ent.filesize);
|
||||
|
||||
err = partition->get_file(ent.filename, nullptr, nullptr, filter);
|
||||
if (err)
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if (err)
|
||||
reporterror(err, c, argv[0], argv[1], nullptr, nullptr, nullptr);
|
||||
return err ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cmd_del(const struct command *c, int argc, char *argv[])
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
imgtool::image::ptr image;
|
||||
imgtool::partition::ptr partition;
|
||||
int partition_index = 0;
|
||||
|
||||
err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_RW, image);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = imgtool::partition::open(*image, partition_index, partition);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = partition->delete_file(argv[2]);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
done:
|
||||
if (err)
|
||||
reporterror(err, c, argv[0], argv[1], argv[2], nullptr, nullptr);
|
||||
return err ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cmd_mkdir(const struct command *c, int argc, char *argv[])
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
imgtool::image::ptr image;
|
||||
imgtool::partition::ptr partition;
|
||||
int partition_index = 0;
|
||||
|
||||
err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_RW, image);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = imgtool::partition::open(*image, partition_index, partition);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = partition->create_directory(argv[2]);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
done:
|
||||
if (err)
|
||||
reporterror(err, c, argv[0], argv[1], argv[2], nullptr, nullptr);
|
||||
return err ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cmd_rmdir(const struct command *c, int argc, char *argv[])
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
imgtool::image::ptr image;
|
||||
imgtool::partition::ptr partition;
|
||||
int partition_index = 0;
|
||||
|
||||
err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_RW, image);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = imgtool::partition::open(*image, partition_index, partition);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = partition->delete_directory(argv[2]);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
done:
|
||||
if (err)
|
||||
reporterror(err, c, argv[0], argv[1], argv[2], nullptr, nullptr);
|
||||
return err ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cmd_identify(const struct command *c, int argc, char *argv[])
|
||||
{
|
||||
imgtool_module *modules[128];
|
||||
imgtoolerr_t err;
|
||||
int i;
|
||||
|
||||
err = imgtool::image::identify_file(argv[0], modules, std::size(modules));
|
||||
if (err)
|
||||
{
|
||||
reporterror(err, c, nullptr, argv[0], nullptr, nullptr, nullptr);
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; modules[i]; i++)
|
||||
{
|
||||
util::stream_format(std::wcout, L"%.16s %s\n", wstring_from_utf8(modules[i]->name), wstring_from_utf8(modules[i]->description));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cmd_create(const struct command *c, int argc, char *argv[])
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
int unnamedargs;
|
||||
const imgtool_module *module;
|
||||
std::unique_ptr<util::option_resolution> resolution;
|
||||
|
||||
module = imgtool_find_module(argv[0]);
|
||||
if (!module)
|
||||
{
|
||||
err = (imgtoolerr_t)(IMGTOOLERR_MODULENOTFOUND | IMGTOOLERR_SRC_MODULE);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (module->createimage_optguide && !module->createimage_optspec.empty())
|
||||
{
|
||||
try { resolution.reset(new util::option_resolution(*module->createimage_optguide)); }
|
||||
catch (...)
|
||||
{
|
||||
err = IMGTOOLERR_OUTOFMEMORY;
|
||||
goto error;
|
||||
}
|
||||
resolution->set_specification(module->createimage_optspec.c_str());
|
||||
}
|
||||
|
||||
unnamedargs = parse_options(argc, argv, 2, 3, resolution.get(), nullptr, nullptr);
|
||||
if (unnamedargs < 0)
|
||||
return -1;
|
||||
|
||||
err = imgtool::image::create(module, argv[1], resolution.get());
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
reporterror(err, c, argv[0], argv[1], nullptr, nullptr, nullptr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cmd_readsector(const struct command *c, int argc, char *argv[])
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
std::unique_ptr<imgtool::image> img;
|
||||
imgtool::stream::ptr stream;
|
||||
std::vector<uint8_t> buffer;
|
||||
uint32_t track, head, sector;
|
||||
|
||||
/* attempt to open image */
|
||||
err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_READ, img);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
track = atoi(argv[2]);
|
||||
head = atoi(argv[3]);
|
||||
sector = atoi(argv[4]);
|
||||
|
||||
err = img->read_sector(track, head, sector, buffer);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
stream = imgtool::stream::open(argv[5], OSD_FOPEN_WRITE);
|
||||
if (!stream)
|
||||
{
|
||||
err = (imgtoolerr_t)(IMGTOOLERR_FILENOTFOUND | IMGTOOLERR_SRC_NATIVEFILE);
|
||||
goto done;
|
||||
}
|
||||
|
||||
stream->write(&buffer[0], buffer.size());
|
||||
|
||||
done:
|
||||
if (err)
|
||||
reporterror(err, c, argv[0], argv[1], nullptr, nullptr, nullptr);
|
||||
return err ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cmd_writesector(const struct command *c, int argc, char *argv[])
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
std::unique_ptr<imgtool::image> img;
|
||||
imgtool::stream::ptr stream;
|
||||
std::vector<uint8_t> buffer;
|
||||
uint32_t size, track, head, sector;
|
||||
|
||||
// attempt to open image
|
||||
err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_RW, img);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
track = atoi(argv[2]);
|
||||
head = atoi(argv[3]);
|
||||
sector = atoi(argv[4]);
|
||||
|
||||
stream = imgtool::stream::open(argv[5], OSD_FOPEN_READ);
|
||||
if (!stream)
|
||||
{
|
||||
err = (imgtoolerr_t)(IMGTOOLERR_FILENOTFOUND | IMGTOOLERR_SRC_NATIVEFILE);
|
||||
goto done;
|
||||
}
|
||||
|
||||
size = (uint32_t) stream->size();
|
||||
|
||||
buffer.resize(size);
|
||||
|
||||
stream->read(&buffer[0], size);
|
||||
|
||||
err = img->write_sector(track, head, sector, &buffer[0], size);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
done:
|
||||
if (err)
|
||||
reporterror(err, c, argv[0], argv[1], nullptr, nullptr, nullptr);
|
||||
return err ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cmd_listformats(const struct command *c, int argc, char *argv[])
|
||||
{
|
||||
util::stream_format(std::wcout, L"Image formats supported by imgtool:\n\n");
|
||||
|
||||
for (const auto &module : imgtool_get_modules())
|
||||
{
|
||||
util::stream_format(std::wcout, L" %-25s%s\n", wstring_from_utf8(module->name), wstring_from_utf8(module->description));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cmd_listfilters(const struct command *c, int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
util::stream_format(std::wcout, L"Filters supported by imgtool:\n\n");
|
||||
|
||||
for (i = 0; filters[i]; i++)
|
||||
{
|
||||
util::stream_format(std::wcout, L" %-11s%s\n",
|
||||
wstring_from_utf8(filter_get_info_string(filters[i], FILTINFO_STR_NAME)),
|
||||
wstring_from_utf8(filter_get_info_string(filters[i], FILTINFO_STR_HUMANNAME)));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void listoptions(const util::option_guide &opt_guide, const char *opt_spec)
|
||||
{
|
||||
util::option_resolution resolution(opt_guide);
|
||||
resolution.set_specification(opt_spec);
|
||||
|
||||
util::stream_format(std::wcout, L"Option Allowed values Description\n");
|
||||
util::stream_format(std::wcout, L"---------------- ------------------------------ -----------\n");
|
||||
|
||||
for (auto iter = resolution.entries_begin(); iter != resolution.entries_end(); iter++)
|
||||
{
|
||||
const util::option_resolution::entry &entry = *iter;
|
||||
std::stringstream description_buffer;
|
||||
|
||||
std::string opt_name = util::string_format("--%s", entry.identifier());
|
||||
const char *opt_desc = entry.display_name();
|
||||
|
||||
// is this option relevant?
|
||||
if (!strchr(opt_spec, entry.parameter()))
|
||||
continue;
|
||||
|
||||
switch (entry.option_type())
|
||||
{
|
||||
case util::option_guide::entry::option_type::INT:
|
||||
for (const auto &range : entry.ranges())
|
||||
{
|
||||
if (!description_buffer.str().empty())
|
||||
description_buffer << "/";
|
||||
|
||||
if (range.min == range.max)
|
||||
util::stream_format(description_buffer, "%d", range.min);
|
||||
else
|
||||
util::stream_format(description_buffer, "%d-%d", range.min, range.max);
|
||||
}
|
||||
break;
|
||||
|
||||
case util::option_guide::entry::option_type::ENUM_BEGIN:
|
||||
for (auto enum_value = entry.enum_value_begin(); enum_value != entry.enum_value_end(); enum_value++)
|
||||
{
|
||||
if (!description_buffer.str().empty())
|
||||
description_buffer << '/';
|
||||
description_buffer << enum_value->identifier();
|
||||
}
|
||||
break;
|
||||
|
||||
case util::option_guide::entry::option_type::STRING:
|
||||
description_buffer << "(string)";
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
util::stream_format(std::wcout, L"%16s %-30s %s\n",
|
||||
wstring_from_utf8(opt_name),
|
||||
wstring_from_utf8(description_buffer.str()),
|
||||
wstring_from_utf8(opt_desc));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cmd_listdriveroptions(const struct command *c, int argc, char *argv[])
|
||||
{
|
||||
const imgtool_module *mod;
|
||||
const util::option_guide *opt_guide;
|
||||
const char *opt_spec;
|
||||
|
||||
mod = imgtool_find_module(argv[0]);
|
||||
if (!mod)
|
||||
{
|
||||
reporterror((imgtoolerr_t)(IMGTOOLERR_MODULENOTFOUND|IMGTOOLERR_SRC_MODULE), c, argv[0], nullptr, nullptr, nullptr, nullptr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
util::stream_format(std::wcout, L"Driver specific options for module '%s':\n\n", wstring_from_utf8(argv[0]));
|
||||
|
||||
/* list write options */
|
||||
opt_guide = (const util::option_guide *) imgtool_get_info_ptr(&mod->imgclass, IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE);
|
||||
opt_spec = imgtool_get_info_string(&mod->imgclass, IMGTOOLINFO_STR_WRITEFILE_OPTSPEC);
|
||||
if (opt_guide)
|
||||
{
|
||||
util::stream_format(std::wcout, L"Image specific file options (usable on the 'put' command):\n\n");
|
||||
listoptions(*opt_guide, opt_spec);
|
||||
util::stream_format(std::wcout, L"\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
util::stream_format(std::wcout, L"No image specific file options\n\n");
|
||||
}
|
||||
|
||||
/* list create options */
|
||||
opt_guide = mod->createimage_optguide;
|
||||
if (opt_guide)
|
||||
{
|
||||
util::stream_format(std::wcout, L"Image specific creation options (usable on the 'create' command):\n\n");
|
||||
listoptions(*opt_guide, mod->createimage_optspec.c_str());
|
||||
util::stream_format(std::wcout, L"\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
util::stream_format(std::wcout, L"No image specific creation options\n\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static const struct command cmds[] =
|
||||
{
|
||||
{ "create", cmd_create, "<format> <imagename> [--(createoption)=value]", 2, 8, 0},
|
||||
{ "dir", cmd_dir, "<format> <imagename> [path]", 2, 3, 0 },
|
||||
{ "get", cmd_get, "<format> <imagename> <filename> [newname] [--filter=filter] [--fork=fork]", 3, 6, 0 },
|
||||
{ "put", cmd_put, "<format> <imagename> <filename>... <destname> [--(fileoption)==value] [--filter=filter] [--fork=fork]", 3, 0xffff, 0 },
|
||||
{ "getall", cmd_getall, "<format> <imagename> [path] [--filter=filter]", 2, 3, 0 },
|
||||
{ "del", cmd_del, "<format> <imagename> <filename>...", 3, 3, 1 },
|
||||
{ "mkdir", cmd_mkdir, "<format> <imagename> <dirname>", 3, 3, 0 },
|
||||
{ "rmdir", cmd_rmdir, "<format> <imagename> <dirname>...", 3, 3, 1 },
|
||||
{ "readsector", cmd_readsector, "<format> <imagename> <track> <head> <sector> <filename>", 6, 6, 0 },
|
||||
{ "writesector", cmd_writesector, "<format> <imagename> <track> <head> <sector> <filename>", 6, 6, 0 },
|
||||
{ "identify", cmd_identify, "<imagename>", 1, 1 },
|
||||
{ "listformats", cmd_listformats, nullptr, 0, 0, 0 },
|
||||
{ "listfilters", cmd_listfilters, nullptr, 0, 0, 0 },
|
||||
{ "listdriveroptions", cmd_listdriveroptions, "<format>", 1, 1, 0 }
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
int result;
|
||||
const struct command *c;
|
||||
const char *sample_format = "coco_jvc_rsdos";
|
||||
std::string cmdname(core_filename_extract_base(argv[0]));
|
||||
|
||||
#ifdef _WIN32
|
||||
_setmode(_fileno(stdout), _O_U8TEXT);
|
||||
#endif // _WIN32
|
||||
|
||||
#ifdef MAME_DEBUG
|
||||
if (imgtool_validitychecks())
|
||||
return -1;
|
||||
#endif // MAME_DEBUG
|
||||
|
||||
// convert arguments to UTF-8
|
||||
std::vector<std::string> args = osd_get_command_line(argc, argv);
|
||||
argv = (char **)alloca(sizeof(char *) * args.size());
|
||||
for (i = 0; i < args.size(); i++)
|
||||
argv[i] = (char *)args[i].c_str();
|
||||
|
||||
util::stream_format(std::wcout, L"\n");
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
/* figure out what command they are running, and run it */
|
||||
for (i = 0; i < std::size(cmds); i++)
|
||||
{
|
||||
c = &cmds[i];
|
||||
if (!core_stricmp(c->name, argv[1]))
|
||||
{
|
||||
/* check argument count */
|
||||
if (c->minargs > (argc - 2))
|
||||
goto cmderror;
|
||||
|
||||
/* initialize the imgtool core */
|
||||
imgtool_init(true, nullptr);
|
||||
|
||||
if (c->lastargrepeats && (argc > c->maxargs))
|
||||
{
|
||||
for (i = c->maxargs+1; i < argc; i++)
|
||||
{
|
||||
argv[c->maxargs+1] = argv[i];
|
||||
|
||||
result = c->cmdproc(c, c->maxargs, argv + 2);
|
||||
if (result)
|
||||
goto done;
|
||||
}
|
||||
result = 0;
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((c->maxargs > 0) && (c->maxargs < (argc - 2)))
|
||||
goto cmderror;
|
||||
|
||||
result = c->cmdproc(c, argc - 2, argv + 2);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
util::stream_format(std::wcerr, L"imgtool - Generic image manipulation tool for use with MAME\n\n");
|
||||
for (i = 0; i < std::size(cmds); i++)
|
||||
{
|
||||
writeusage(std::wcerr, (i == 0), &cmds[i], argv);
|
||||
}
|
||||
util::stream_format(std::wcerr, L"\n<format> is the image format, e.g. %s\n", wstring_from_utf8(sample_format));
|
||||
util::stream_format(std::wcerr, L"<imagename> is the image filename; can specify a ZIP file for image name\n");
|
||||
|
||||
util::stream_format(std::wcerr, L"\nExample usage:\n");
|
||||
util::stream_format(std::wcerr, L"\t%s dir %s myimageinazip.zip\n", wstring_from_utf8(cmdname), wstring_from_utf8(sample_format));
|
||||
util::stream_format(std::wcerr, L"\t%s get %s myimage.dsk myfile.bin mynewfile.txt\n", wstring_from_utf8(cmdname), wstring_from_utf8(sample_format));
|
||||
util::stream_format(std::wcerr, L"\t%s getall %s myimage.dsk\n", wstring_from_utf8(cmdname), wstring_from_utf8(sample_format));
|
||||
result = 0;
|
||||
goto done;
|
||||
|
||||
cmderror:
|
||||
writeusage(std::wcout, 1, &cmds[i], argv);
|
||||
result = -1;
|
||||
|
||||
done:
|
||||
imgtool_exit();
|
||||
return result;
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
#include "imgtool.h"
|
||||
|
||||
struct command
|
||||
{
|
||||
const char *name;
|
||||
int (*cmdproc)(const struct command *c, int argc, char *argv[]);
|
||||
const char *usage;
|
||||
int minargs;
|
||||
int maxargs;
|
||||
int lastargrepeats;
|
||||
};
|
||||
|
||||
void reporterror(imgtoolerr_t err, const struct command *c, const char *format, const char *imagename,
|
||||
const char *filename, const char *newname, util::option_resolution *opts);
|
||||
|
||||
#ifdef MAME_DEBUG
|
||||
int cmd_testsuite(struct command *c, int argc, char *argv[]);
|
||||
#endif
|
@ -1,104 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/***************************************************************************
|
||||
|
||||
modules.c
|
||||
|
||||
List of Imgtool modules
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "modules.h"
|
||||
|
||||
#ifndef MODULES_RECURSIVE
|
||||
#define MODULES_RECURSIVE
|
||||
|
||||
/* step 1: declare all external references */
|
||||
#define MODULE(name) extern void name##_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info);
|
||||
#include "modules.cpp"
|
||||
#undef MODULE
|
||||
|
||||
/* step 2: define the modules[] array */
|
||||
#define MODULE(name) name##_get_info,
|
||||
static void (*const modules[])(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) =
|
||||
{
|
||||
#include "modules.cpp"
|
||||
};
|
||||
|
||||
/* step 3: declare imgtool_create_canonical_library() */
|
||||
imgtoolerr_t imgtool_create_canonical_library(bool omit_untested, std::unique_ptr<imgtool::library> &library)
|
||||
{
|
||||
/* list of modules that we drop */
|
||||
static const char *const irrelevant_modules[] =
|
||||
{
|
||||
"coco_os9_rsdos"
|
||||
};
|
||||
|
||||
library.reset(new imgtool::library());
|
||||
if (!library)
|
||||
return IMGTOOLERR_OUTOFMEMORY;
|
||||
|
||||
// create all modules
|
||||
for (auto &module : modules)
|
||||
library->add(module);
|
||||
|
||||
// remove irrelevant modules
|
||||
for (auto &module : irrelevant_modules)
|
||||
library->unlink(module);
|
||||
|
||||
// if we are omitting untested, go through and block out the functionality in question
|
||||
if (omit_untested)
|
||||
{
|
||||
for (auto &module : library->modules())
|
||||
{
|
||||
if (module->writing_untested)
|
||||
{
|
||||
module->write_sector = nullptr;
|
||||
}
|
||||
if (module->creation_untested)
|
||||
{
|
||||
module->create = nullptr;
|
||||
module->createimage_optguide = nullptr;
|
||||
module->createimage_optspec.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
#else /* MODULES_RECURSIVE */
|
||||
|
||||
MODULE(amiga_floppy)
|
||||
MODULE(concept)
|
||||
MODULE(mac_mfs)
|
||||
MODULE(mac_hfs)
|
||||
MODULE(hd)
|
||||
MODULE(rsdos)
|
||||
MODULE(dgndos)
|
||||
MODULE(vzdos)
|
||||
MODULE(os9)
|
||||
MODULE(ti99_old)
|
||||
MODULE(ti99_v9t9)
|
||||
MODULE(ti99_pc99fm)
|
||||
MODULE(ti99_pc99mfm)
|
||||
MODULE(ti99_ti99hd)
|
||||
MODULE(ti990)
|
||||
MODULE(pc_floppy)
|
||||
MODULE(pc_chd)
|
||||
MODULE(prodos_525)
|
||||
MODULE(prodos_35)
|
||||
MODULE(thom_fd_basic)
|
||||
MODULE(thom_qd_basic)
|
||||
MODULE(thom_sap_basic)
|
||||
MODULE(cybiko)
|
||||
MODULE(cybikoxt)
|
||||
MODULE(psion)
|
||||
MODULE(bml3)
|
||||
MODULE(hp48)
|
||||
MODULE(hp9845_tape)
|
||||
MODULE(hp85_tape)
|
||||
MODULE(rt11)
|
||||
|
||||
#endif /* MODULES_RECURSIVE */
|
@ -1,18 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/****************************************************************************
|
||||
|
||||
modules.h
|
||||
|
||||
Code that creates the "canonical" Imgtool library
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef MODULES_H
|
||||
#define MODULES_H
|
||||
|
||||
#include "library.h"
|
||||
|
||||
imgtoolerr_t imgtool_create_canonical_library(bool omit_untested, std::unique_ptr<imgtool::library> &library);
|
||||
|
||||
#endif /* MODULES_H */
|
File diff suppressed because it is too large
Load Diff
@ -1,932 +0,0 @@
|
||||
// license:GPL-2.0+
|
||||
// copyright-holders:Jonathan Edwards
|
||||
/****************************************************************************
|
||||
|
||||
bml3.c
|
||||
|
||||
Hitachi bml3 disk images
|
||||
|
||||
By Jonathan Edwards, based on rsdos.c (both use Microsoft BASIC)
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
/* Supported Hitachi floppy formats are:
|
||||
- 3" or 5"1/4 single density, single-sided: 40 tracks, 16 sectors/track, 128 bytes/sector
|
||||
- (used with MP-1805 floppy disk controller card)
|
||||
- 5"1/4 double density, double-sided: 40 tracks, 16 sectors/track, 256 bytes/sector
|
||||
- (first track on first head may be single density)
|
||||
- (used with MP-1802 floppy disk controller card)
|
||||
*/
|
||||
|
||||
#include "imgtool.h"
|
||||
#include "filter.h"
|
||||
#include "iflopimg.h"
|
||||
|
||||
#include "corestr.h"
|
||||
#include "opresolv.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#define MAX_SECTOR_SIZE 256
|
||||
|
||||
struct bml3_diskinfo
|
||||
{
|
||||
uint16_t sector_size; /* 128 or 256 */
|
||||
uint8_t heads; /* 1 or 2 */
|
||||
uint8_t fat_start_sector; /* in cylinder 20, start sector of FAT */
|
||||
uint8_t fat_start_offset; /* start byte of FAT in sector */
|
||||
uint8_t fat_sectors; /* the number of sectors in the FAT */
|
||||
uint8_t dirent_start_sector; /* in cylinder 20, start sector of directory entries */
|
||||
uint8_t granule_sectors; /* how many sectors per granule */
|
||||
uint8_t first_granule_cylinder; /* the number of the first cylinder with granule numbers assigned */
|
||||
uint8_t variant; /* 0 - older version, uses EOF to terminate files, 1 - newer version, stores file length */
|
||||
};
|
||||
|
||||
/* this structure mirrors the structure of a directory entry on disk */
|
||||
struct bml3_dirent
|
||||
{
|
||||
char fname[8];
|
||||
char fext[3];
|
||||
uint8_t ftype;
|
||||
uint8_t asciiflag;
|
||||
uint8_t first_granule;
|
||||
uint16_t lastsectorbytes;
|
||||
// TODO there are some 'unused' bytes here that are sometimes used to store a timestamp, maybe support this?
|
||||
};
|
||||
|
||||
struct bml3_direnum
|
||||
{
|
||||
int index;
|
||||
int eof;
|
||||
};
|
||||
|
||||
#define MAX_GRANULEMAP_SIZE 256
|
||||
|
||||
struct granule_list_t {
|
||||
uint8_t granules[MAX_GRANULEMAP_SIZE];
|
||||
uint8_t granule_count;
|
||||
uint8_t last_granule_sectors;
|
||||
};
|
||||
|
||||
#define BML3_OPTIONS_FTYPE 'T'
|
||||
#define BML3_OPTIONS_ASCII 'M'
|
||||
|
||||
static imgtoolerr_t bml3_diskimage_deletefile(imgtool::partition &partition, const char *fname);
|
||||
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
Imgtool module code
|
||||
*********************************************************************/
|
||||
|
||||
static bml3_diskinfo *bml3_get_diskinfo(imgtool::image &image)
|
||||
{
|
||||
return (bml3_diskinfo *) imgtool_floppy_extrabytes(image);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int max_dirents(imgtool::image &image)
|
||||
{
|
||||
bml3_diskinfo *info = bml3_get_diskinfo(image);
|
||||
return (16 * info->heads + 1 - info->dirent_start_sector)*(info->sector_size/32);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void dirent_location(imgtool::image &image, int index_loc, uint8_t *head, uint8_t *track, uint8_t *sector, uint8_t *offset)
|
||||
{
|
||||
bml3_diskinfo *info = bml3_get_diskinfo(image);
|
||||
*track = 20;
|
||||
*sector = info->dirent_start_sector + index_loc / (info->sector_size / 32);
|
||||
*head = 0;
|
||||
if (*sector > 16) {
|
||||
// wrap to second head
|
||||
*sector -= 16;
|
||||
(*head)++;
|
||||
}
|
||||
*offset = index_loc % (info->sector_size/32) * 32;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static floperr_t get_bml3_dirent(imgtool::image &f, int index_loc, struct bml3_dirent *ent)
|
||||
{
|
||||
floperr_t err;
|
||||
uint8_t head, track, sector, offset;
|
||||
uint8_t buf[32];
|
||||
bml3_diskinfo *info = bml3_get_diskinfo(f);
|
||||
dirent_location(f, index_loc, &head, &track, §or, &offset);
|
||||
err = floppy_read_sector(imgtool_floppy(f), head, track, sector, offset, (void *) buf, sizeof(buf));
|
||||
memset(ent, 0, sizeof(*ent));
|
||||
switch (info->variant) {
|
||||
case 0:
|
||||
memcpy(&ent->fname, &buf[0], 8);
|
||||
ent->ftype = buf[11];
|
||||
ent->asciiflag = buf[12];
|
||||
ent->first_granule = buf[14];
|
||||
break;
|
||||
case 1:
|
||||
memcpy(&ent->fname, &buf[0], 8);
|
||||
memcpy(&ent->fext, &buf[8], 3);
|
||||
ent->ftype = buf[11];
|
||||
ent->asciiflag = buf[12];
|
||||
ent->first_granule = buf[13];
|
||||
ent->lastsectorbytes = (buf[14] << 8) | buf[15];
|
||||
break;
|
||||
default:
|
||||
return FLOPPY_ERROR_INVALIDIMAGE;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static floperr_t put_bml3_dirent(imgtool::image &f, int index_loc, const struct bml3_dirent *ent)
|
||||
{
|
||||
floperr_t err;
|
||||
uint8_t head, track, sector, offset;
|
||||
uint8_t buf[32];
|
||||
bml3_diskinfo *info = bml3_get_diskinfo(f);
|
||||
if (index_loc >= max_dirents(f))
|
||||
return (floperr_t)IMGTOOLERR_FILENOTFOUND;
|
||||
dirent_location(f, index_loc, &head, &track, §or, &offset);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
switch (info->variant) {
|
||||
case 0:
|
||||
memcpy(&buf[0], &ent->fname, 8);
|
||||
buf[11] = ent->ftype;
|
||||
buf[12] = ent->asciiflag;
|
||||
buf[14] = ent->first_granule;
|
||||
break;
|
||||
case 1:
|
||||
memcpy(&buf[0], &ent->fname, 8);
|
||||
memcpy(&buf[8], &ent->fext, 3);
|
||||
buf[11] = ent->ftype;
|
||||
buf[12] = ent->asciiflag;
|
||||
buf[13] = ent->first_granule;
|
||||
buf[14] = ent->lastsectorbytes >> 8;
|
||||
buf[15] = ent->lastsectorbytes & 0xff;
|
||||
break;
|
||||
default:
|
||||
return FLOPPY_ERROR_INVALIDIMAGE;
|
||||
}
|
||||
err = floppy_write_sector(imgtool_floppy(f), head, track, sector, offset, (void *) buf, sizeof(buf), 0); /* TODO: pass ddam argument from imgtool */
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* fnamebuf must have at least 13 bytes */
|
||||
static void get_dirent_fname(char *fnamebuf, const struct bml3_dirent *ent)
|
||||
{
|
||||
char *s;
|
||||
|
||||
memset(fnamebuf, 0, 13);
|
||||
memcpy(fnamebuf, ent->fname, sizeof(ent->fname));
|
||||
rtrim(fnamebuf);
|
||||
s = fnamebuf + strlen(fnamebuf);
|
||||
*(s++) = '.';
|
||||
memcpy(s, ent->fext, sizeof(ent->fext));
|
||||
rtrim(s);
|
||||
|
||||
/* If no extension, remove period */
|
||||
if (*s == '\0')
|
||||
s[-1] = '\0';
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t lookup_bml3_file(imgtool::image &f, const char *fname, struct bml3_dirent *ent, int *position)
|
||||
{
|
||||
int i;
|
||||
floperr_t ferr;
|
||||
char fnamebuf[13];
|
||||
|
||||
i = 0;
|
||||
fnamebuf[0] = '\0';
|
||||
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
ferr = get_bml3_dirent(f, i++, ent);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
}
|
||||
while(ent->fname[0] == '\0');
|
||||
|
||||
|
||||
if (ent->fname[0] != -1)
|
||||
get_dirent_fname(fnamebuf, ent);
|
||||
}
|
||||
while((ent->fname[0] != -1) && core_stricmp(fnamebuf, fname));
|
||||
|
||||
if (ent->fname[0] == -1)
|
||||
return IMGTOOLERR_FILENOTFOUND;
|
||||
|
||||
if (position)
|
||||
*position = i - 1;
|
||||
return (imgtoolerr_t)0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static uint8_t get_granule_count(imgtool::image &img)
|
||||
{
|
||||
// uint16_t tracks;
|
||||
uint16_t disk_granules;
|
||||
bml3_diskinfo *info = bml3_get_diskinfo(img);
|
||||
|
||||
// This always returns 82 for D88, so not quite right
|
||||
// tracks = floppy_get_tracks_per_disk(imgtool_floppy(img));
|
||||
|
||||
// The number of granules is primarily constrained by the disk capacity.
|
||||
disk_granules = (40 - 1 - info->first_granule_cylinder) * info->heads * (16 / info->granule_sectors);
|
||||
// Also, granule numbers from 0xC0 upwards are reserved for terminating a granule chain
|
||||
return (uint8_t)((disk_granules < 0xC0) ? disk_granules : 0xC0);
|
||||
}
|
||||
|
||||
/* granule_map must be an array of MAX_GRANULEMAP_SIZE bytes */
|
||||
static floperr_t get_granule_map(imgtool::image &img, uint8_t *granule_map, uint8_t *granule_count)
|
||||
{
|
||||
bml3_diskinfo *info = bml3_get_diskinfo(img);
|
||||
uint8_t count;
|
||||
|
||||
count = get_granule_count(img);
|
||||
if (granule_count)
|
||||
*granule_count = count;
|
||||
|
||||
// The first byte of the granule map sector is ignored (and expected to be 0)
|
||||
return floppy_read_sector(imgtool_floppy(img), 0, 20, info->fat_start_sector, info->fat_start_offset, granule_map, count);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static floperr_t put_granule_map(imgtool::image &img, const uint8_t *granule_map, uint8_t granule_count)
|
||||
{
|
||||
bml3_diskinfo *info = bml3_get_diskinfo(img);
|
||||
return floppy_write_sector(imgtool_floppy(img), 0, 20, info->fat_start_sector, info->fat_start_offset, granule_map, granule_count, 0); /* TODO: pass ddam argument from imgtool */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void granule_location(imgtool::image &image, uint8_t granule, uint8_t *head, uint8_t *track, uint8_t *sector)
|
||||
{
|
||||
bml3_diskinfo *info = bml3_get_diskinfo(image);
|
||||
uint16_t abs_track = granule * info->granule_sectors / 16;
|
||||
*head = abs_track % info->heads;
|
||||
*track = abs_track / info->heads + info->first_granule_cylinder;
|
||||
// skip filesystem cylinder
|
||||
if (*track >= 20)
|
||||
(*track)++;
|
||||
*sector = granule * info->granule_sectors % 16 + 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t transfer_granule(imgtool::image &img, uint8_t granule, int length, imgtool::stream &f, imgtoolerr_t (*proc)(imgtool::image &, int, int, int, int, size_t, imgtool::stream &))
|
||||
{
|
||||
imgtoolerr_t err = IMGTOOLERR_SUCCESS;
|
||||
uint8_t head, track, sector;
|
||||
granule_location(img, granule, &head, &track, §or);
|
||||
if (length > 0)
|
||||
err = proc(img, head, track, sector, 0, length, f);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static imgtoolerr_t transfer_from_granule(imgtool::image &img, uint8_t granule, int length, imgtool::stream &destf)
|
||||
{
|
||||
return transfer_granule(img, granule, length, destf, imgtool_floppy_read_sector_to_stream);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t transfer_to_granule(imgtool::image &img, uint8_t granule, int length, imgtool::stream &sourcef)
|
||||
{
|
||||
return transfer_granule(img, granule, length, sourcef, imgtool_floppy_write_sector_from_stream);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static floperr_t read_granule(imgtool::image &img, uint8_t granule, int offset, int length, uint8_t *buf)
|
||||
{
|
||||
uint8_t head, track, sector;
|
||||
granule_location(img, granule, &head, &track, §or);
|
||||
return floppy_read_sector(imgtool_floppy(img), head, track, sector, offset, buf, length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static floperr_t write_granule(imgtool::image &img, uint8_t granule, int offset, int length, const uint8_t *buf)
|
||||
{
|
||||
uint8_t head, track, sector;
|
||||
granule_location(img, granule, &head, &track, §or);
|
||||
return floppy_write_sector(imgtool_floppy(img), head, track, sector, offset, buf, length, 0); /* TODO: pass ddam argument from imgtool */
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t list_granules(struct bml3_dirent *ent, imgtool::image &img, struct granule_list_t *granule_list)
|
||||
{
|
||||
floperr_t ferr;
|
||||
uint8_t max_granules;
|
||||
uint8_t granule;
|
||||
uint8_t usedmap[MAX_GRANULEMAP_SIZE]; /* Used to detect infinite loops */
|
||||
uint8_t granule_map[MAX_GRANULEMAP_SIZE];
|
||||
bml3_diskinfo *info = bml3_get_diskinfo(img);
|
||||
|
||||
ferr = get_granule_map(img, granule_map, &max_granules);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
memset(usedmap, 0, max_granules);
|
||||
|
||||
granule = ent->first_granule;
|
||||
granule_list->granule_count = 0;
|
||||
|
||||
while(!usedmap[granule] && granule < max_granules)
|
||||
{
|
||||
usedmap[granule] = 1;
|
||||
granule_list->granules[granule_list->granule_count++] = granule;
|
||||
granule = granule_map[granule];
|
||||
}
|
||||
|
||||
granule_list->last_granule_sectors = granule - 0xc0;
|
||||
if (info->variant == 0) {
|
||||
// add final incomplete sector
|
||||
granule_list->last_granule_sectors++;
|
||||
}
|
||||
|
||||
// A value of zero (variant 1) and max (variant 0) seem to indicate a file open for writing.
|
||||
// Strictly speaking this means the image is corrupt, although a real system will happily read
|
||||
// garbage from the file.
|
||||
if (granule_list->last_granule_sectors > info->granule_sectors)
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t get_file_size(struct bml3_dirent *ent, imgtool::image &img, const struct granule_list_t *granule_list, size_t *size)
|
||||
{
|
||||
floperr_t ferr;
|
||||
size_t last_sector_bytes = 0;
|
||||
bml3_diskinfo *info = bml3_get_diskinfo(img);
|
||||
|
||||
// TODO are these special cases valid, or maybe indicate a corrupt image?
|
||||
if (granule_list->granule_count == 0) {
|
||||
*size = 0;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
else if (granule_list->last_granule_sectors == 0) {
|
||||
*size = info->sector_size * ((granule_list->granule_count - 1) * info->granule_sectors);
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
// determine size excluding final sector
|
||||
*size = info->sector_size * ((granule_list->granule_count - 1) * info->granule_sectors + granule_list->last_granule_sectors - 1);
|
||||
|
||||
// determine bytes used in final sector
|
||||
switch (info->variant) {
|
||||
case 0:
|
||||
// look for EOF (ASCII SUB) and trailing NULs in final sector
|
||||
{
|
||||
uint8_t buf[MAX_SECTOR_SIZE];
|
||||
ferr = read_granule(img, granule_list->granules[granule_list->granule_count-1], info->sector_size * (granule_list->last_granule_sectors - 1), info->sector_size, buf);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
for (last_sector_bytes = info->sector_size - 1; ; last_sector_bytes--) {
|
||||
if (buf[last_sector_bytes] != 0)
|
||||
break;
|
||||
if (last_sector_bytes == 0)
|
||||
break;
|
||||
}
|
||||
if (buf[last_sector_bytes] != 0x1a) {
|
||||
last_sector_bytes++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
last_sector_bytes = ent->lastsectorbytes;
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO is it valid for last_sector_bytes == 0?
|
||||
if (last_sector_bytes > info->sector_size) {
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
}
|
||||
*size += last_sector_bytes;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t process_bml3_file(struct bml3_dirent *ent, imgtool::image &img, imgtool::stream *destf, size_t *size)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
size_t remaining_size, granule_size;
|
||||
bml3_diskinfo *info = bml3_get_diskinfo(img);
|
||||
struct granule_list_t granule_list;
|
||||
granule_list.granule_count = 0;
|
||||
|
||||
err = list_granules(ent, img, &granule_list);
|
||||
if (err)
|
||||
return err;
|
||||
err = get_file_size(ent, img, &granule_list, size);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (destf) {
|
||||
remaining_size = *size;
|
||||
granule_size = info->granule_sectors * info->sector_size;
|
||||
|
||||
for (int c = 0; c < granule_list.granule_count; c++) {
|
||||
if (granule_size >= remaining_size)
|
||||
granule_size = remaining_size;
|
||||
transfer_from_granule(img, granule_list.granules[c], granule_size, *destf);
|
||||
remaining_size -= granule_size;
|
||||
}
|
||||
}
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* create a new directory entry with a specified name */
|
||||
static imgtoolerr_t prepare_dirent(uint8_t variant, struct bml3_dirent *ent, const char *fname)
|
||||
{
|
||||
const char *fname_end;
|
||||
const char *fname_ext;
|
||||
int fname_ext_len;
|
||||
|
||||
memset(ent, '\0', sizeof(*ent));
|
||||
memset(ent->fname, ' ', sizeof(ent->fname));
|
||||
memset(ent->fext, ' ', sizeof(ent->fext));
|
||||
|
||||
fname_end = strchr(fname, '.');
|
||||
if (fname_end)
|
||||
fname_ext = fname_end + 1;
|
||||
else
|
||||
fname_end = fname_ext = fname + strlen(fname);
|
||||
|
||||
fname_ext_len = strlen(fname_ext);
|
||||
|
||||
switch (variant) {
|
||||
case 0:
|
||||
/* 8-character max filename */
|
||||
if (((fname_end - fname) > 8) || (fname_ext_len > 0))
|
||||
return IMGTOOLERR_BADFILENAME;
|
||||
break;
|
||||
case 1:
|
||||
/*8.3 filename */
|
||||
if (((fname_end - fname) > 8) || (fname_ext_len > 3))
|
||||
return IMGTOOLERR_BADFILENAME;
|
||||
break;
|
||||
default:
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
}
|
||||
|
||||
memcpy(ent->fname, fname, fname_end - fname);
|
||||
memcpy(ent->fext, fname_ext, fname_ext_len);
|
||||
|
||||
/* By default, set as a type 2 binary file */
|
||||
ent->ftype = 2;
|
||||
ent->asciiflag = 0;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t bml3_diskimage_open(imgtool::image &image, imgtool::stream::ptr &&dummy)
|
||||
{
|
||||
// imgtoolerr_t err;
|
||||
floperr_t ferr;
|
||||
bml3_diskinfo *info = bml3_get_diskinfo(image);
|
||||
floppy_image_legacy *floppy = imgtool_floppy(image);
|
||||
const struct FloppyCallbacks *callbacks = floppy_callbacks(floppy);
|
||||
|
||||
// probe disk geometry to guess format
|
||||
int heads_per_disk = callbacks->get_heads_per_disk(floppy);
|
||||
uint32_t sector_length;
|
||||
ferr = callbacks->get_sector_length(floppy, 0, 20, 1, §or_length);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
int sectors_per_track = callbacks->get_sectors_per_track(floppy, 0, 20);
|
||||
|
||||
if (heads_per_disk == 2 && sector_length == 128 && sectors_per_track == 16) {
|
||||
// single-sided, single-density
|
||||
info->sector_size = 128;
|
||||
info->heads = 1;
|
||||
info->fat_start_sector = 1;
|
||||
info->fat_start_offset = 5;
|
||||
info->fat_sectors = 2;
|
||||
info->dirent_start_sector = 7;
|
||||
info->granule_sectors = 4;
|
||||
info->first_granule_cylinder = 0;
|
||||
info->variant = 0;
|
||||
}
|
||||
else if (heads_per_disk == 2 && sector_length == 256 && sectors_per_track == 16) {
|
||||
// double-sided, double-density
|
||||
info->sector_size = 256;
|
||||
info->heads = 2;
|
||||
info->fat_start_sector = 2;
|
||||
info->fat_start_offset = 1;
|
||||
info->fat_sectors = 1;
|
||||
info->dirent_start_sector = 5;
|
||||
info->granule_sectors = 8;
|
||||
info->first_granule_cylinder = 1;
|
||||
info->variant = 1;
|
||||
}
|
||||
else {
|
||||
// invalid or unsupported format
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t bml3_diskimage_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent)
|
||||
{
|
||||
floperr_t ferr;
|
||||
imgtoolerr_t err;
|
||||
size_t filesize;
|
||||
struct bml3_direnum *rsenum;
|
||||
struct bml3_dirent rsent;
|
||||
char fname[13];
|
||||
imgtool::image &image(enumeration.image());
|
||||
|
||||
rsenum = (struct bml3_direnum *) enumeration.extra_bytes();
|
||||
|
||||
/* Did we hit the end of file before? */
|
||||
if (rsenum->eof)
|
||||
goto eof;
|
||||
|
||||
do
|
||||
{
|
||||
if (rsenum->index >= max_dirents(image))
|
||||
goto eof;
|
||||
|
||||
ferr = get_bml3_dirent(image, rsenum->index++, &rsent);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
}
|
||||
while(rsent.fname[0] == '\0');
|
||||
|
||||
/* Now are we at the eof point? */
|
||||
if (rsent.fname[0] == -1)
|
||||
{
|
||||
rsenum->eof = 1;
|
||||
eof:
|
||||
ent.eof = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Not the end of file */
|
||||
err = process_bml3_file(&rsent, image, nullptr, &filesize);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (filesize == ((size_t) -1))
|
||||
{
|
||||
/* corrupt! */
|
||||
ent.filesize = 0;
|
||||
ent.corrupt = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent.filesize = filesize;
|
||||
ent.corrupt = 0;
|
||||
}
|
||||
ent.eof = 0;
|
||||
|
||||
get_dirent_fname(fname, &rsent);
|
||||
|
||||
snprintf(ent.filename, std::size(ent.filename), "%s", fname);
|
||||
snprintf(ent.attr, std::size(ent.attr), "%d %c", (int) rsent.ftype, (char) (rsent.asciiflag + 'B'));
|
||||
}
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t bml3_diskimage_freespace(imgtool::partition &partition, uint64_t *size)
|
||||
{
|
||||
floperr_t ferr;
|
||||
uint8_t i;
|
||||
size_t s = 0;
|
||||
uint8_t granule_count;
|
||||
uint8_t granule_map[MAX_GRANULEMAP_SIZE];
|
||||
imgtool::image &image(partition.image());
|
||||
bml3_diskinfo *info = bml3_get_diskinfo(image);
|
||||
|
||||
ferr = get_granule_map(image, granule_map, &granule_count);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
for (i = 0; i < granule_count; i++)
|
||||
{
|
||||
if (granule_map[i] == 0xff)
|
||||
s += (info->granule_sectors * info->sector_size);
|
||||
}
|
||||
*size = s;
|
||||
return (imgtoolerr_t)FLOPPY_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t delete_entry(imgtool::image &img, struct bml3_dirent *ent, int pos)
|
||||
{
|
||||
floperr_t ferr;
|
||||
unsigned char g, i;
|
||||
uint8_t granule_count;
|
||||
uint8_t granule_map[MAX_GRANULEMAP_SIZE];
|
||||
|
||||
/* Write a NUL in the filename, marking it deleted */
|
||||
ent->fname[0] = 0;
|
||||
ferr = put_bml3_dirent(img, pos, ent);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
ferr = get_granule_map(img, granule_map, &granule_count);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
/* Now free up the granules */
|
||||
g = ent->first_granule;
|
||||
while (g < granule_count)
|
||||
{
|
||||
i = granule_map[g];
|
||||
granule_map[g] = 0xff;
|
||||
g = i;
|
||||
}
|
||||
|
||||
ferr = put_granule_map(img, granule_map, granule_count);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t bml3_diskimage_readfile(imgtool::partition &partition, const char *fname, const char *fork, imgtool::stream &destf)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
struct bml3_dirent ent;
|
||||
size_t size;
|
||||
imgtool::image &img(partition.image());
|
||||
|
||||
err = lookup_bml3_file(img, fname, &ent, nullptr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = process_bml3_file(&ent, img, &destf, &size);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (size == (size_t) -1)
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
|
||||
return (imgtoolerr_t)0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t bml3_diskimage_writefile(imgtool::partition &partition, const char *fname, const char *fork, imgtool::stream &sourcef, util::option_resolution *writeoptions)
|
||||
{
|
||||
floperr_t ferr;
|
||||
imgtoolerr_t err;
|
||||
imgtool::image &img(partition.image());
|
||||
bml3_diskinfo *info = bml3_get_diskinfo(img);
|
||||
struct bml3_dirent ent, ent2;
|
||||
size_t i;
|
||||
uint64_t sz, read_sz;
|
||||
uint64_t freespace = 0;
|
||||
unsigned char *gptr;
|
||||
uint8_t granule_count;
|
||||
uint8_t granule_map[MAX_GRANULEMAP_SIZE];
|
||||
uint8_t eof_buf[MAX_SECTOR_SIZE];
|
||||
|
||||
// one-time setup of eof_buf
|
||||
memset(eof_buf, 0, sizeof(eof_buf));
|
||||
eof_buf[0] = 0x1A;
|
||||
|
||||
/* can we write to this image? */
|
||||
if (floppy_is_read_only(imgtool_floppy(img)))
|
||||
return IMGTOOLERR_READONLY;
|
||||
|
||||
err = bml3_diskimage_freespace(partition, &freespace);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* is there enough space? */
|
||||
sz = read_sz = sourcef.size();
|
||||
if (info->variant == 0) {
|
||||
// also need to write EOF
|
||||
sz++;
|
||||
}
|
||||
if (sz > freespace)
|
||||
return IMGTOOLERR_NOSPACE;
|
||||
|
||||
/* setup our directory entry */
|
||||
err = prepare_dirent(info->variant, &ent, fname);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ent.ftype = writeoptions->lookup_int(BML3_OPTIONS_FTYPE);
|
||||
ent.asciiflag = uint8_t(writeoptions->lookup_int(BML3_OPTIONS_ASCII)) - 1;
|
||||
gptr = &ent.first_granule;
|
||||
|
||||
ferr = get_granule_map(img, granule_map, &granule_count);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
unsigned char g = 0x00;
|
||||
uint32_t granule_bytes = info->granule_sectors * info->sector_size;
|
||||
|
||||
do
|
||||
{
|
||||
while (granule_map[g] != 0xff)
|
||||
{
|
||||
g++;
|
||||
if ((g >= granule_count) || (g == 0))
|
||||
return IMGTOOLERR_UNEXPECTED; /* We should have already verified that there is enough space */
|
||||
}
|
||||
*gptr = g;
|
||||
gptr = &granule_map[g];
|
||||
|
||||
|
||||
i = std::min(read_sz, uint64_t(granule_bytes));
|
||||
if (i > 0) {
|
||||
err = transfer_to_granule(img, g, i, sourcef);
|
||||
if (err)
|
||||
return err;
|
||||
read_sz -= i;
|
||||
sz -= i;
|
||||
}
|
||||
if (i < info->granule_sectors * info->sector_size && sz > 0) {
|
||||
// write EOF and trailing NULs in the final sector
|
||||
ferr = write_granule(img, g, i, (info->granule_sectors * info->sector_size - i - 1) % info->sector_size + 1, eof_buf);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
sz--;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Go to next granule */
|
||||
g++;
|
||||
}
|
||||
while(sz > 0);
|
||||
|
||||
/* Now that we are done with the file, we need to specify the final entry
|
||||
* in the file allocation table
|
||||
*/
|
||||
*gptr = 0xc0 + ((i + info->sector_size-1) / info->sector_size) - (info->variant == 0 ? 1 : 0);
|
||||
ent.lastsectorbytes = (i - 1) % info->sector_size + 1;
|
||||
|
||||
/* delete file if it already exists */
|
||||
err = bml3_diskimage_deletefile(partition, fname);
|
||||
if (err && err != IMGTOOLERR_FILENOTFOUND)
|
||||
return err;
|
||||
|
||||
/* Now we need to find an empty directory entry */
|
||||
i = -1;
|
||||
do
|
||||
{
|
||||
ferr = get_bml3_dirent(img, ++i, &ent2);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
}
|
||||
while(ent2.fname[0] != '\0' && ent2.fname[0] != -1);
|
||||
|
||||
ferr = put_bml3_dirent(img, i, &ent);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
/* write the granule map back out */
|
||||
ferr = put_granule_map(img, granule_map, granule_count);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t bml3_diskimage_deletefile(imgtool::partition &partition, const char *fname)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
imgtool::image &image(partition.image());
|
||||
int pos = 0;
|
||||
struct bml3_dirent ent;
|
||||
|
||||
err = lookup_bml3_file(image, fname, &ent, &pos);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return delete_entry(image, &ent, pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t bml3_diskimage_suggesttransfer(imgtool::partition &partition, const char *fname, imgtool_transfer_suggestion *suggestions, size_t suggestions_length)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
imgtool::image &image(partition.image());
|
||||
struct bml3_dirent ent;
|
||||
int pos;
|
||||
|
||||
if (fname)
|
||||
{
|
||||
err = lookup_bml3_file(image, fname, &ent, &pos);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (ent.asciiflag == 0xFF)
|
||||
{
|
||||
/* ASCII file */
|
||||
suggestions[0].viability = SUGGESTION_RECOMMENDED;
|
||||
suggestions[0].filter = filter_eoln_getinfo;
|
||||
suggestions[1].viability = SUGGESTION_POSSIBLE;
|
||||
suggestions[1].filter = NULL;
|
||||
}
|
||||
else if (ent.ftype == 0)
|
||||
{
|
||||
/* tokenized BASIC file */
|
||||
suggestions[0].viability = SUGGESTION_RECOMMENDED;
|
||||
suggestions[0].filter = NULL;
|
||||
suggestions[1].viability = SUGGESTION_POSSIBLE;
|
||||
suggestions[1].filter = filter_bml3bas_getinfo;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
suggestions[0].viability = SUGGESTION_RECOMMENDED;
|
||||
suggestions[0].filter = NULL;
|
||||
suggestions[1].viability = SUGGESTION_POSSIBLE;
|
||||
suggestions[1].filter = filter_eoln_getinfo;
|
||||
suggestions[2].viability = SUGGESTION_POSSIBLE;
|
||||
suggestions[2].filter = filter_bml3bas_getinfo;
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
Imgtool module declaration
|
||||
*********************************************************************/
|
||||
|
||||
OPTION_GUIDE_START( bml3_writefile_optionguide )
|
||||
OPTION_ENUM_START( BML3_OPTIONS_FTYPE, "ftype", "File type" )
|
||||
OPTION_ENUM( 0, "basic", "Basic" )
|
||||
OPTION_ENUM( 1, "data", "Data" )
|
||||
OPTION_ENUM( 2, "binary", "Binary" )
|
||||
OPTION_ENUM( 3, "assembler", "Assembler Source" )
|
||||
OPTION_ENUM_END
|
||||
OPTION_ENUM_START( BML3_OPTIONS_ASCII, "ascii", "Ascii flag" )
|
||||
OPTION_ENUM( 0, "ascii", "Ascii" )
|
||||
OPTION_ENUM( 1, "binary", "Binary" )
|
||||
OPTION_ENUM_END
|
||||
OPTION_GUIDE_END
|
||||
|
||||
|
||||
|
||||
void bml3_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
||||
case IMGTOOLINFO_INT_PREFER_UCASE: info->i = 1; break;
|
||||
case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(bml3_diskinfo); break;
|
||||
case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(struct bml3_direnum); break;
|
||||
|
||||
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
||||
case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "bml3"); break;
|
||||
case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Basic Master Level 3 format"); break;
|
||||
case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break;
|
||||
case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break;
|
||||
case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), "T0-[2]-3;M0-[1]"); break;
|
||||
|
||||
/* --- the following bits of info are returned as pointers to data or functions --- */
|
||||
case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break;
|
||||
case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = bml3_diskimage_open; break;
|
||||
case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = bml3_diskimage_nextenum; break;
|
||||
case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = bml3_diskimage_freespace; break;
|
||||
case IMGTOOLINFO_PTR_READ_FILE: info->read_file = bml3_diskimage_readfile; break;
|
||||
case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = bml3_diskimage_writefile; break;
|
||||
case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = bml3_diskimage_deletefile; break;
|
||||
case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = bml3_diskimage_suggesttransfer; break;
|
||||
case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE: info->writefile_optguide = &bml3_writefile_optionguide; break;
|
||||
case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_default; break;
|
||||
}
|
||||
}
|
@ -1,499 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Raphael Nabet
|
||||
/*
|
||||
Handlers for concept floppy images
|
||||
|
||||
Disk images are in MESS format.
|
||||
|
||||
Raphael Nabet, 2003
|
||||
*/
|
||||
|
||||
#include "imgtool.h"
|
||||
|
||||
#include "opresolv.h"
|
||||
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
struct UINT16xE
|
||||
{
|
||||
uint8_t bytes[2];
|
||||
};
|
||||
|
||||
/*
|
||||
get_UINT16xE
|
||||
|
||||
Read a 16-bit word, whether it is little-endian or big-endian
|
||||
|
||||
little_endian (I): non-zero if word is little-endian, zero if word is
|
||||
big-endian
|
||||
word (I): pointer to word to read
|
||||
|
||||
Returns value of word in native format
|
||||
*/
|
||||
static inline uint16_t get_UINT16xE(int little_endian, UINT16xE word)
|
||||
{
|
||||
return little_endian ? (word.bytes[0] | (word.bytes[1] << 8)) : ((word.bytes[0] << 8) | word.bytes[1]);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
set_UINT16xE
|
||||
|
||||
Write a 16-bit word, whether it is little-endian or big-endian
|
||||
|
||||
little_endian (I): non-zero if word is little-endian, zero if word is
|
||||
big-endian
|
||||
word (O): pointer to word to write
|
||||
data (I): value to write in word, in native format
|
||||
*/
|
||||
static inline void set_UINT16xE(int little_endian, UINT16xE *word, uint16_t data)
|
||||
{
|
||||
if (little_endian)
|
||||
{
|
||||
word->bytes[0] = data & 0xff;
|
||||
word->bytes[1] = (data >> 8) & 0xff;
|
||||
}
|
||||
else
|
||||
{
|
||||
word->bytes[0] = (data >> 8) & 0xff;
|
||||
word->bytes[1] = data & 0xff;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Disk structure:
|
||||
|
||||
Track 0 Sector 0 & 1: bootstrap loader
|
||||
Track 0 Sector 2 through 5: disk directory
|
||||
Remaining sectors are used for data.
|
||||
*/
|
||||
|
||||
/*
|
||||
device directory record (Disk sector 2-5)
|
||||
*/
|
||||
|
||||
struct concept_vol_hdr_entry
|
||||
{
|
||||
UINT16xE first_block;
|
||||
UINT16xE next_block;
|
||||
UINT16xE ftype;
|
||||
|
||||
unsigned char volname[8];
|
||||
UINT16xE last_block;
|
||||
UINT16xE num_files;
|
||||
UINT16xE last_boot;
|
||||
UINT16xE last_access;
|
||||
char mem_flipped;
|
||||
char disk_flipped;
|
||||
UINT16xE unused;
|
||||
};
|
||||
|
||||
struct concept_file_dir_entry
|
||||
{
|
||||
UINT16xE first_block;
|
||||
UINT16xE next_block;
|
||||
UINT16xE ftype;
|
||||
|
||||
unsigned char filename[16];
|
||||
UINT16xE last_byte;
|
||||
UINT16xE last_access;
|
||||
};
|
||||
|
||||
struct concept_dev_dir
|
||||
{
|
||||
concept_vol_hdr_entry vol_hdr;
|
||||
concept_file_dir_entry file_dir[77];
|
||||
char unused[20];
|
||||
};
|
||||
|
||||
/*
|
||||
concept disk image descriptor
|
||||
*/
|
||||
struct concept_image
|
||||
{
|
||||
imgtool::stream *file_handle; /* imgtool file handle */
|
||||
concept_dev_dir dev_dir; /* cached copy of device directory */
|
||||
};
|
||||
|
||||
/*
|
||||
concept catalog iterator, used when imgtool reads the catalog
|
||||
*/
|
||||
struct concept_iterator
|
||||
{
|
||||
concept_image *image;
|
||||
int index; /* current index */
|
||||
};
|
||||
|
||||
|
||||
static imgtoolerr_t concept_image_init(imgtool::image &img, imgtool::stream::ptr &&stream);
|
||||
static void concept_image_exit(imgtool::image &img);
|
||||
static void concept_image_info(imgtool::image &img, std::ostream &stream);
|
||||
static imgtoolerr_t concept_image_beginenum(imgtool::directory &enumeration, const char *path);
|
||||
static imgtoolerr_t concept_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent);
|
||||
static void concept_image_closeenum(imgtool::directory &enumeration);
|
||||
static imgtoolerr_t concept_image_freespace(imgtool::partition &partition, uint64_t *size);
|
||||
static imgtoolerr_t concept_image_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf);
|
||||
#if 0
|
||||
static imgtoolerr_t concept_image_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream *sourcef, util::option_resolution *writeoptions);
|
||||
static imgtoolerr_t concept_image_deletefile(imgtool::partition &partition, const char *filename);
|
||||
static imgtoolerr_t concept_image_create(const imgtool_module *mod, imgtool::stream *f, util::option_resolution *createoptions);
|
||||
#endif
|
||||
|
||||
void concept_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
||||
case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(concept_image); break;
|
||||
case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(concept_iterator); break;
|
||||
|
||||
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
||||
case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "concept"); break;
|
||||
case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Concept floppy disk image"); break;
|
||||
case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "img"); break;
|
||||
case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break;
|
||||
|
||||
/* --- the following bits of info are returned as pointers to data or functions --- */
|
||||
case IMGTOOLINFO_PTR_OPEN: info->open = concept_image_init; break;
|
||||
case IMGTOOLINFO_PTR_CLOSE: info->close = concept_image_exit; break;
|
||||
case IMGTOOLINFO_PTR_INFO: info->info = concept_image_info; break;
|
||||
case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = concept_image_beginenum; break;
|
||||
case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = concept_image_nextenum; break;
|
||||
case IMGTOOLINFO_PTR_CLOSE_ENUM: info->close_enum = concept_image_closeenum; break;
|
||||
case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = concept_image_freespace; break;
|
||||
case IMGTOOLINFO_PTR_READ_FILE: info->read_file = concept_image_readfile; break;
|
||||
case IMGTOOLINFO_PTR_WRITE_FILE: /* info->write_file = concept_image_writefile */; break;
|
||||
case IMGTOOLINFO_PTR_DELETE_FILE: /* info->delete_file = concept_image_deletefile */; break;
|
||||
case IMGTOOLINFO_PTR_CREATE: /* info->create = concept_image_create */; break;
|
||||
}
|
||||
}
|
||||
|
||||
static concept_image *get_concept_image(imgtool::image &image)
|
||||
{
|
||||
return (concept_image *)image.extra_bytes();
|
||||
}
|
||||
|
||||
/*
|
||||
read_physical_record
|
||||
|
||||
Read one 512-byte physical record from a disk image
|
||||
|
||||
file_handle: imgtool file handle
|
||||
secnum: physical record address
|
||||
dest: pointer to destination buffer
|
||||
|
||||
Return non-zero on error
|
||||
*/
|
||||
static int read_physical_record(imgtool::stream &file_handle, int secnum, void *dest)
|
||||
{
|
||||
int reply;
|
||||
|
||||
/* seek to sector */
|
||||
reply = file_handle.seek(secnum*512, SEEK_SET);
|
||||
if (reply)
|
||||
return 1;
|
||||
/* read it */
|
||||
reply = file_handle.read(dest, 512);
|
||||
if (reply != 512)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef UNUSED_FUNCTION
|
||||
/*
|
||||
write_physical_record
|
||||
|
||||
Write one 512-byte physical record to a disk image
|
||||
|
||||
file_handle: imgtool file handle
|
||||
secnum: logical sector address
|
||||
src: pointer to source buffer
|
||||
|
||||
Return non-zero on error
|
||||
*/
|
||||
static int write_physical_record(imgtool::stream *file_handle, int secnum, const void *src)
|
||||
{
|
||||
int reply;
|
||||
|
||||
/* seek to sector */
|
||||
reply = file_handle->seek(secnum*512, SEEK_SET);
|
||||
if (reply)
|
||||
return 1;
|
||||
/* read it */
|
||||
reply = file_handle->write(src, 512);
|
||||
if (reply != 512)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Search for a file name on a concept_image
|
||||
|
||||
image (I): image reference
|
||||
filename (I): name of the file to search
|
||||
entry_index (O): index of file in disk catalog
|
||||
|
||||
Return non-zero on error
|
||||
*/
|
||||
static int get_catalog_entry(concept_image *image, const unsigned char *filename, int *entry_index)
|
||||
{
|
||||
int filename_len = filename[0];
|
||||
int i;
|
||||
|
||||
if (filename_len > 15)
|
||||
/* file name is bad */
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < 77; i++)
|
||||
{
|
||||
if (!memcmp(filename, image->dev_dir.file_dir[i].filename, filename_len+1))
|
||||
{
|
||||
/* file found */
|
||||
*entry_index = i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* file not found */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
Open a file as a concept_image.
|
||||
*/
|
||||
static imgtoolerr_t concept_image_init(imgtool::image &img, imgtool::stream::ptr &&stream)
|
||||
{
|
||||
concept_image *image = get_concept_image(img);
|
||||
int reply;
|
||||
int i;
|
||||
unsigned totphysrecs;
|
||||
|
||||
/* read device directory */
|
||||
for (i=0; i<4; i++)
|
||||
{
|
||||
reply = read_physical_record(*stream, i+2, ((char *) & image->dev_dir)+i*512);
|
||||
if (reply)
|
||||
return IMGTOOLERR_READERROR;
|
||||
}
|
||||
|
||||
/* do primitive checks */
|
||||
totphysrecs = get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.last_block)
|
||||
- get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.first_block);
|
||||
|
||||
if ((get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.first_block) != 0)
|
||||
|| (get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.next_block) != 6)
|
||||
|| (totphysrecs < 6) /*|| (f->size() != totphysrecs*512)*/
|
||||
|| (image->dev_dir.vol_hdr.volname[0] > 7))
|
||||
{
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
}
|
||||
|
||||
image->file_handle = stream.release();
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
close a concept_image
|
||||
*/
|
||||
static void concept_image_exit(imgtool::image &img)
|
||||
{
|
||||
/*concept_image *image = get_concept_image(img);*/
|
||||
}
|
||||
|
||||
/*
|
||||
get basic information on a concept_image
|
||||
|
||||
Currently returns the volume name
|
||||
*/
|
||||
static void concept_image_info(imgtool::image &img, std::ostream &stream)
|
||||
{
|
||||
concept_image *image = get_concept_image(img);
|
||||
char vol_name[8];
|
||||
|
||||
memcpy(vol_name, image->dev_dir.vol_hdr.volname + 1, image->dev_dir.vol_hdr.volname[0]);
|
||||
vol_name[image->dev_dir.vol_hdr.volname[0]] = 0;
|
||||
|
||||
stream << vol_name;
|
||||
}
|
||||
|
||||
/*
|
||||
Open the disk catalog for enumeration
|
||||
*/
|
||||
static imgtoolerr_t concept_image_beginenum(imgtool::directory &enumeration, const char *path)
|
||||
{
|
||||
concept_iterator *iter;
|
||||
|
||||
iter = (concept_iterator *) enumeration.extra_bytes();
|
||||
iter->image = (concept_image *) enumeration.image().extra_bytes();
|
||||
iter->index = 0;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
Enumerate disk catalog next entry
|
||||
*/
|
||||
static imgtoolerr_t concept_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent)
|
||||
{
|
||||
concept_iterator *iter = (concept_iterator *) enumeration.extra_bytes();
|
||||
|
||||
|
||||
ent.corrupt = 0;
|
||||
ent.eof = 0;
|
||||
|
||||
if ((iter->image->dev_dir.file_dir[iter->index].filename[0] == 0) || (iter->index > 77))
|
||||
{
|
||||
ent.eof = 1;
|
||||
}
|
||||
else if (iter->image->dev_dir.file_dir[iter->index].filename[0] > 15)
|
||||
{
|
||||
ent.corrupt = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int len = iter->image->dev_dir.file_dir[iter->index].filename[0];
|
||||
const char *type;
|
||||
|
||||
if (len > std::size(ent.filename))
|
||||
len = std::size(ent.filename);
|
||||
memcpy(ent.filename, iter->image->dev_dir.file_dir[iter->index].filename + 1, len);
|
||||
ent.filename[len] = 0;
|
||||
|
||||
/* parse flags */
|
||||
switch (get_UINT16xE(iter->image->dev_dir.vol_hdr.disk_flipped, iter->image->dev_dir.file_dir[iter->index].ftype) & 0xf)
|
||||
{
|
||||
case 0:
|
||||
case 8:
|
||||
type = "DIRHDR";
|
||||
break;
|
||||
case 2:
|
||||
type = "CODE";
|
||||
break;
|
||||
case 3:
|
||||
type = "TEXT";
|
||||
break;
|
||||
case 5:
|
||||
type = "DATA";
|
||||
break;
|
||||
default:
|
||||
type = "???";
|
||||
break;
|
||||
}
|
||||
snprintf(ent.attr, std::size(ent.attr), "%s", type);
|
||||
|
||||
/* len in physrecs */
|
||||
ent.filesize = get_UINT16xE(iter->image->dev_dir.vol_hdr.disk_flipped, iter->image->dev_dir.file_dir[iter->index].next_block)
|
||||
- get_UINT16xE(iter->image->dev_dir.vol_hdr.disk_flipped, iter->image->dev_dir.file_dir[iter->index].first_block);
|
||||
|
||||
iter->index++;
|
||||
}
|
||||
|
||||
return (imgtoolerr_t)0;
|
||||
}
|
||||
|
||||
/*
|
||||
Free enumerator
|
||||
*/
|
||||
static void concept_image_closeenum(imgtool::directory &enumeration)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
Compute free space on disk image
|
||||
*/
|
||||
static imgtoolerr_t concept_image_freespace(imgtool::partition &partition, uint64_t *size)
|
||||
{
|
||||
imgtool::image &img(partition.image());
|
||||
concept_image *image = get_concept_image(img);
|
||||
int free_blocks;
|
||||
int i;
|
||||
|
||||
/* first get number of data blocks */
|
||||
free_blocks = get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.last_block)
|
||||
- get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.next_block);
|
||||
|
||||
/* next substract length of each file */
|
||||
for (i=0; (image->dev_dir.file_dir[i].filename[0] != 0) && (i <= 77); i++)
|
||||
{
|
||||
free_blocks -= get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.file_dir[i].next_block)
|
||||
- get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.file_dir[i].first_block);
|
||||
}
|
||||
|
||||
*size = free_blocks;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
Extract a file from a concept_image.
|
||||
*/
|
||||
static imgtoolerr_t concept_image_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)
|
||||
{
|
||||
imgtool::image &img(partition.image());
|
||||
concept_image *image = get_concept_image(img);
|
||||
size_t filename_len = strlen(filename);
|
||||
unsigned char concept_fname[16];
|
||||
int catalog_index;
|
||||
int i;
|
||||
uint8_t buf[512];
|
||||
|
||||
if (filename_len > 15)
|
||||
return IMGTOOLERR_BADFILENAME;
|
||||
|
||||
concept_fname[0] = filename_len;
|
||||
memcpy(concept_fname+1, filename, filename_len);
|
||||
|
||||
if (get_catalog_entry(image, concept_fname, &catalog_index))
|
||||
return IMGTOOLERR_FILENOTFOUND;
|
||||
|
||||
for (i = get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.file_dir[catalog_index].first_block);
|
||||
i < get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.file_dir[catalog_index].next_block);
|
||||
i++)
|
||||
{
|
||||
if (read_physical_record(*image->file_handle, i, buf))
|
||||
return IMGTOOLERR_READERROR;
|
||||
|
||||
if (destf.write(buf, 512) != 512)
|
||||
return IMGTOOLERR_WRITEERROR;
|
||||
}
|
||||
|
||||
return (imgtoolerr_t)0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
Add a file to a concept_image.
|
||||
*/
|
||||
static imgtoolerr_t concept_image_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream *sourcef, util::option_resolution *writeoptions)
|
||||
{
|
||||
/* ... */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Delete a file from a concept_image.
|
||||
*/
|
||||
static imgtoolerr_t concept_image_deletefile(imgtool::partition &partition, const char *filename)
|
||||
{
|
||||
/* ... */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Create a blank concept_image.
|
||||
*/
|
||||
static imgtoolerr_t concept_image_create(const imgtool_module *mod, imgtool::stream *f, util::option_resolution *createoptions)
|
||||
{
|
||||
/* ... */
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
@ -1,583 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Tim Schuerewegen
|
||||
/*
|
||||
|
||||
Cybiko Classic File System
|
||||
|
||||
(c) 2007 Tim Schuerewegen
|
||||
|
||||
*/
|
||||
|
||||
#include "imgtool.h"
|
||||
|
||||
#include "opresolv.h"
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
struct cybiko_file_system
|
||||
{
|
||||
imgtool::stream *stream;
|
||||
uint32_t page_count, page_size, block_count_boot, block_count_file;
|
||||
uint16_t write_count;
|
||||
};
|
||||
|
||||
struct cybiko_iter
|
||||
{
|
||||
uint16_t block;
|
||||
};
|
||||
|
||||
struct cfs_file
|
||||
{
|
||||
char name[64]; // name of the file
|
||||
uint32_t date; // date/time of the file (seconds since 1900/01/01)
|
||||
uint32_t size; // size of the file
|
||||
uint32_t blocks; // number of blocks occupied by the file
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
BLOCK_TYPE_INVALID,
|
||||
BLOCK_TYPE_BOOT,
|
||||
BLOCK_TYPE_FILE
|
||||
};
|
||||
|
||||
#define MAX_PAGE_SIZE (264 * 2)
|
||||
|
||||
#define INVALID_FILE_ID 0xFFFF
|
||||
|
||||
enum
|
||||
{
|
||||
FLASH_TYPE_INVALID,
|
||||
FLASH_TYPE_AT45DB041,
|
||||
FLASH_TYPE_AT45DB081,
|
||||
FLASH_TYPE_AT45DB161
|
||||
};
|
||||
|
||||
#define BLOCK_USED(x) (x[0] & 0x80)
|
||||
#define BLOCK_FILE_ID(x) buffer_read_16_be( x + 2)
|
||||
#define BLOCK_PART_ID(x) buffer_read_16_be( x + 4)
|
||||
#define BLOCK_FILENAME(x) (char*)(x + 7)
|
||||
|
||||
#define FILE_HEADER_SIZE 0x48
|
||||
|
||||
static cybiko_file_system *get_cfs(imgtool::image &image)
|
||||
{
|
||||
return (cybiko_file_system*)image.extra_bytes();
|
||||
}
|
||||
|
||||
// 2208988800 is the number of seconds between 1900/01/01 and 1970/01/01
|
||||
typedef util::arbitrary_clock<std::uint32_t, 1900, 1, 1, 0, 0, 0, std::ratio<1, 1> > cybiko_clock;
|
||||
|
||||
imgtool::datetime cybiko_time_crack(uint32_t cfs_time)
|
||||
{
|
||||
cybiko_clock::duration d(cfs_time);
|
||||
std::chrono::time_point<cybiko_clock> tp(d);
|
||||
return imgtool::datetime(imgtool::datetime::datetime_type::LOCAL, tp);
|
||||
}
|
||||
|
||||
uint32_t cybiko_time_setup(const imgtool::datetime &t)
|
||||
{
|
||||
auto cybiko_time_point = cybiko_clock::from_arbitrary_time_point(t.time_point());
|
||||
return cybiko_time_point.time_since_epoch().count();
|
||||
}
|
||||
|
||||
static uint32_t buffer_read_32_be( uint8_t *buffer)
|
||||
{
|
||||
return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3] << 0);
|
||||
}
|
||||
|
||||
static uint16_t buffer_read_16_be( uint8_t *buffer)
|
||||
{
|
||||
return (buffer[0] << 8) | (buffer[1] << 0);
|
||||
}
|
||||
|
||||
static void buffer_write_32_be( uint8_t *buffer, uint32_t data)
|
||||
{
|
||||
buffer[0] = (data >> 24) & 0xFF;
|
||||
buffer[1] = (data >> 16) & 0xFF;
|
||||
buffer[2] = (data >> 8) & 0xFF;
|
||||
buffer[3] = (data >> 0) & 0xFF;
|
||||
}
|
||||
|
||||
static void buffer_write_16_be( uint8_t *buffer, uint16_t data)
|
||||
{
|
||||
buffer[0] = (data >> 8) & 0xFF;
|
||||
buffer[1] = (data >> 0) & 0xFF;
|
||||
}
|
||||
|
||||
// page = crc1 (4) + wcnt (2) + crc2 (2) + data (x) + unk (2)
|
||||
|
||||
static uint32_t page_buffer_calc_checksum_1( uint8_t *buffer, uint32_t size, int block_type)
|
||||
{
|
||||
return crc32( 0, buffer + 8, (block_type == BLOCK_TYPE_BOOT) ? 250 : size - 10);
|
||||
}
|
||||
|
||||
static uint16_t page_buffer_calc_checksum_2( uint8_t *buffer)
|
||||
{
|
||||
uint16_t val = 0xAF17;
|
||||
val ^= buffer_read_16_be( buffer + 0);
|
||||
val ^= buffer_read_16_be( buffer + 2);
|
||||
val ^= buffer_read_16_be( buffer + 4);
|
||||
return swapendian_int16(val);
|
||||
}
|
||||
|
||||
static int page_buffer_verify( uint8_t *buffer, uint32_t size, int block_type)
|
||||
{
|
||||
uint32_t checksum_page, checksum_calc;
|
||||
// checksum 1
|
||||
checksum_calc = page_buffer_calc_checksum_1( buffer, size, block_type);
|
||||
checksum_page = buffer_read_32_be( buffer + 0);
|
||||
if (checksum_calc != checksum_page) return false;
|
||||
// checksum 2
|
||||
checksum_calc = page_buffer_calc_checksum_2( buffer);
|
||||
checksum_page = buffer_read_16_be( buffer + 6);
|
||||
if (checksum_calc != checksum_page) return false;
|
||||
// ok
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cfs_block_to_page( cybiko_file_system *cfs, int block_type, uint32_t block, uint32_t *page)
|
||||
{
|
||||
switch (block_type)
|
||||
{
|
||||
case BLOCK_TYPE_BOOT : if (page) *page = block; return true;
|
||||
case BLOCK_TYPE_FILE : if (page) *page = block + cfs->block_count_boot; return true;
|
||||
default : return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int cfs_page_to_block( cybiko_file_system *cfs, uint32_t page, int *block_type, uint32_t *block)
|
||||
{
|
||||
uint32_t tmp = page;
|
||||
// boot block
|
||||
if (tmp < cfs->block_count_boot)
|
||||
{
|
||||
if (block_type) *block_type = BLOCK_TYPE_BOOT;
|
||||
if (block) *block = tmp;
|
||||
return true;
|
||||
}
|
||||
tmp -= cfs->block_count_boot;
|
||||
// file block
|
||||
if (tmp < cfs->block_count_file)
|
||||
{
|
||||
if (block_type) *block_type = BLOCK_TYPE_FILE;
|
||||
if (block) *block = tmp;
|
||||
return true;
|
||||
}
|
||||
tmp -= cfs->block_count_file;
|
||||
// error
|
||||
return false;
|
||||
}
|
||||
|
||||
static int cfs_page_read( cybiko_file_system *cfs, uint8_t *buffer, uint32_t page)
|
||||
{
|
||||
if (page >= cfs->page_count) return false;
|
||||
cfs->stream->seek(page * cfs->page_size, SEEK_SET);
|
||||
cfs->stream->read(buffer, cfs->page_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cfs_page_write( cybiko_file_system *cfs, uint8_t *buffer, uint32_t page)
|
||||
{
|
||||
if (page >= cfs->page_count) return false;
|
||||
cfs->stream->seek(page * cfs->page_size, SEEK_SET);
|
||||
cfs->stream->write(buffer, cfs->page_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cfs_block_read( cybiko_file_system *cfs, uint8_t *buffer, int block_type, uint32_t block)
|
||||
{
|
||||
uint8_t buffer_page[MAX_PAGE_SIZE];
|
||||
uint32_t page;
|
||||
if (!cfs_block_to_page( cfs, block_type, block, &page)) return false;
|
||||
if (!cfs_page_read( cfs, buffer_page, page)) return false;
|
||||
memcpy( buffer, buffer_page + 8, cfs->page_size - 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cfs_block_write( cybiko_file_system *cfs, uint8_t *buffer, int block_type, uint32_t block)
|
||||
{
|
||||
uint8_t buffer_page[MAX_PAGE_SIZE];
|
||||
uint32_t page;
|
||||
memcpy( buffer_page + 8, buffer, cfs->page_size - 10);
|
||||
buffer_write_32_be( buffer_page + 0, page_buffer_calc_checksum_1( buffer_page, cfs->page_size, block_type));
|
||||
buffer_write_16_be( buffer_page + 4, cfs->write_count++);
|
||||
buffer_write_16_be( buffer_page + 6, page_buffer_calc_checksum_2( buffer_page));
|
||||
buffer_write_16_be( buffer_page + cfs->page_size - 2, 0xFFFF);
|
||||
if (!cfs_block_to_page( cfs, block_type, block, &page)) return false;
|
||||
if (!cfs_page_write( cfs, buffer_page, page)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cfs_file_delete( cybiko_file_system *cfs, uint16_t file_id)
|
||||
{
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
int i;
|
||||
for (i=0;i<cfs->block_count_file;i++)
|
||||
{
|
||||
if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return false;
|
||||
if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id))
|
||||
{
|
||||
buffer[0] &= ~0x80;
|
||||
if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cfs_file_info( cybiko_file_system *cfs, uint16_t file_id, cfs_file *file)
|
||||
{
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
int i;
|
||||
file->blocks = file->size = 0;
|
||||
for (i=0;i<cfs->block_count_file;i++)
|
||||
{
|
||||
if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return false;
|
||||
if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id))
|
||||
{
|
||||
if (BLOCK_PART_ID(buffer) == 0)
|
||||
{
|
||||
strcpy( file->name, BLOCK_FILENAME(buffer));
|
||||
file->date = buffer_read_32_be( buffer + 6 + FILE_HEADER_SIZE - 4);
|
||||
}
|
||||
file->size += buffer[1];
|
||||
file->blocks++;
|
||||
}
|
||||
}
|
||||
return (file->blocks > 0) ? true : false;
|
||||
}
|
||||
|
||||
static int cfs_file_find( cybiko_file_system *cfs, const char *filename, uint16_t *file_id)
|
||||
{
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
int i;
|
||||
for (i=0;i<cfs->block_count_file;i++)
|
||||
{
|
||||
if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return false;
|
||||
if (BLOCK_USED(buffer) && (strncmp( filename, BLOCK_FILENAME(buffer), 40) == 0))
|
||||
{
|
||||
*file_id = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int cfs_verify(cybiko_file_system &cfs)
|
||||
{
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
int i, block_type;
|
||||
for (i = 0; i < cfs.page_count; i++)
|
||||
{
|
||||
if (!cfs_page_read(&cfs, buffer, i)) return false;
|
||||
if (!cfs_page_to_block(&cfs, i, &block_type, NULL)) return false;
|
||||
if (!page_buffer_verify(buffer, cfs.page_size, block_type)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cfs_init(cybiko_file_system &cfs, imgtool::stream::ptr &&stream, int flash_type)
|
||||
{
|
||||
cfs.stream = stream.release();
|
||||
switch (flash_type)
|
||||
{
|
||||
case FLASH_TYPE_AT45DB041 : cfs.page_count = 2048; cfs.page_size = 264; break;
|
||||
case FLASH_TYPE_AT45DB081 : cfs.page_count = 4096; cfs.page_size = 264; break;
|
||||
case FLASH_TYPE_AT45DB161 : cfs.page_count = 4096; cfs.page_size = 528; break;
|
||||
default : return false;
|
||||
}
|
||||
cfs.block_count_boot = 5;
|
||||
cfs.block_count_file = cfs.page_count - cfs.block_count_boot;
|
||||
cfs.write_count = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cfs_format( cybiko_file_system *cfs)
|
||||
{
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
int i;
|
||||
// boot blocks
|
||||
memset( buffer, 0xFF, sizeof( buffer));
|
||||
for (i=0;i<cfs->block_count_boot;i++)
|
||||
{
|
||||
if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_BOOT, i)) return false;
|
||||
}
|
||||
// file blocks
|
||||
memset( buffer, 0xFF, sizeof( buffer));
|
||||
buffer[0] &= ~0x80;
|
||||
for (i=0;i<cfs->block_count_file;i++)
|
||||
{
|
||||
if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return false;
|
||||
}
|
||||
// ok
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint16_t cfs_calc_free_blocks( cybiko_file_system *cfs)
|
||||
{
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
int i;
|
||||
uint16_t blocks = 0;
|
||||
for (i=0;i<cfs->block_count_file;i++)
|
||||
{
|
||||
if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return 0;
|
||||
if (!BLOCK_USED(buffer)) blocks++;
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
static uint32_t cfs_calc_free_space( cybiko_file_system *cfs, uint16_t blocks)
|
||||
{
|
||||
uint32_t free_space;
|
||||
free_space = blocks * (cfs->page_size - 0x10);
|
||||
if (free_space > 0) free_space -= FILE_HEADER_SIZE;
|
||||
return free_space;
|
||||
}
|
||||
|
||||
static int flash_size_to_flash_type( size_t size)
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case 0x084000 : return FLASH_TYPE_AT45DB041;
|
||||
case 0x108000 : return FLASH_TYPE_AT45DB081;
|
||||
case 0x210000 : return FLASH_TYPE_AT45DB161;
|
||||
default : return FLASH_TYPE_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
static int flash_option_to_flash_type( int option)
|
||||
{
|
||||
switch (option)
|
||||
{
|
||||
case 0 : return FLASH_TYPE_AT45DB041;
|
||||
case 1 : return FLASH_TYPE_AT45DB081;
|
||||
case 2 : return FLASH_TYPE_AT45DB161;
|
||||
default : return FLASH_TYPE_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_open(imgtool::image &image, imgtool::stream::ptr &&stream)
|
||||
{
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
int flash_type;
|
||||
// init
|
||||
flash_type = flash_size_to_flash_type(stream->size());
|
||||
if (!cfs_init(*cfs, std::move(stream), flash_type)) return IMGTOOLERR_CORRUPTIMAGE;
|
||||
// verify
|
||||
if (!cfs_verify(*cfs)) return IMGTOOLERR_CORRUPTIMAGE;
|
||||
// ok
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static void cybiko_image_close(imgtool::image &image)
|
||||
{
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
delete cfs->stream;
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts)
|
||||
{
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
int flash_type;
|
||||
// init
|
||||
flash_type = flash_option_to_flash_type(opts->lookup_int('F'));
|
||||
if (!cfs_init(*cfs, std::move(stream), flash_type)) return IMGTOOLERR_CORRUPTIMAGE;
|
||||
// format
|
||||
if (!cfs_format(cfs)) return IMGTOOLERR_CORRUPTIMAGE;
|
||||
// ok
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_begin_enum(imgtool::directory &enumeration, const char *path)
|
||||
{
|
||||
cybiko_iter *iter = (cybiko_iter*)enumeration.extra_bytes();
|
||||
iter->block = 0;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_next_enum(imgtool::directory &enumeration, imgtool_dirent &ent)
|
||||
{
|
||||
imgtool::image &image(enumeration.image());
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
cybiko_iter *iter = (cybiko_iter*)enumeration.extra_bytes();
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
uint16_t file_id = INVALID_FILE_ID;
|
||||
cfs_file file;
|
||||
// find next file
|
||||
while (iter->block < cfs->block_count_file)
|
||||
{
|
||||
if (!cfs_block_read(cfs, buffer, BLOCK_TYPE_FILE, iter->block++)) return IMGTOOLERR_READERROR;
|
||||
if (BLOCK_USED(buffer) && (BLOCK_PART_ID(buffer) == 0))
|
||||
{
|
||||
file_id = BLOCK_FILE_ID(buffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// get file information
|
||||
if ((file_id != INVALID_FILE_ID) && cfs_file_info(cfs, file_id, &file))
|
||||
{
|
||||
strcpy(ent.filename, file.name);
|
||||
ent.filesize = file.size;
|
||||
ent.lastmodified_time = cybiko_time_crack(file.date);
|
||||
ent.filesize = file.size;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent.eof = 1;
|
||||
}
|
||||
// ok
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_free_space(imgtool::partition &partition, uint64_t *size)
|
||||
{
|
||||
imgtool::image &image(partition.image());
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
if (size) *size = cfs_calc_free_space( cfs, cfs_calc_free_blocks( cfs));
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_read_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)
|
||||
{
|
||||
imgtool::image &image(partition.image());
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
uint16_t file_id, part_id = 0, old_part_id;
|
||||
int i;
|
||||
// find file
|
||||
if (!cfs_file_find( cfs, filename, &file_id)) return IMGTOOLERR_FILENOTFOUND;
|
||||
// read file
|
||||
do
|
||||
{
|
||||
old_part_id = part_id;
|
||||
for (i=0;i<cfs->block_count_file;i++)
|
||||
{
|
||||
if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_READERROR;
|
||||
if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id) && (BLOCK_PART_ID(buffer) == part_id))
|
||||
{
|
||||
destf.write(buffer + 6 + ((part_id == 0) ? FILE_HEADER_SIZE : 0), buffer[1]);
|
||||
part_id++;
|
||||
}
|
||||
}
|
||||
} while (old_part_id != part_id);
|
||||
// ok
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_write_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts)
|
||||
{
|
||||
imgtool::image &image(partition.image());
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
uint16_t file_id, part_id = 0, free_blocks;
|
||||
uint64_t bytes_left;
|
||||
cfs_file file;
|
||||
int i;
|
||||
// find file
|
||||
if (!cfs_file_find(cfs, filename, &file_id)) file_id = INVALID_FILE_ID;
|
||||
// check free space
|
||||
free_blocks = cfs_calc_free_blocks( cfs);
|
||||
if (file_id != INVALID_FILE_ID)
|
||||
{
|
||||
if (!cfs_file_info(cfs, file_id, &file)) return IMGTOOLERR_UNEXPECTED;
|
||||
free_blocks += file.blocks;
|
||||
}
|
||||
if (cfs_calc_free_space(cfs, free_blocks) < sourcef.size()) return IMGTOOLERR_NOSPACE;
|
||||
// delete file
|
||||
if (file_id != INVALID_FILE_ID)
|
||||
{
|
||||
if (!cfs_file_delete( cfs, file_id)) return IMGTOOLERR_UNEXPECTED;
|
||||
}
|
||||
// create/write destination file
|
||||
bytes_left = sourcef.size();
|
||||
i = 0;
|
||||
while ((bytes_left > 0) && (i < cfs->block_count_file))
|
||||
{
|
||||
if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_READERROR;
|
||||
if (!BLOCK_USED(buffer))
|
||||
{
|
||||
if (part_id == 0) file_id = i;
|
||||
memset( buffer, 0xFF, cfs->page_size - 0x10);
|
||||
buffer[0] = 0x80;
|
||||
buffer[1] = cfs->page_size - 0x10 - ((part_id == 0) ? FILE_HEADER_SIZE : 0);
|
||||
if (bytes_left < buffer[1]) buffer[1] = bytes_left;
|
||||
buffer_write_16_be( buffer + 2, file_id);
|
||||
buffer_write_16_be( buffer + 4, part_id);
|
||||
if (part_id == 0)
|
||||
{
|
||||
buffer[6] = 0;
|
||||
strcpy(BLOCK_FILENAME(buffer), filename);
|
||||
buffer_write_32_be(buffer + 6 + FILE_HEADER_SIZE - 4, cybiko_time_setup(imgtool::datetime::now(imgtool::datetime::datetime_type::LOCAL)));
|
||||
sourcef.read(buffer + 6 + FILE_HEADER_SIZE, buffer[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
sourcef.read(buffer + 6, buffer[1]);
|
||||
}
|
||||
if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_WRITEERROR;
|
||||
bytes_left -= buffer[1];
|
||||
part_id++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// ok
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_delete_file(imgtool::partition &partition, const char *filename)
|
||||
{
|
||||
imgtool::image &image(partition.image());
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
uint16_t file_id;
|
||||
// find file
|
||||
if (!cfs_file_find( cfs, filename, &file_id)) return IMGTOOLERR_FILENOTFOUND;
|
||||
// delete file
|
||||
if (!cfs_file_delete( cfs, file_id)) return IMGTOOLERR_UNEXPECTED;
|
||||
// ok
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
OPTION_GUIDE_START( cybiko_image_createimage_optguide )
|
||||
OPTION_ENUM_START( 'F', "flash", "Flash Type" )
|
||||
OPTION_ENUM( 0, "AT45DB041", "AT45DB041 (528 KByte)" )
|
||||
OPTION_ENUM( 1, "AT45DB081", "AT45DB081 (1056 KByte)" )
|
||||
OPTION_ENUM( 2, "AT45DB161", "AT45DB161 (2112 KByte)" )
|
||||
OPTION_ENUM_END
|
||||
OPTION_GUIDE_END
|
||||
|
||||
//OPTION_GUIDE_START( cybiko_image_writefile_optguide )
|
||||
// OPTION_INT( 'B', "boot", "Boot Flag" )
|
||||
//OPTION_GUIDE_END
|
||||
|
||||
void cybiko_get_info( const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
// --- the following bits of info are returned as 64-bit signed integers ---
|
||||
case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES : info->i = sizeof( cybiko_file_system); break;
|
||||
case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES : info->i = sizeof( cybiko_iter); break;
|
||||
// case IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME : info->i = 1; break;
|
||||
case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME : info->i = 1; break;
|
||||
// case IMGTOOLINFO_INT_BLOCK_SIZE : info->i = 264; break;
|
||||
// --- the following bits of info are returned as pointers to data or functions ---
|
||||
case IMGTOOLINFO_PTR_OPEN : info->open = cybiko_image_open; break;
|
||||
case IMGTOOLINFO_PTR_CREATE : info->create = cybiko_image_create; break;
|
||||
case IMGTOOLINFO_PTR_CLOSE : info->close = cybiko_image_close; break;
|
||||
case IMGTOOLINFO_PTR_BEGIN_ENUM : info->begin_enum = cybiko_image_begin_enum; break;
|
||||
case IMGTOOLINFO_PTR_NEXT_ENUM : info->next_enum = cybiko_image_next_enum; break;
|
||||
case IMGTOOLINFO_PTR_FREE_SPACE : info->free_space = cybiko_image_free_space; break;
|
||||
case IMGTOOLINFO_PTR_READ_FILE : info->read_file = cybiko_image_read_file; break;
|
||||
case IMGTOOLINFO_PTR_WRITE_FILE : info->write_file = cybiko_image_write_file; break;
|
||||
case IMGTOOLINFO_PTR_DELETE_FILE : info->delete_file = cybiko_image_delete_file; break;
|
||||
case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE : info->createimage_optguide = &cybiko_image_createimage_optguide; break;
|
||||
// case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE : info->writefile_optguide = cybiko_image_writefile_optguide; break;
|
||||
// --- the following bits of info are returned as NULL-terminated strings ---
|
||||
case IMGTOOLINFO_STR_NAME : strcpy( info->s = imgtool_temp_str(), "cybiko"); break;
|
||||
case IMGTOOLINFO_STR_DESCRIPTION : strcpy( info->s = imgtool_temp_str(), "Cybiko Classic File System"); break;
|
||||
case IMGTOOLINFO_STR_FILE : strcpy( info->s = imgtool_temp_str(), __FILE__); break;
|
||||
case IMGTOOLINFO_STR_FILE_EXTENSIONS : strcpy( info->s = imgtool_temp_str(), "bin,nv"); break;
|
||||
case IMGTOOLINFO_STR_EOLN : strcpy( info->s = imgtool_temp_str(), "\r\n"); break;
|
||||
case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC : strcpy( info->s = imgtool_temp_str(), "F[0]-2"); break;
|
||||
// case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC : strcpy( info->s = imgtool_temp_str(), "B[0]-1"); break;
|
||||
}
|
||||
}
|
@ -1,529 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Tim Schuerewegen
|
||||
/*
|
||||
|
||||
Cybiko Xtreme File System
|
||||
|
||||
(c) 2010 Tim Schuerewegen
|
||||
|
||||
*/
|
||||
|
||||
#include "imgtool.h"
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
struct cybiko_file_system
|
||||
{
|
||||
imgtool::stream *stream;
|
||||
uint32_t page_count, page_size, block_count_boot, block_count_file;
|
||||
uint16_t write_count;
|
||||
};
|
||||
|
||||
struct cybiko_iter
|
||||
{
|
||||
uint16_t block;
|
||||
};
|
||||
|
||||
struct cfs_file
|
||||
{
|
||||
char name[64]; // name of the file
|
||||
uint32_t date; // date/time of the file (seconds since 1900/01/01)
|
||||
uint32_t size; // size of the file
|
||||
uint32_t blocks; // number of blocks occupied by the file
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
BLOCK_TYPE_INVALID,
|
||||
BLOCK_TYPE_BOOT,
|
||||
BLOCK_TYPE_FILE
|
||||
};
|
||||
|
||||
#define MAX_PAGE_SIZE (264 * 2)
|
||||
|
||||
#define INVALID_FILE_ID 0xFFFF
|
||||
|
||||
#define BLOCK_USED(x) (x[0] & 0x80)
|
||||
#define BLOCK_FILE_ID(x) buffer_read_16_be( x + 2)
|
||||
#define BLOCK_PART_ID(x) buffer_read_16_be( x + 4)
|
||||
#define BLOCK_FILENAME(x) (char*)(x + 7)
|
||||
|
||||
#define FILE_HEADER_SIZE 0x48
|
||||
|
||||
static cybiko_file_system *get_cfs(imgtool::image &image)
|
||||
{
|
||||
return (cybiko_file_system*)image.extra_bytes();
|
||||
}
|
||||
|
||||
extern imgtool::datetime cybiko_time_crack(uint32_t cfs_time);
|
||||
extern uint32_t cybiko_time_setup(const imgtool::datetime &t);
|
||||
|
||||
static uint32_t buffer_read_32_be( uint8_t *buffer)
|
||||
{
|
||||
return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3] << 0);
|
||||
}
|
||||
|
||||
static uint16_t buffer_read_16_be( uint8_t *buffer)
|
||||
{
|
||||
return (buffer[0] << 8) | (buffer[1] << 0);
|
||||
}
|
||||
|
||||
static void buffer_write_32_be( uint8_t *buffer, uint32_t data)
|
||||
{
|
||||
buffer[0] = (data >> 24) & 0xFF;
|
||||
buffer[1] = (data >> 16) & 0xFF;
|
||||
buffer[2] = (data >> 8) & 0xFF;
|
||||
buffer[3] = (data >> 0) & 0xFF;
|
||||
}
|
||||
|
||||
static void buffer_write_16_be( uint8_t *buffer, uint16_t data)
|
||||
{
|
||||
buffer[0] = (data >> 8) & 0xFF;
|
||||
buffer[1] = (data >> 0) & 0xFF;
|
||||
}
|
||||
|
||||
// page = crc (2) + data (x)
|
||||
|
||||
static uint16_t page_buffer_calc_checksum( uint8_t *data, uint32_t size)
|
||||
{
|
||||
int i;
|
||||
uint32_t val = 0;
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
val = (val ^ data[i] ^ i) << 1;
|
||||
val = val | ((val >> 16) & 0x0001);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static int page_buffer_verify( uint8_t *buffer, uint32_t size, int block_type)
|
||||
{
|
||||
// checksum
|
||||
if (block_type == BLOCK_TYPE_FILE)
|
||||
{
|
||||
uint32_t checksum_page, checksum_calc;
|
||||
checksum_calc = page_buffer_calc_checksum( buffer + 2, size - 2);
|
||||
checksum_page = buffer_read_16_be( buffer + 0);
|
||||
if (checksum_calc != checksum_page) return false;
|
||||
}
|
||||
// ok
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cfs_block_to_page( cybiko_file_system *cfs, int block_type, uint32_t block, uint32_t *page)
|
||||
{
|
||||
switch (block_type)
|
||||
{
|
||||
case BLOCK_TYPE_BOOT : if (page) *page = block; return true;
|
||||
case BLOCK_TYPE_FILE : if (page) *page = block + cfs->block_count_boot; return true;
|
||||
default : return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int cfs_page_to_block( cybiko_file_system *cfs, uint32_t page, int *block_type, uint32_t *block)
|
||||
{
|
||||
uint32_t tmp = page;
|
||||
// boot block
|
||||
if (tmp < cfs->block_count_boot)
|
||||
{
|
||||
if (block_type) *block_type = BLOCK_TYPE_BOOT;
|
||||
if (block) *block = tmp;
|
||||
return true;
|
||||
}
|
||||
tmp -= cfs->block_count_boot;
|
||||
// file block
|
||||
if (tmp < cfs->block_count_file)
|
||||
{
|
||||
if (block_type) *block_type = BLOCK_TYPE_FILE;
|
||||
if (block) *block = tmp;
|
||||
return true;
|
||||
}
|
||||
tmp -= cfs->block_count_file;
|
||||
// error
|
||||
return false;
|
||||
}
|
||||
|
||||
static int cfs_page_read( cybiko_file_system *cfs, uint8_t *buffer, uint32_t page)
|
||||
{
|
||||
if (page >= cfs->page_count) return false;
|
||||
cfs->stream->seek(page * cfs->page_size, SEEK_SET);
|
||||
cfs->stream->read(buffer, cfs->page_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cfs_page_write( cybiko_file_system *cfs, uint8_t *buffer, uint32_t page)
|
||||
{
|
||||
if (page >= cfs->page_count) return false;
|
||||
cfs->stream->seek(page * cfs->page_size, SEEK_SET);
|
||||
cfs->stream->write(buffer, cfs->page_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cfs_block_read( cybiko_file_system *cfs, uint8_t *buffer, int block_type, uint32_t block)
|
||||
{
|
||||
uint8_t buffer_page[MAX_PAGE_SIZE];
|
||||
uint32_t page;
|
||||
if (!cfs_block_to_page( cfs, block_type, block, &page)) return false;
|
||||
if (!cfs_page_read( cfs, buffer_page, page)) return false;
|
||||
memcpy( buffer, buffer_page + 2, cfs->page_size - 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cfs_block_write( cybiko_file_system *cfs, uint8_t *buffer, int block_type, uint32_t block)
|
||||
{
|
||||
uint8_t buffer_page[MAX_PAGE_SIZE];
|
||||
uint32_t page;
|
||||
uint16_t checksum;
|
||||
memcpy( buffer_page + 2, buffer, cfs->page_size - 2);
|
||||
if (block_type == BLOCK_TYPE_BOOT)
|
||||
{
|
||||
checksum = 0xFFFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
checksum = page_buffer_calc_checksum( buffer_page + 2, cfs->page_size - 2);
|
||||
}
|
||||
buffer_write_16_be( buffer_page + 0, checksum);
|
||||
if (!cfs_block_to_page( cfs, block_type, block, &page)) return false;
|
||||
if (!cfs_page_write( cfs, buffer_page, page)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cfs_file_delete( cybiko_file_system *cfs, uint16_t file_id)
|
||||
{
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
int i;
|
||||
for (i=0;i<cfs->block_count_file;i++)
|
||||
{
|
||||
if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return false;
|
||||
if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id))
|
||||
{
|
||||
buffer[0] &= ~0x80;
|
||||
if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cfs_file_info( cybiko_file_system *cfs, uint16_t file_id, cfs_file *file)
|
||||
{
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
int i;
|
||||
file->blocks = file->size = 0;
|
||||
for (i=0;i<cfs->block_count_file;i++)
|
||||
{
|
||||
if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return false;
|
||||
if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id))
|
||||
{
|
||||
if (BLOCK_PART_ID(buffer) == 0)
|
||||
{
|
||||
strcpy( file->name, BLOCK_FILENAME(buffer));
|
||||
file->date = buffer_read_32_be( buffer + 6 + FILE_HEADER_SIZE - 4);
|
||||
}
|
||||
file->size += buffer[1];
|
||||
file->blocks++;
|
||||
}
|
||||
}
|
||||
return (file->blocks > 0) ? true : false;
|
||||
}
|
||||
|
||||
static int cfs_file_find( cybiko_file_system *cfs, const char *filename, uint16_t *file_id)
|
||||
{
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
int i;
|
||||
for (i=0;i<cfs->block_count_file;i++)
|
||||
{
|
||||
if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return false;
|
||||
if (BLOCK_USED(buffer) && (BLOCK_PART_ID(buffer) == 0) && (strcmp( filename, BLOCK_FILENAME(buffer)) == 0))
|
||||
{
|
||||
*file_id = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool cfs_verify(cybiko_file_system &cfs)
|
||||
{
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
int i, block_type;
|
||||
for (i = 0; i < cfs.page_count; i++)
|
||||
{
|
||||
if (!cfs_page_read(&cfs, buffer, i)) return false;
|
||||
if (!cfs_page_to_block(&cfs, i, &block_type, NULL)) return false;
|
||||
if (!page_buffer_verify(buffer, cfs.page_size, block_type)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cfs_init(cybiko_file_system &cfs, imgtool::stream::ptr &&stream)
|
||||
{
|
||||
cfs.stream = stream.release();
|
||||
cfs.page_count = 2005;
|
||||
cfs.page_size = 258;
|
||||
cfs.block_count_boot = 5;
|
||||
cfs.block_count_file = cfs.page_count - cfs.block_count_boot;
|
||||
cfs.write_count = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cfs_format(cybiko_file_system *cfs)
|
||||
{
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
int i;
|
||||
// boot blocks
|
||||
memset( buffer, 0xFF, sizeof( buffer));
|
||||
for (i=0;i<cfs->block_count_boot;i++)
|
||||
{
|
||||
if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_BOOT, i)) return false;
|
||||
}
|
||||
// file blocks
|
||||
memset( buffer, 0xFF, sizeof( buffer));
|
||||
buffer[0] &= ~0x80;
|
||||
for (i=0;i<cfs->block_count_file;i++)
|
||||
{
|
||||
if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return false;
|
||||
}
|
||||
// padding
|
||||
buffer[0] = 0xFF;
|
||||
for (i=0;i<0x1B56;i++)
|
||||
{
|
||||
cfs->stream->write(buffer, 1);
|
||||
}
|
||||
// ok
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint16_t cfs_calc_free_blocks( cybiko_file_system *cfs)
|
||||
{
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
int i;
|
||||
uint16_t blocks = 0;
|
||||
for (i=0;i<cfs->block_count_file;i++)
|
||||
{
|
||||
if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return 0;
|
||||
if (!BLOCK_USED(buffer)) blocks++;
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
static uint32_t cfs_calc_free_space( cybiko_file_system *cfs, uint16_t blocks)
|
||||
{
|
||||
uint32_t free_space;
|
||||
free_space = blocks * ((cfs->page_size - 2) - 6);
|
||||
if (free_space > 0) free_space -= FILE_HEADER_SIZE;
|
||||
return free_space;
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_open(imgtool::image &image, imgtool::stream::ptr &&stream)
|
||||
{
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
// init
|
||||
if (!cfs_init(*cfs, std::move(stream))) return IMGTOOLERR_CORRUPTIMAGE;
|
||||
// verify
|
||||
if (!cfs_verify(*cfs)) return IMGTOOLERR_CORRUPTIMAGE;
|
||||
// ok
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static void cybiko_image_close(imgtool::image &image)
|
||||
{
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
delete cfs->stream;
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts)
|
||||
{
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
// init
|
||||
if (!cfs_init(*cfs, std::move(stream))) return IMGTOOLERR_CORRUPTIMAGE;
|
||||
// format
|
||||
if (!cfs_format(cfs)) return IMGTOOLERR_CORRUPTIMAGE;
|
||||
// ok
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_begin_enum(imgtool::directory &enumeration, const char *path)
|
||||
{
|
||||
cybiko_iter *iter = (cybiko_iter*)enumeration.extra_bytes();
|
||||
iter->block = 0;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_next_enum(imgtool::directory &enumeration, imgtool_dirent &ent)
|
||||
{
|
||||
imgtool::image &image(enumeration.image());
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
cybiko_iter *iter = (cybiko_iter*)enumeration.extra_bytes();
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
uint16_t file_id = INVALID_FILE_ID;
|
||||
cfs_file file;
|
||||
// find next file
|
||||
while (iter->block < cfs->block_count_file)
|
||||
{
|
||||
if (!cfs_block_read(cfs, buffer, BLOCK_TYPE_FILE, iter->block++)) return IMGTOOLERR_READERROR;
|
||||
if (BLOCK_USED(buffer) && (BLOCK_PART_ID(buffer) == 0))
|
||||
{
|
||||
file_id = BLOCK_FILE_ID(buffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// get file information
|
||||
if ((file_id != INVALID_FILE_ID) && cfs_file_info(cfs, file_id, &file))
|
||||
{
|
||||
strcpy(ent.filename, file.name);
|
||||
ent.filesize = file.size;
|
||||
ent.lastmodified_time = cybiko_time_crack(file.date);
|
||||
ent.filesize = file.size;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent.eof = 1;
|
||||
}
|
||||
// ok
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_free_space(imgtool::partition &partition, uint64_t *size)
|
||||
{
|
||||
imgtool::image &image(partition.image());
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
if (size) *size = cfs_calc_free_space( cfs, cfs_calc_free_blocks( cfs));
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_read_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)
|
||||
{
|
||||
imgtool::image &image(partition.image());
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
uint16_t file_id, part_id = 0, old_part_id;
|
||||
int i;
|
||||
// check filename
|
||||
if (strlen( filename) > 58) return IMGTOOLERR_BADFILENAME;
|
||||
// find file
|
||||
if (!cfs_file_find( cfs, filename, &file_id)) return IMGTOOLERR_FILENOTFOUND;
|
||||
// read file
|
||||
do
|
||||
{
|
||||
old_part_id = part_id;
|
||||
for (i=0;i<cfs->block_count_file;i++)
|
||||
{
|
||||
if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_READERROR;
|
||||
if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id) && (BLOCK_PART_ID(buffer) == part_id))
|
||||
{
|
||||
destf.write(buffer + 6 + ((part_id == 0) ? FILE_HEADER_SIZE : 0), buffer[1]);
|
||||
part_id++;
|
||||
}
|
||||
}
|
||||
} while (old_part_id != part_id);
|
||||
// ok
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_write_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts)
|
||||
{
|
||||
imgtool::image &image(partition.image());
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
uint8_t buffer[MAX_PAGE_SIZE];
|
||||
uint16_t file_id, part_id = 0, free_blocks;
|
||||
uint64_t bytes_left;
|
||||
cfs_file file;
|
||||
int i;
|
||||
// check filename
|
||||
if (strlen( filename) > 58) return IMGTOOLERR_BADFILENAME;
|
||||
// find file
|
||||
if (!cfs_file_find( cfs, filename, &file_id)) file_id = INVALID_FILE_ID;
|
||||
// check free space
|
||||
free_blocks = cfs_calc_free_blocks(cfs);
|
||||
if (file_id != INVALID_FILE_ID)
|
||||
{
|
||||
if (!cfs_file_info(cfs, file_id, &file)) return IMGTOOLERR_UNEXPECTED;
|
||||
free_blocks += file.blocks;
|
||||
}
|
||||
if (cfs_calc_free_space(cfs, free_blocks) < sourcef.size()) return IMGTOOLERR_NOSPACE;
|
||||
// delete file
|
||||
if (file_id != INVALID_FILE_ID)
|
||||
{
|
||||
if (!cfs_file_delete(cfs, file_id)) return IMGTOOLERR_UNEXPECTED;
|
||||
}
|
||||
// create/write destination file
|
||||
bytes_left = sourcef.size();
|
||||
i = 0;
|
||||
while (i < cfs->block_count_file)
|
||||
{
|
||||
if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_READERROR;
|
||||
if (!BLOCK_USED(buffer))
|
||||
{
|
||||
if (part_id == 0) file_id = i;
|
||||
memset( buffer, 0xFF, cfs->page_size - 0x02);
|
||||
buffer[0] = 0x80;
|
||||
buffer[1] = (cfs->page_size - 2) - 6 - ((part_id == 0) ? FILE_HEADER_SIZE : 0);
|
||||
if (bytes_left < buffer[1]) buffer[1] = bytes_left;
|
||||
buffer_write_16_be( buffer + 2, file_id);
|
||||
buffer_write_16_be( buffer + 4, part_id);
|
||||
if (part_id == 0)
|
||||
{
|
||||
buffer[6] = 0x20;
|
||||
strcpy(BLOCK_FILENAME(buffer), filename);
|
||||
buffer_write_32_be( buffer + 6 + FILE_HEADER_SIZE - 4, cybiko_time_setup(imgtool::datetime::now(imgtool::datetime::datetime_type::LOCAL)));
|
||||
sourcef.read(buffer + 6 + FILE_HEADER_SIZE, buffer[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
sourcef.read(buffer + 6, buffer[1]);
|
||||
}
|
||||
if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_WRITEERROR;
|
||||
bytes_left -= buffer[1];
|
||||
if (bytes_left == 0) break;
|
||||
part_id++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// ok
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t cybiko_image_delete_file(imgtool::partition &partition, const char *filename)
|
||||
{
|
||||
imgtool::image &image(partition.image());
|
||||
cybiko_file_system *cfs = get_cfs(image);
|
||||
uint16_t file_id;
|
||||
// check filename
|
||||
if (strlen(filename) > 58) return IMGTOOLERR_BADFILENAME;
|
||||
// find file
|
||||
if (!cfs_file_find(cfs, filename, &file_id)) return IMGTOOLERR_FILENOTFOUND;
|
||||
// delete file
|
||||
if (!cfs_file_delete(cfs, file_id)) return IMGTOOLERR_UNEXPECTED;
|
||||
// ok
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
void cybikoxt_get_info( const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
// --- the following bits of info are returned as 64-bit signed integers ---
|
||||
case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES : info->i = sizeof( cybiko_file_system); break;
|
||||
case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES : info->i = sizeof( cybiko_iter); break;
|
||||
// case IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME : info->i = 1; break;
|
||||
case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME : info->i = 1; break;
|
||||
// case IMGTOOLINFO_INT_BLOCK_SIZE : info->i = 264; break;
|
||||
// --- the following bits of info are returned as pointers to data or functions ---
|
||||
case IMGTOOLINFO_PTR_OPEN : info->open = cybiko_image_open; break;
|
||||
case IMGTOOLINFO_PTR_CREATE : info->create = cybiko_image_create; break;
|
||||
case IMGTOOLINFO_PTR_CLOSE : info->close = cybiko_image_close; break;
|
||||
case IMGTOOLINFO_PTR_BEGIN_ENUM : info->begin_enum = cybiko_image_begin_enum; break;
|
||||
case IMGTOOLINFO_PTR_NEXT_ENUM : info->next_enum = cybiko_image_next_enum; break;
|
||||
case IMGTOOLINFO_PTR_FREE_SPACE : info->free_space = cybiko_image_free_space; break;
|
||||
case IMGTOOLINFO_PTR_READ_FILE : info->read_file = cybiko_image_read_file; break;
|
||||
case IMGTOOLINFO_PTR_WRITE_FILE : info->write_file = cybiko_image_write_file; break;
|
||||
case IMGTOOLINFO_PTR_DELETE_FILE : info->delete_file = cybiko_image_delete_file; break;
|
||||
// --- the following bits of info are returned as NULL-terminated strings ---
|
||||
case IMGTOOLINFO_STR_NAME : strcpy( info->s = imgtool_temp_str(), "cybikoxt"); break;
|
||||
case IMGTOOLINFO_STR_DESCRIPTION : strcpy( info->s = imgtool_temp_str(), "Cybiko Xtreme File System"); break;
|
||||
case IMGTOOLINFO_STR_FILE : strcpy( info->s = imgtool_temp_str(), __FILE__); break;
|
||||
case IMGTOOLINFO_STR_FILE_EXTENSIONS : strcpy( info->s = imgtool_temp_str(), "bin,nv"); break;
|
||||
case IMGTOOLINFO_STR_EOLN : strcpy( info->s = imgtool_temp_str(), "\r\n"); break;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,6 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Raphael Nabet
|
||||
|
||||
#include "imgtool.h"
|
||||
|
||||
void fat_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info);
|
@ -1,741 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Antoine Mine
|
||||
/****************************************************************************
|
||||
|
||||
hp48.cpp
|
||||
|
||||
Memory cards for HP48 SX/GX
|
||||
|
||||
Antoine Mine' 2014
|
||||
|
||||
*****************************************************************************
|
||||
*/
|
||||
|
||||
/*
|
||||
HP memory cards can contain two kinds of objects:
|
||||
- attached libraries
|
||||
- backup objects (encapsulating any kind of object)
|
||||
|
||||
Attached libraries appear with a LIB attribute in imgtool's listing, and in
|
||||
the global "LIBRARY" menu on the HP48.
|
||||
Backup objects appear with a BAK attribute in imgtool's listing, and
|
||||
in the relevant "PORTS" menu on the HP48.
|
||||
|
||||
Currently, we only support storing host files into backup objects and
|
||||
retreiving backup objects into files.
|
||||
For attached library objects in the memory card, we only show their name, but
|
||||
cannot retrieve their contents.
|
||||
|
||||
To install a library contained in a host file, copy it with imgtool to
|
||||
the card; it creates a backup object visible in the "PORTS" menu;
|
||||
then evalue the object, store it into some port (e.g, 0 STO),
|
||||
turn off the HP48 and back on; the library should now be attached and
|
||||
visible in the "LIBRARY" menu.
|
||||
(The process is similar to installing a library from a file you get
|
||||
from the serial interface.)
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
HP memory cards have size from 32 KB to 4096 KB.
|
||||
However, cards with size greater than 128 KB are seen as multiple ports of
|
||||
size 128 KB on the HP48 GX. In imgtool, we see them as distinct 128 KB
|
||||
partitions.
|
||||
Unfortunately, at the moment, partitions do not seem to be supported in the
|
||||
command-line tool...
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Here is the format of backup objects (addresses are in 4-bit nibbles):
|
||||
|
||||
offset field size value
|
||||
0 prolog 5 02B62 prolog for overall backup object
|
||||
5 size 5 len total size in nibble without prolog
|
||||
10 name length 2 nbcar object name, in characters
|
||||
12 name 2*nbcar -
|
||||
12+2*nbcar name length 2 nbcar same value as name length at 10
|
||||
14+2*nbcar object - -
|
||||
len-5 prolog 5 02911 prolog for inlined system integer
|
||||
len - 1 0
|
||||
len+1 CRC 4 - CRC from 5 to len
|
||||
|
||||
total length: len+4
|
||||
|
||||
i.e., the backup object is a container containing the object name
|
||||
and two objects: the backuped object itself and a system integer
|
||||
that contains the CRC.
|
||||
|
||||
HP48 host files start with a "HPHP48-X" header (where X can be any
|
||||
single letter, which denotes a ROM revision and, implicitly, whether
|
||||
the program is for the S/SX or the G/GX family).
|
||||
When storing a file to the memory card, we strip the header and
|
||||
embed the object into a backup object with the correct CRC.
|
||||
When copying from the memory card to a host file, we extract the object
|
||||
from the backup container, stripping its CRC, and add the HPHP48-X header.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
Includes
|
||||
*****************************************************************************/
|
||||
|
||||
#include "imgtool.h"
|
||||
|
||||
#include "opresolv.h"
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
Data structures
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
struct hp48_card
|
||||
{
|
||||
imgtool::stream *stream;
|
||||
int modified;
|
||||
|
||||
/* size, in bytes of card data: from 32 KB to 4 MB */
|
||||
int size;
|
||||
|
||||
/* we store each nibble (4-bit) into its own byte, for simpler addressing;
|
||||
hence, data has 2*size
|
||||
*/
|
||||
uint8_t* data;
|
||||
|
||||
};
|
||||
|
||||
struct hp48_partition
|
||||
{
|
||||
/* pointer to the beginning of the partition inside the hp48_card */
|
||||
uint8_t* data;
|
||||
|
||||
/* size, in bytes (128 KB or less) */
|
||||
int size;
|
||||
|
||||
};
|
||||
|
||||
struct hp48_directory
|
||||
{
|
||||
int pos;
|
||||
};
|
||||
|
||||
|
||||
#define PROLOG_BACKUP 0x02B62
|
||||
#define PROLOG_LIBRARY 0x02B40
|
||||
#define PROLOG_SYSINT 0x02911
|
||||
|
||||
/* memory cards are composed solely of libraries and backup objects */
|
||||
#define IS_OBJECT(prolog) ((prolog) == PROLOG_LIBRARY || (prolog) == PROLOG_BACKUP)
|
||||
|
||||
/* host files begin with this prefix (up to the last letter) */
|
||||
static const char* hp48_prefix = "HPHP48-R";
|
||||
|
||||
|
||||
|
||||
OPTION_GUIDE_START( hp48_create_optionguide )
|
||||
OPTION_INT('S', "size", "Size in KB" )
|
||||
OPTION_GUIDE_END
|
||||
|
||||
/* size in KB, 128 KB being the default */
|
||||
static const char hp48_create_optionspec[] = "S32/64/[128]/256/512/1024/2048/4096";
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
Utility functions
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
static hp48_card *get_hp48_card(imgtool::image &image)
|
||||
{
|
||||
return (hp48_card*) image.extra_bytes();
|
||||
}
|
||||
|
||||
/* byes to nibbles */
|
||||
static void unpack(uint8_t* dst, uint8_t* src, int nsize)
|
||||
{
|
||||
int i;
|
||||
|
||||
if ( nsize & 1 )
|
||||
{
|
||||
dst[nsize-1] = src[nsize/2] & 0xf;
|
||||
}
|
||||
|
||||
for ( i = nsize/2-1; i >= 0; i-- )
|
||||
{
|
||||
dst[2*i+1] = src[i] >> 4;
|
||||
dst[2*i ] = src[i] & 0xf;
|
||||
}
|
||||
}
|
||||
|
||||
/* nibbles to bytes */
|
||||
static void pack(uint8_t* dst, uint8_t* src, int nsize)
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i = 0 ; i < nsize/2; i++ )
|
||||
{
|
||||
dst[i] = (src[2*i] & 0xf) | (src[2*i+1] << 4);
|
||||
}
|
||||
|
||||
if ( nsize & 1 )
|
||||
{
|
||||
dst[nsize/2] = src[nsize-1] & 0xf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int read20(uint8_t* data)
|
||||
{
|
||||
return data[0] | (data[1] << 4) | (data[2] << 8) | (data[3] << 12) | (data[4] << 16);
|
||||
}
|
||||
|
||||
static int read8(uint8_t* data)
|
||||
{
|
||||
return data[0] | (data[1] << 4);
|
||||
}
|
||||
|
||||
static void readstring(char* dst, uint8_t* data, int nb)
|
||||
{
|
||||
int i;
|
||||
for ( i = 0; i < nb; i++ )
|
||||
{
|
||||
dst[i] = read8( data + 2*i );
|
||||
}
|
||||
dst[nb] = 0;
|
||||
}
|
||||
|
||||
static void write20(uint8_t* data, int v)
|
||||
{
|
||||
data[0] = v & 0xf;
|
||||
data[1] = (v >> 4) & 0xf;
|
||||
data[2] = (v >> 8) & 0xf;
|
||||
data[3] = (v >> 12) & 0xf;
|
||||
data[4] = (v >> 16) & 0xf;
|
||||
}
|
||||
|
||||
static void write8(uint8_t* data, int v)
|
||||
{
|
||||
data[0] = v & 0xf;
|
||||
data[1] = (v >> 4) & 0xf;
|
||||
}
|
||||
|
||||
static void writestring(uint8_t* data, const char* str, int nb)
|
||||
{
|
||||
int i;
|
||||
for ( i = 0; i < nb; i++ )
|
||||
{
|
||||
write8( data + 2*i, str[i] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* go to the end, return its offset */
|
||||
static int find_end(hp48_partition* p)
|
||||
{
|
||||
int pos = 0;
|
||||
while (1)
|
||||
{
|
||||
if ( pos + 10 > 2*p->size) break;
|
||||
|
||||
int prolog = read20( p->data + pos );
|
||||
if ( !IS_OBJECT( prolog )) break;
|
||||
|
||||
pos += read20( p->data + pos + 5 ) + 5;
|
||||
}
|
||||
|
||||
if ( pos > 2*p->size ) pos = 2*p->size;
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
/* find the backup object with the given name, returns its offset or -1 (not found) */
|
||||
static int find_file(hp48_partition* p, const char* filename, int *ptotalsize, int* pstart, int* pcontentsize)
|
||||
{
|
||||
uint8_t* data = p->data;
|
||||
int pos = 0;
|
||||
|
||||
/* find file */
|
||||
while (1)
|
||||
{
|
||||
if ( pos + 10 > 2*p->size) return -1;
|
||||
|
||||
/* get prolog */
|
||||
int prolog = read20( data+pos );
|
||||
if ( !IS_OBJECT(prolog)) return -1;
|
||||
|
||||
/* get size */
|
||||
int totalsize = read20( data+pos+5 );
|
||||
if ( totalsize < 14) return -1;
|
||||
if ( pos + 5 + totalsize > 2*p->size ) return -1;
|
||||
|
||||
if ( prolog == PROLOG_BACKUP )
|
||||
{
|
||||
/* get name */
|
||||
int namelen = read8( data + pos + 10 );
|
||||
char name[257];
|
||||
if ( 9 + 2*namelen > totalsize ) return -1;
|
||||
readstring( name, data + pos + 12, namelen );
|
||||
|
||||
/* check name */
|
||||
if ( !strcmp( name, filename ) )
|
||||
{
|
||||
/* found! */
|
||||
if ( ptotalsize ) *ptotalsize = totalsize;
|
||||
if ( pstart ) *pstart = pos + 14 + 2*namelen;
|
||||
if ( pcontentsize ) *pcontentsize = totalsize - (9 + 2*namelen);
|
||||
return pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* skip */
|
||||
pos += totalsize + 5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* skip */
|
||||
pos += totalsize + 5;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// never executed
|
||||
//return -1;
|
||||
}
|
||||
|
||||
|
||||
/* CRC computing.
|
||||
This is the same CRC that is computed by the HP48 hardware.
|
||||
*/
|
||||
static uint16_t crc(uint8_t* data, int len)
|
||||
{
|
||||
uint16_t crc = 0;
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < len; i++ )
|
||||
{
|
||||
crc = (crc >> 4) ^ (((crc ^ data[i]) & 0xf) * 0x1081);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
Imgtool functions
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
static imgtoolerr_t hp48_open(imgtool::image &img, imgtool::stream::ptr &&stream)
|
||||
{
|
||||
hp48_card* c = get_hp48_card(img);
|
||||
int size = stream->size();
|
||||
|
||||
/* check that size is a power of 2 between 32 KB and 4 MG */
|
||||
if ( (size < 32 * 1024) ||
|
||||
(size > 4 * 1024 * 1024) ||
|
||||
(size & (size-1)) )
|
||||
{
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
}
|
||||
|
||||
/* store info */
|
||||
c->stream = stream.get();
|
||||
c->modified = 0;
|
||||
c->size = size;
|
||||
c->data = (uint8_t*) malloc( 2 * size );
|
||||
if ( !c->data )
|
||||
{
|
||||
return IMGTOOLERR_READERROR;
|
||||
}
|
||||
|
||||
/* fully load image */
|
||||
c->stream->seek(0, SEEK_SET);
|
||||
if (c->stream->read(c->data, size) < size)
|
||||
{
|
||||
return IMGTOOLERR_READERROR;
|
||||
}
|
||||
unpack( c->data, c->data, 2 * size );
|
||||
|
||||
c->stream = stream.release();
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t hp48_create(imgtool::image &img,
|
||||
imgtool::stream::ptr &&stream,
|
||||
util::option_resolution *opts)
|
||||
{
|
||||
hp48_card* c = get_hp48_card(img);
|
||||
int size;
|
||||
|
||||
size = opts->lookup_int('S');
|
||||
|
||||
c->stream = stream.get();
|
||||
c->modified = 1;
|
||||
c->size = size * 1024;
|
||||
c->data = (uint8_t*) malloc( 2 * c->size );
|
||||
if ( !c->data )
|
||||
{
|
||||
return IMGTOOLERR_READERROR;
|
||||
}
|
||||
|
||||
/* zeroing the image seems fine */
|
||||
memset( c->data, 0, 2 * c->size );
|
||||
|
||||
c->stream = stream.release();
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void hp48_close(imgtool::image &img)
|
||||
{
|
||||
hp48_card* c = get_hp48_card(img);
|
||||
|
||||
if ( c->modified )
|
||||
{
|
||||
/* save image */
|
||||
pack( c->data, c->data, 2 * c->size );
|
||||
c->stream->seek(0, SEEK_SET);
|
||||
c->stream->write(c->data, c->size);
|
||||
}
|
||||
|
||||
/* clean up */
|
||||
free( c->data );
|
||||
delete c->stream;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* each 128 KB chunk is a distinct partition */
|
||||
#define MAX_PORT_SIZE (128*1024)
|
||||
|
||||
void hp48_partition_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info);
|
||||
|
||||
static imgtoolerr_t hp48_list_partitions(imgtool::image &img, std::vector<imgtool::partition_info> &partitions)
|
||||
{
|
||||
hp48_card* c = get_hp48_card(img);
|
||||
|
||||
int i;
|
||||
for (i = 0; i * MAX_PORT_SIZE < c->size ; i++)
|
||||
{
|
||||
// offset and size in bytes
|
||||
uint64_t base_block = i * MAX_PORT_SIZE;
|
||||
uint64_t block_count = std::min((uint64_t)c->size - base_block, (uint64_t)MAX_PORT_SIZE);
|
||||
|
||||
// append the partition
|
||||
partitions.emplace_back(hp48_partition_get_info, base_block, block_count);
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t hp48_open_partition(imgtool::partition &part,
|
||||
uint64_t first_block, uint64_t block_count)
|
||||
{
|
||||
imgtool::image &img(part.image());
|
||||
hp48_card* c = get_hp48_card(img);
|
||||
hp48_partition* p = (hp48_partition*) part.extra_bytes();
|
||||
|
||||
if ( first_block + block_count > c->size )
|
||||
return IMGTOOLERR_INVALIDPARTITION;
|
||||
|
||||
/* store partition position */
|
||||
p->data = c->data + first_block;
|
||||
p->size = block_count;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t hp48_beginenum(imgtool::directory &enumeration, const char *path)
|
||||
{
|
||||
hp48_directory* d = (hp48_directory*) enumeration.extra_bytes();
|
||||
|
||||
d->pos = 0;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t hp48_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent)
|
||||
{
|
||||
imgtool::partition &part(enumeration.partition());
|
||||
//imgtool::image &img(part.image());
|
||||
//hp48_card* c = get_hp48_card(img);
|
||||
hp48_partition* p = (hp48_partition*) part.extra_bytes();
|
||||
hp48_directory* d = (hp48_directory*) enumeration.extra_bytes();
|
||||
|
||||
uint8_t* data = p->data;
|
||||
int pos = d->pos;
|
||||
|
||||
if ( pos < 0 || pos+12 > 2*p->size )
|
||||
{
|
||||
ent.eof = 1;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
int prolog = read20( data+pos );
|
||||
|
||||
if ( IS_OBJECT(prolog) )
|
||||
{
|
||||
pos += 5;
|
||||
int totalsize = read20( data+pos );
|
||||
pos += 5;
|
||||
|
||||
int namelen = read8( data+pos );
|
||||
pos += 2;
|
||||
if ( (pos + 2*namelen > 2*p->size) ||
|
||||
(namelen >= sizeof(ent.filename)) )
|
||||
{
|
||||
ent.eof = 1;
|
||||
return IMGTOOLERR_CORRUPTFILE;
|
||||
}
|
||||
readstring( ent.filename, data+pos, namelen );
|
||||
|
||||
/* compute size in bytes, removing name, length & CRC fields */
|
||||
ent.filesize = ((totalsize - 19 - 2*namelen) + 1) / 2;
|
||||
|
||||
switch (prolog)
|
||||
{
|
||||
case PROLOG_LIBRARY: strncpy( ent.attr, "LIB", sizeof(ent.attr) ); break;
|
||||
case PROLOG_BACKUP: strncpy( ent.attr, "BAK", sizeof(ent.attr) ); break;
|
||||
default: strncpy( ent.attr, "?", sizeof(ent.attr) );
|
||||
}
|
||||
|
||||
d->pos = d->pos + totalsize + 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 0 or unknown object => end */
|
||||
ent.eof = 1;
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t hp48_freespace(imgtool::partition &part, uint64_t *size)
|
||||
{
|
||||
//imgtool::image &img(part.image());
|
||||
//hp48_card* c = get_hp48_card(img);
|
||||
hp48_partition* p = (hp48_partition*) part.extra_bytes();
|
||||
|
||||
*size = p->size - (find_end(p)+1)/2;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t hp48_readfile(imgtool::partition &part,
|
||||
const char *filename,
|
||||
const char *fork,
|
||||
imgtool::stream &destf)
|
||||
{
|
||||
//imgtool::image &img(part.image());
|
||||
//hp48_card* c = get_hp48_card(img);
|
||||
hp48_partition* p = (hp48_partition*) part.extra_bytes();
|
||||
|
||||
/* find entry */
|
||||
int totalsize, start, size;
|
||||
int pos = find_file(p, filename, &totalsize, &start, &size);
|
||||
if ( pos == -1 )
|
||||
{
|
||||
return IMGTOOLERR_FILENOTFOUND;
|
||||
}
|
||||
|
||||
/* CRC check */
|
||||
uint16_t objcrc = read20( p->data + pos + totalsize ) >> 4;
|
||||
uint16_t mycrc = crc( p->data + pos + 5, totalsize - 4);
|
||||
if ( objcrc != mycrc )
|
||||
{
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
}
|
||||
size -= 10;
|
||||
|
||||
/* save header */
|
||||
destf.write(hp48_prefix, 8);
|
||||
|
||||
/* save contents to host file */
|
||||
int bytesize = (size + 1) / 2;
|
||||
uint8_t* buf = (uint8_t*) malloc( bytesize );
|
||||
if (!buf)
|
||||
{
|
||||
return IMGTOOLERR_FILENOTFOUND;
|
||||
}
|
||||
pack( buf, p->data + start, size );
|
||||
destf.write(buf, bytesize);
|
||||
free( buf );
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t hp48_deletefile(imgtool::partition &part,
|
||||
const char *filename)
|
||||
{
|
||||
imgtool::image &img(part.image());
|
||||
hp48_card* c = get_hp48_card(img);
|
||||
hp48_partition* p = (hp48_partition*) part.extra_bytes();
|
||||
|
||||
/* find entry */
|
||||
int totalsize;
|
||||
int pos = find_file(p, filename, &totalsize, NULL, NULL );
|
||||
if ( pos == -1 )
|
||||
{
|
||||
return IMGTOOLERR_FILENOTFOUND;
|
||||
}
|
||||
|
||||
/* move */
|
||||
totalsize += 5;
|
||||
memmove( p->data+pos, p->data+pos+totalsize, 2*p->size-(pos+totalsize) );
|
||||
memset( p->data + 2*p->size-totalsize, 0, totalsize);
|
||||
c->modified = 1;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t hp48_writefile(imgtool::partition &part,
|
||||
const char *filename,
|
||||
const char *fork,
|
||||
imgtool::stream &sourcef,
|
||||
util::option_resolution *opts)
|
||||
{
|
||||
imgtool::image &img(part.image());
|
||||
hp48_card* c = get_hp48_card(img);
|
||||
hp48_partition* p = (hp48_partition*) part.extra_bytes();
|
||||
|
||||
/* check header */
|
||||
char head[8];
|
||||
sourcef.read(head, 8);
|
||||
if ( memcmp( head, hp48_prefix, 7) )
|
||||
{
|
||||
return IMGTOOLERR_READERROR;
|
||||
}
|
||||
|
||||
/* ensure that the file does not exist */
|
||||
/* TODO: resize the existing file instead, to keep it in place? */
|
||||
hp48_deletefile( part, filename );
|
||||
|
||||
/* goto end */
|
||||
//uint8_t* data = p->data;
|
||||
int pos = find_end(p);
|
||||
|
||||
int len = strlen( filename );
|
||||
if ( len > 255 ) len = 255;
|
||||
|
||||
/* check size */
|
||||
int filesize = sourcef.size() - 8;
|
||||
if ( pos + 2*filesize + 24 + 2*len > 2 * p->size )
|
||||
{
|
||||
return IMGTOOLERR_NOSPACE;
|
||||
}
|
||||
|
||||
/* load file */
|
||||
uint8_t* buf = (uint8_t*) malloc( filesize );
|
||||
if ( !buf ) return IMGTOOLERR_NOSPACE;
|
||||
sourcef.read(buf, filesize);
|
||||
|
||||
/* store backup object */
|
||||
int org = pos;
|
||||
int totalsize = 2*filesize + 19 + 2*len;
|
||||
write20( p->data+pos, PROLOG_BACKUP );
|
||||
pos +=5;
|
||||
write20( p->data+pos, totalsize );
|
||||
pos +=5;
|
||||
write8( p->data+pos, len );
|
||||
pos += 2;
|
||||
writestring( p->data+pos, filename, len );
|
||||
pos += 2*len;
|
||||
write8( p->data+pos, len );
|
||||
pos += 2;
|
||||
unpack( p->data+pos, buf, 2*filesize );
|
||||
pos += 2*filesize;
|
||||
|
||||
/* store crc */
|
||||
write20( p->data+pos, PROLOG_SYSINT );
|
||||
pos += 5;
|
||||
p->data[pos] = 0;
|
||||
write20( p->data+pos, crc(p->data+org+5, totalsize-4) << 4 );
|
||||
|
||||
free(buf);
|
||||
|
||||
c->modified = 1;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
Imgtool module declaration
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
void hp48_partition_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
||||
case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "hp48"); break;
|
||||
case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "HP48 SX/GX memory card"); break;
|
||||
case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break;
|
||||
case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "crd"); break;
|
||||
|
||||
/* --- the following bits of info are returned as pointers to data or functions --- */
|
||||
case IMGTOOLINFO_PTR_OPEN_PARTITION: info->open_partition = hp48_open_partition; break;
|
||||
case IMGTOOLINFO_PTR_CLOSE: info->close = hp48_close; break;
|
||||
case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = hp48_beginenum; break;
|
||||
case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = hp48_nextenum; break;
|
||||
case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = hp48_freespace; break;
|
||||
case IMGTOOLINFO_PTR_READ_FILE: info->read_file = hp48_readfile; break;
|
||||
case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = hp48_writefile; break;
|
||||
case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = hp48_deletefile; break;
|
||||
|
||||
case IMGTOOLINFO_INT_PARTITION_EXTRA_BYTES: info->i = sizeof(hp48_partition); break;
|
||||
case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(hp48_directory); break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void hp48_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
||||
case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "hp48"); break;
|
||||
case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "HP48 SX/GX memory card"); break;
|
||||
case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break;
|
||||
case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "crd"); break;
|
||||
case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), hp48_create_optionspec); break;
|
||||
|
||||
case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: info->createimage_optguide = &hp48_create_optionguide; break;
|
||||
|
||||
/* --- the following bits of info are returned as pointers to data or functions --- */
|
||||
case IMGTOOLINFO_PTR_OPEN: info->open = hp48_open; break;
|
||||
case IMGTOOLINFO_PTR_CREATE: info->create = hp48_create; break;
|
||||
case IMGTOOLINFO_PTR_CLOSE: info->close = hp48_close; break;
|
||||
case IMGTOOLINFO_PTR_LIST_PARTITIONS: info->list_partitions = hp48_list_partitions; break;
|
||||
|
||||
case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(hp48_card); break;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,384 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Raphael Nabet
|
||||
/****************************************************************************
|
||||
|
||||
macbin.cpp
|
||||
|
||||
MacBinary filter for use with Mac and ProDOS drivers
|
||||
|
||||
*****************************************************************************
|
||||
|
||||
MacBinary file format
|
||||
|
||||
Offset Length Description
|
||||
------ ------ -----------
|
||||
0 1 [I] Magic byte (0x00)
|
||||
1 64 [I] File name (Pascal String)
|
||||
65 4 [I] File Type Code
|
||||
69 4 [I] File Creator Code
|
||||
73 1 [I] Finder Flags (bits 15-8)
|
||||
74 1 [I] Magic byte (0x00)
|
||||
75 2 [I] File Vertical Position
|
||||
77 2 [I] File Horizontal Position
|
||||
79 2 [I] Window/Folder ID
|
||||
81 1 [I] Protected (bit 0)
|
||||
82 1 [I] Magic byte (0x00)
|
||||
83 4 [I] Data Fork Length
|
||||
87 4 [I] Resource Fork Length
|
||||
91 4 [I] Creation Date
|
||||
95 4 [I] Last Modified Date
|
||||
99 2 [I] "Get Info" comment length
|
||||
101 1 [II] Finder Flags (bits 7-0)
|
||||
102 4 [III] MacBinary III Signature 'mBIN'
|
||||
106 1 [III] Script of Filename
|
||||
107 1 [III] Extended Finder Flags
|
||||
116 4 [II] Unpacked Length
|
||||
120 2 [II] Secondary Header Length
|
||||
122 1 [II] MacBinary II Version Number (II: 0x81, III: 0x82)
|
||||
123 1 [II] Minimum Compatible MacBinary II Version Number (0x81)
|
||||
124 2 [II] CRC of previous 124 bytes
|
||||
|
||||
For more information, consult http://www.lazerware.com/formats/macbinary.html
|
||||
|
||||
TODO: I believe that the script code is some sort of identifier identifying
|
||||
the character set used for the filename. If this is true, we are not
|
||||
handling that properly
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include "imgtool.h"
|
||||
#include "filter.h"
|
||||
|
||||
#include "macutil.h"
|
||||
|
||||
#include "formats/imageutl.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
|
||||
|
||||
static uint32_t pad128(uint32_t length)
|
||||
{
|
||||
if (length % 128)
|
||||
length += 128 - (length % 128);
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t macbinary_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)
|
||||
{
|
||||
static const uint32_t attrs[] =
|
||||
{
|
||||
IMGTOOLATTR_TIME_CREATED,
|
||||
IMGTOOLATTR_TIME_LASTMODIFIED,
|
||||
IMGTOOLATTR_INT_MAC_TYPE,
|
||||
IMGTOOLATTR_INT_MAC_CREATOR,
|
||||
IMGTOOLATTR_INT_MAC_FINDERFLAGS,
|
||||
IMGTOOLATTR_INT_MAC_COORDX,
|
||||
IMGTOOLATTR_INT_MAC_COORDY,
|
||||
IMGTOOLATTR_INT_MAC_FINDERFOLDER,
|
||||
IMGTOOLATTR_INT_MAC_SCRIPTCODE,
|
||||
IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS,
|
||||
0
|
||||
};
|
||||
imgtoolerr_t err;
|
||||
uint8_t header[128];
|
||||
const char *basename;
|
||||
|
||||
uint32_t type_code = 0x3F3F3F3F;
|
||||
uint32_t creator_code = 0x3F3F3F3F;
|
||||
uint16_t finder_flags = 0;
|
||||
uint16_t coord_x = 0;
|
||||
uint16_t coord_y = 0;
|
||||
uint16_t finder_folder = 0;
|
||||
uint8_t script_code = 0;
|
||||
uint8_t extended_flags = 0;
|
||||
|
||||
uint32_t creation_time = 0;
|
||||
uint32_t lastmodified_time = 0;
|
||||
imgtool_attribute attr_values[10];
|
||||
|
||||
// get the forks
|
||||
std::vector<imgtool::fork_entry> fork_entries;
|
||||
err = partition.list_file_forks(filename, fork_entries);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
const imgtool::fork_entry *data_fork = nullptr;
|
||||
const imgtool::fork_entry *resource_fork = nullptr;
|
||||
for (const auto &entry : fork_entries)
|
||||
{
|
||||
switch (entry.type())
|
||||
{
|
||||
case imgtool::fork_entry::type_t::DATA:
|
||||
data_fork = &entry;
|
||||
break;
|
||||
case imgtool::fork_entry::type_t::RESOURCE:
|
||||
resource_fork = &entry;
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* get the attributes */
|
||||
err = partition.get_file_attributes(filename, attrs, attr_values);
|
||||
if (err && (ERRORCODE(err) != IMGTOOLERR_UNIMPLEMENTED))
|
||||
return err;
|
||||
if (err == IMGTOOLERR_SUCCESS)
|
||||
{
|
||||
creation_time = mac_setup_time(attr_values[0].t);
|
||||
lastmodified_time = mac_setup_time(attr_values[1].t);
|
||||
type_code = attr_values[2].i;
|
||||
creator_code = attr_values[3].i;
|
||||
finder_flags = attr_values[4].i;
|
||||
coord_x = attr_values[5].i;
|
||||
coord_y = attr_values[6].i;
|
||||
finder_folder = attr_values[7].i;
|
||||
script_code = attr_values[8].i;
|
||||
extended_flags = attr_values[9].i;
|
||||
}
|
||||
|
||||
memset(header, 0, sizeof(header));
|
||||
|
||||
/* place filename */
|
||||
basename = filename;
|
||||
while(basename[strlen(basename) + 1])
|
||||
basename += strlen(basename) + 1;
|
||||
pascal_from_c_string((unsigned char *) &header[1], 64, basename);
|
||||
|
||||
place_integer_be(header, 65, 4, type_code);
|
||||
place_integer_be(header, 69, 4, creator_code);
|
||||
place_integer_be(header, 73, 1, (finder_flags >> 8) & 0xFF);
|
||||
place_integer_be(header, 75, 2, coord_x);
|
||||
place_integer_be(header, 77, 2, coord_y);
|
||||
place_integer_be(header, 79, 2, finder_folder);
|
||||
place_integer_be(header, 83, 4, data_fork ? data_fork->size() : 0);
|
||||
place_integer_be(header, 87, 4, resource_fork ? resource_fork->size() : 0);
|
||||
place_integer_be(header, 91, 4, creation_time);
|
||||
place_integer_be(header, 95, 4, lastmodified_time);
|
||||
place_integer_be(header, 101, 1, (finder_flags >> 0) & 0xFF);
|
||||
place_integer_be(header, 102, 4, 0x6D42494E);
|
||||
place_integer_be(header, 106, 1, script_code);
|
||||
place_integer_be(header, 107, 1, extended_flags);
|
||||
place_integer_be(header, 122, 1, 0x82);
|
||||
place_integer_be(header, 123, 1, 0x81);
|
||||
place_integer_be(header, 124, 2, ccitt_crc16(0, header, 124));
|
||||
|
||||
destf.write(header, sizeof(header));
|
||||
|
||||
if (data_fork)
|
||||
{
|
||||
err = partition.read_file(filename, "", destf, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
destf.fill(0, pad128(data_fork->size()));
|
||||
}
|
||||
|
||||
if (resource_fork)
|
||||
{
|
||||
err = partition.read_file(filename, "RESOURCE_FORK", destf, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
destf.fill(0, pad128(resource_fork->size()));
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t write_fork(imgtool::partition &partition, const char *filename, const char *fork,
|
||||
imgtool::stream &sourcef, uint64_t pos, uint64_t fork_len, util::option_resolution *opts)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
imgtool::stream::ptr mem_stream;
|
||||
size_t len;
|
||||
|
||||
if (fork_len > 0)
|
||||
{
|
||||
mem_stream = imgtool::stream::open_mem(nullptr, 0);
|
||||
if (!mem_stream)
|
||||
return IMGTOOLERR_OUTOFMEMORY;
|
||||
|
||||
sourcef.seek(pos, SEEK_SET);
|
||||
len = imgtool::stream::transfer(*mem_stream, sourcef, fork_len);
|
||||
if (len < fork_len)
|
||||
mem_stream->fill(0, fork_len);
|
||||
|
||||
mem_stream->seek(0, SEEK_SET);
|
||||
err = partition.write_file(filename, fork, *mem_stream, opts, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t macbinary_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts)
|
||||
{
|
||||
static const uint32_t attrs[] =
|
||||
{
|
||||
IMGTOOLATTR_TIME_CREATED,
|
||||
IMGTOOLATTR_TIME_LASTMODIFIED,
|
||||
IMGTOOLATTR_INT_MAC_TYPE,
|
||||
IMGTOOLATTR_INT_MAC_CREATOR,
|
||||
IMGTOOLATTR_INT_MAC_FINDERFLAGS,
|
||||
IMGTOOLATTR_INT_MAC_COORDX,
|
||||
IMGTOOLATTR_INT_MAC_COORDY,
|
||||
IMGTOOLATTR_INT_MAC_FINDERFOLDER,
|
||||
IMGTOOLATTR_INT_MAC_SCRIPTCODE,
|
||||
IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS,
|
||||
0
|
||||
};
|
||||
imgtoolerr_t err;
|
||||
imgtool::image *image = &partition.image();
|
||||
uint8_t header[128];
|
||||
uint32_t datafork_size;
|
||||
uint32_t resourcefork_size;
|
||||
uint64_t total_size;
|
||||
uint32_t creation_time;
|
||||
uint32_t lastmodified_time;
|
||||
//int version;
|
||||
imgtool_attribute attr_values[10];
|
||||
|
||||
uint32_t type_code;
|
||||
uint32_t creator_code;
|
||||
uint16_t finder_flags;
|
||||
uint16_t coord_x;
|
||||
uint16_t coord_y;
|
||||
uint16_t finder_folder;
|
||||
uint8_t script_code = 0;
|
||||
uint8_t extended_flags = 0;
|
||||
|
||||
/* read in the header */
|
||||
memset(header, 0, sizeof(header));
|
||||
sourcef.read(header, sizeof(header));
|
||||
|
||||
/* check magic and zero fill bytes */
|
||||
if (header[0] != 0x00)
|
||||
return IMGTOOLERR_CORRUPTFILE;
|
||||
if (header[74] != 0x00)
|
||||
return IMGTOOLERR_CORRUPTFILE;
|
||||
if (header[82] != 0x00)
|
||||
return IMGTOOLERR_CORRUPTFILE;
|
||||
|
||||
datafork_size = pick_integer_be(header, 83, 4);
|
||||
resourcefork_size = pick_integer_be(header, 87, 4);
|
||||
total_size = sourcef.size();
|
||||
|
||||
/* size of a MacBinary header is always 128 bytes */
|
||||
if (total_size - pad128(datafork_size) - pad128(resourcefork_size) != 128)
|
||||
return IMGTOOLERR_CORRUPTFILE;
|
||||
|
||||
/* check filename length byte */
|
||||
if ((header[1] <= 0x00) || (header[1] > 0x3F))
|
||||
return IMGTOOLERR_CORRUPTFILE;
|
||||
|
||||
/* check the CRC */
|
||||
if (pick_integer_be(header, 124, 2) != ccitt_crc16(0, header, 124))
|
||||
{
|
||||
/* the CRC does not match; this file is MacBinary I */
|
||||
//version = 1;
|
||||
}
|
||||
else if (pick_integer_be(header, 102, 4) != 0x6D42494E)
|
||||
{
|
||||
/* did not see 'mBIN'; this file is MacBinary II */
|
||||
if (header[122] < 0x81)
|
||||
return IMGTOOLERR_CORRUPTFILE;
|
||||
if (header[123] < 0x81)
|
||||
return IMGTOOLERR_CORRUPTFILE;
|
||||
//version = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we did see 'mBIN'; this file is MacBinary III */
|
||||
if (header[122] < 0x82)
|
||||
return IMGTOOLERR_CORRUPTFILE;
|
||||
if (header[123] < 0x81)
|
||||
return IMGTOOLERR_CORRUPTFILE;
|
||||
//version = 3;
|
||||
}
|
||||
|
||||
type_code = pick_integer_be(header, 65, 4);
|
||||
creator_code = pick_integer_be(header, 69, 4);
|
||||
finder_flags = pick_integer_be(header, 73, 1) << 8;
|
||||
coord_x = pick_integer_be(header, 75, 2);
|
||||
coord_y = pick_integer_be(header, 77, 2);
|
||||
finder_folder = pick_integer_be(header, 79, 2);
|
||||
creation_time = pick_integer_be(header, 91, 4);
|
||||
lastmodified_time = pick_integer_be(header, 95, 4);
|
||||
|
||||
if (image)
|
||||
{
|
||||
/* write out both forks */
|
||||
err = write_fork(partition, filename, "", sourcef, sizeof(header), datafork_size, opts);
|
||||
if (err)
|
||||
return err;
|
||||
err = write_fork(partition, filename, "RESOURCE_FORK", sourcef, sizeof(header) + pad128(datafork_size), resourcefork_size, opts);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* set up attributes */
|
||||
attr_values[0].t = mac_crack_time(creation_time).to_time_t();
|
||||
attr_values[1].t = mac_crack_time(lastmodified_time).to_time_t();
|
||||
attr_values[2].i = type_code;
|
||||
attr_values[3].i = creator_code;
|
||||
attr_values[4].i = finder_flags;
|
||||
attr_values[5].i = coord_x;
|
||||
attr_values[6].i = coord_y;
|
||||
attr_values[7].i = finder_folder;
|
||||
attr_values[8].i = script_code;
|
||||
attr_values[9].i = extended_flags;
|
||||
|
||||
err = partition.put_file_attributes(filename, attrs, attr_values);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// this was completely broken - it was calling macbinary_writefile() with a nullptr partition
|
||||
#if 0
|
||||
static imgtoolerr_t macbinary_checkstream(imgtool::stream &stream, imgtool_suggestion_viability_t *viability)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
|
||||
err = macbinary_writefile(NULL, NULL, NULL, stream, NULL);
|
||||
if (err == IMGTOOLERR_CORRUPTFILE)
|
||||
{
|
||||
/* the filter returned corrupt; this is not a valid file */
|
||||
*viability = SUGGESTION_END;
|
||||
err = IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
else if (err == IMGTOOLERR_SUCCESS)
|
||||
{
|
||||
/* success; lets recommend this filter */
|
||||
*viability = SUGGESTION_RECOMMENDED;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
void filter_macbinary_getinfo(uint32_t state, union filterinfo *info)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case FILTINFO_STR_NAME: info->s = "macbinary"; break;
|
||||
case FILTINFO_STR_HUMANNAME: info->s = "MacBinary"; break;
|
||||
case FILTINFO_STR_EXTENSION: info->s = "bin"; break;
|
||||
case FILTINFO_PTR_READFILE: info->read_file = macbinary_readfile; break;
|
||||
case FILTINFO_PTR_WRITEFILE: info->write_file = macbinary_writefile; break;
|
||||
//case FILTINFO_PTR_CHECKSTREAM: info->check_stream = macbinary_checkstream; break;
|
||||
}
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Raphael Nabet
|
||||
/****************************************************************************
|
||||
|
||||
macutil.cpp
|
||||
|
||||
Imgtool Utility code for manipulating certain Apple/Mac data structures
|
||||
and conventions
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include "macutil.h"
|
||||
#include "filter.h"
|
||||
|
||||
#include "timeconv.h"
|
||||
|
||||
|
||||
typedef util::arbitrary_clock<std::uint32_t, 1904, 1, 1, 0, 0, 0, std::ratio<1, 1> > classic_mac_clock;
|
||||
|
||||
//-------------------------------------------------
|
||||
// mac_crack_time
|
||||
//-------------------------------------------------
|
||||
|
||||
imgtool::datetime mac_crack_time(uint32_t t)
|
||||
{
|
||||
classic_mac_clock::duration d(t);
|
||||
std::chrono::time_point<classic_mac_clock> tp(d);
|
||||
return imgtool::datetime(imgtool::datetime::datetime_type::LOCAL, tp);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// mac_setup_time
|
||||
//-------------------------------------------------
|
||||
|
||||
uint32_t mac_setup_time(const imgtool::datetime &t)
|
||||
{
|
||||
auto mac_time_point = classic_mac_clock::from_arbitrary_time_point(t.time_point());
|
||||
return mac_time_point.time_since_epoch().count();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// mac_setup_time
|
||||
//-------------------------------------------------
|
||||
|
||||
uint32_t mac_setup_time(time_t t)
|
||||
{
|
||||
imgtool::datetime dt(imgtool::datetime::datetime_type::LOCAL, t);
|
||||
return mac_setup_time(dt);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// mac_time_now
|
||||
//-------------------------------------------------
|
||||
|
||||
uint32_t mac_time_now(void)
|
||||
{
|
||||
imgtool::datetime dt = imgtool::datetime::now(imgtool::datetime::datetime_type::LOCAL);
|
||||
return mac_setup_time(dt);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// mac_identify_fork
|
||||
//-------------------------------------------------
|
||||
|
||||
imgtoolerr_t mac_identify_fork(const char *fork_string, mac_fork_t *fork_num)
|
||||
{
|
||||
if (!strcmp(fork_string, ""))
|
||||
*fork_num = MAC_FORK_DATA;
|
||||
else if (!strcmp(fork_string, "RESOURCE_FORK"))
|
||||
*fork_num = MAC_FORK_RESOURCE;
|
||||
else
|
||||
return IMGTOOLERR_FORKNOTFOUND;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void mac_suggest_transfer(mac_filecategory_t file_category, imgtool_transfer_suggestion *suggestions, size_t suggestions_length)
|
||||
{
|
||||
suggestions[0].viability = (file_category == MAC_FILECATEGORY_FORKED) ? SUGGESTION_RECOMMENDED : SUGGESTION_POSSIBLE;
|
||||
suggestions[0].filter = filter_macbinary_getinfo;
|
||||
suggestions[0].fork = NULL;
|
||||
suggestions[0].description = NULL;
|
||||
|
||||
suggestions[1].viability = (file_category == MAC_FILECATEGORY_TEXT) ? SUGGESTION_RECOMMENDED : SUGGESTION_POSSIBLE;
|
||||
suggestions[1].filter = filter_eoln_getinfo;
|
||||
suggestions[1].fork = NULL;
|
||||
suggestions[1].description = NULL;
|
||||
|
||||
suggestions[2].viability = (file_category == MAC_FILECATEGORY_DATA) ? SUGGESTION_RECOMMENDED : SUGGESTION_POSSIBLE;
|
||||
suggestions[2].filter = NULL;
|
||||
suggestions[2].fork = "";
|
||||
suggestions[2].description = "Raw (data fork)";
|
||||
|
||||
suggestions[3].viability = SUGGESTION_POSSIBLE;
|
||||
suggestions[3].filter = NULL;
|
||||
suggestions[3].fork = "RESOURCE_FORK";
|
||||
suggestions[3].description = "Raw (resource fork)";
|
||||
}
|
||||
|
||||
|
||||
|
||||
void pascal_from_c_string(unsigned char *pstring, size_t pstring_len, const char *cstring)
|
||||
{
|
||||
size_t cstring_len, i;
|
||||
|
||||
cstring_len = strlen(cstring);
|
||||
pstring[0] = std::min(cstring_len, pstring_len - 1);
|
||||
|
||||
for (i = 0; i < pstring[0]; i++)
|
||||
pstring[1 + i] = cstring[i];
|
||||
while(i < pstring_len - 1)
|
||||
pstring[1 + i++] = '\0';
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Raphael Nabet
|
||||
/****************************************************************************
|
||||
|
||||
macutil.h
|
||||
|
||||
Imgtool Utility code for manipulating certain Apple/Mac data structures
|
||||
and conventions
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MACUTIL_H
|
||||
#define MACUTIL_H
|
||||
|
||||
#include "library.h"
|
||||
|
||||
enum mac_fork_t
|
||||
{
|
||||
MAC_FORK_DATA,
|
||||
MAC_FORK_RESOURCE
|
||||
};
|
||||
|
||||
enum mac_filecategory_t
|
||||
{
|
||||
MAC_FILECATEGORY_DATA,
|
||||
MAC_FILECATEGORY_TEXT,
|
||||
MAC_FILECATEGORY_FORKED
|
||||
};
|
||||
|
||||
|
||||
/* converting Classic Mac OS time <==> Imgtool time */
|
||||
imgtool::datetime mac_crack_time(uint32_t t);
|
||||
uint32_t mac_setup_time(const imgtool::datetime &t);
|
||||
uint32_t mac_setup_time(time_t t);
|
||||
uint32_t mac_time_now(void);
|
||||
|
||||
imgtoolerr_t mac_identify_fork(const char *fork_string, mac_fork_t *fork_num);
|
||||
|
||||
void mac_suggest_transfer(mac_filecategory_t file_category, imgtool_transfer_suggestion *suggestions, size_t suggestions_length);
|
||||
|
||||
void pascal_from_c_string(unsigned char *pstring, size_t pstring_len, const char *cstring);
|
||||
|
||||
#endif /* MACUTIL_H */
|
File diff suppressed because it is too large
Load Diff
@ -1,176 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Raphael Nabet
|
||||
/****************************************************************************
|
||||
|
||||
pc_flop.c
|
||||
|
||||
PC floppies
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "imgtool.h"
|
||||
#include "fat.h"
|
||||
#include "iflopimg.h"
|
||||
|
||||
#include "formats/imageutl.h"
|
||||
#include "formats/pc_dsk_legacy.h"
|
||||
|
||||
#include "opresolv.h"
|
||||
|
||||
#define FAT_SECLEN 512
|
||||
|
||||
|
||||
static imgtoolerr_t fat_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
uint32_t tracks, heads, sectors;
|
||||
uint8_t buffer[FAT_SECLEN];
|
||||
imgtool_class imgclass = { fat_get_info };
|
||||
imgtoolerr_t (*fat_partition_create)(imgtool::image &image, uint64_t first_block, uint64_t block_count);
|
||||
|
||||
tracks = opts->lookup_int('T');
|
||||
heads = opts->lookup_int('H');
|
||||
sectors = opts->lookup_int('S');
|
||||
|
||||
/* set up just enough of a boot sector to specify geometry */
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
place_integer_le(buffer, 24, 2, sectors);
|
||||
place_integer_le(buffer, 26, 2, heads);
|
||||
place_integer_le(buffer, 19, 2, (uint16_t) (((uint64_t) tracks * heads * sectors) >> 0));
|
||||
place_integer_le(buffer, 32, 4, (uint16_t) (((uint64_t) tracks * heads * sectors) >> 16));
|
||||
err = image.write_block(0, buffer);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
/* load fat_partition_create */
|
||||
fat_partition_create = (imgtoolerr_t (*)(imgtool::image &, uint64_t, uint64_t))
|
||||
imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_CREATE_PARTITION);
|
||||
|
||||
/* actually create the partition */
|
||||
err = fat_partition_create(image, 0, ((uint64_t) tracks) * heads * sectors);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
done:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t fat_image_get_geometry(imgtool::image &image, uint32_t *tracks, uint32_t *heads, uint32_t *sectors)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
uint64_t total_sectors;
|
||||
uint8_t buffer[FAT_SECLEN];
|
||||
|
||||
err = image.read_block(0, buffer);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
total_sectors = pick_integer_le(buffer, 19, 2)
|
||||
| (pick_integer_le(buffer, 32, 4) << 16);
|
||||
|
||||
*sectors = pick_integer_le(buffer, 24, 2);
|
||||
*heads = pick_integer_le(buffer, 26, 2);
|
||||
*tracks = total_sectors / *heads / *sectors;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t fat_get_sector_position(imgtool::image &image, uint32_t sector_index,
|
||||
uint32_t *track, uint32_t *head, uint32_t *sector)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
uint32_t tracks, heads, sectors;
|
||||
|
||||
if (sector_index == 0)
|
||||
{
|
||||
/* special case */
|
||||
*head = 0;
|
||||
*track = 0;
|
||||
*sector = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = image.get_geometry(&tracks, &heads, §ors);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*track = sector_index / sectors / heads;
|
||||
*head = (sector_index / sectors) % heads;
|
||||
*sector = 1 + (sector_index % sectors);
|
||||
}
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t fat_image_readblock(imgtool::image &image, void *buffer, uint64_t block)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
floperr_t ferr;
|
||||
uint32_t track, head, sector;
|
||||
uint32_t block_size;
|
||||
|
||||
err = image.get_block_size(block_size);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = fat_get_sector_position(image, block, &track, &head, §or);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ferr = floppy_read_sector(imgtool_floppy(image), head, track, sector, 0, buffer, block_size);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t fat_image_writeblock(imgtool::image &image, const void *buffer, uint64_t block)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
floperr_t ferr;
|
||||
uint32_t track, head, sector;
|
||||
uint32_t block_size;
|
||||
|
||||
err = image.get_block_size(block_size);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = fat_get_sector_position(image, block, &track, &head, §or);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ferr = floppy_write_sector(imgtool_floppy(image), head, track, sector, 0, buffer, block_size, 0); /* TODO: pass ddam argument from imgtool */
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void pc_floppy_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
||||
case IMGTOOLINFO_INT_BLOCK_SIZE: info->i = FAT_SECLEN; break;
|
||||
|
||||
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
||||
case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "fat"); break;
|
||||
case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "FAT format"); break;
|
||||
|
||||
/* --- the following bits of info are returned as pointers to data or functions --- */
|
||||
case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break;
|
||||
case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = fat_image_create; break;
|
||||
case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_pc; break;
|
||||
case IMGTOOLINFO_PTR_READ_BLOCK: info->read_block = fat_image_readblock; break;
|
||||
case IMGTOOLINFO_PTR_WRITE_BLOCK: info->write_block = fat_image_writeblock; break;
|
||||
case IMGTOOLINFO_PTR_GET_GEOMETRY: info->get_geometry = fat_image_get_geometry; break;
|
||||
|
||||
default: fat_get_info(imgclass, state, info); break;
|
||||
}
|
||||
}
|
@ -1,470 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Raphael Nabet
|
||||
/****************************************************************************
|
||||
|
||||
pc_hard.c
|
||||
|
||||
PC hard drives
|
||||
|
||||
*****************************************************************************
|
||||
|
||||
Master boot record format:
|
||||
|
||||
Offset Length Description
|
||||
------ ------ -----------
|
||||
0 446 Boot machine code
|
||||
446 16 Partion #1 info
|
||||
462 16 Partion #2 info
|
||||
478 16 Partion #3 info
|
||||
494 16 Partion #4 info
|
||||
510 2 Magic bytes (0x55 0xAA)
|
||||
|
||||
|
||||
Partition info format:
|
||||
|
||||
Offset Length Description
|
||||
------ ------ -----------
|
||||
0 1 Bootable (0x80=bootable 0x00=not bootable)
|
||||
1 1 Starting head
|
||||
2 1 Starting sector (bits 5-0) and high bits of starting track (bits 6-5)
|
||||
3 1 Low bits of starting track
|
||||
4 1 Partition type:
|
||||
0x00 Unused
|
||||
0x?1 FAT12 (0-15 MB)
|
||||
0x?2 XENIX
|
||||
0x?4 FAT16 (16-32 MB)
|
||||
0x?6 FAT16` (32 MB-2 GB)
|
||||
0x?7 HPFS or NTFS
|
||||
0x?A Boot Manager
|
||||
0x?B FAT32 (512 MB-2 TB)
|
||||
0x?C FAT32 (512 MB-2 TB LBA)
|
||||
0x1? OS/2 Boot manager/Win95 hidden
|
||||
0xC? DR-DOS secured partition
|
||||
0xD? Multiuser DOS secured partition
|
||||
0xE? SpeedStor extended partition
|
||||
5 1 Ending head
|
||||
6 1 Ending sector (bits 5-0) and high bits of ending track (bits 6-5)
|
||||
7 1 Low bits of ending track
|
||||
8 4 Sector index of beginning of partition
|
||||
12 4 Total sectors in partition
|
||||
|
||||
****************************************************************************/
|
||||
#include "imgtool.h"
|
||||
#include "formats/imageutl.h"
|
||||
#include "imghd.h"
|
||||
|
||||
#include "opresolv.h"
|
||||
|
||||
#define FAT_SECLEN 512
|
||||
|
||||
OPTION_GUIDE_START( pc_chd_create_optionguide )
|
||||
OPTION_INT('T', "cylinders", "Cylinders" )
|
||||
OPTION_INT('H', "heads", "Heads" )
|
||||
OPTION_INT('S', "sectors", "Sectors" )
|
||||
OPTION_GUIDE_END
|
||||
|
||||
static const char pc_chd_create_optionspec[] = "H1-[16];S1-[32]-63;T10/20/30/40/50/60/70/80/90/[100]/110/120/130/140/150/160/170/180/190/200";
|
||||
|
||||
static const char fat8_string[8] = { 'F', 'A', 'T', ' ', ' ', ' ', ' ', ' ' };
|
||||
static const char fat12_string[8] = { 'F', 'A', 'T', '1', '2', ' ', ' ', ' ' };
|
||||
static const char fat16_string[8] = { 'F', 'A', 'T', '1', '6', ' ', ' ', ' ' };
|
||||
//static const char fat32_string[8] = { 'F', 'A', 'T', '3', '2', ' ', ' ', ' ' };
|
||||
|
||||
/* imports from fat.c */
|
||||
extern void fat_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info);
|
||||
|
||||
|
||||
struct pc_chd_image_info
|
||||
{
|
||||
struct mess_hard_disk_file hard_disk;
|
||||
|
||||
struct
|
||||
{
|
||||
unsigned int corrupt : 1;
|
||||
uint8_t partition_type;
|
||||
uint32_t fat_bits;
|
||||
uint32_t starting_track;
|
||||
uint32_t starting_head;
|
||||
uint32_t starting_sector;
|
||||
uint32_t ending_track;
|
||||
uint32_t ending_head;
|
||||
uint32_t ending_sector;
|
||||
uint32_t sector_index;
|
||||
uint32_t total_sectors;
|
||||
} partitions[4];
|
||||
};
|
||||
|
||||
|
||||
|
||||
static pc_chd_image_info *pc_chd_get_image_info(imgtool::image &image)
|
||||
{
|
||||
return (pc_chd_image_info *) image.extra_bytes();
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void pc_chd_locate_block(imgtool::image &image, uint64_t block, uint32_t *cylinder, uint32_t *head, uint32_t *sector)
|
||||
{
|
||||
pc_chd_image_info *info;
|
||||
const hard_disk_info *hd_info;
|
||||
|
||||
info = pc_chd_get_image_info(image);
|
||||
hd_info = imghd_get_header(&info->hard_disk);
|
||||
|
||||
*sector = block % hd_info->sectors;
|
||||
*head = (block / hd_info->sectors) % hd_info->heads;
|
||||
*cylinder = block / hd_info->sectors / hd_info->heads;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t pc_chd_partition_create(imgtool::image &image, int partition_index, uint64_t first_block, uint64_t block_count)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
uint8_t header_block[FAT_SECLEN];
|
||||
uint8_t partition_block[FAT_SECLEN];
|
||||
uint8_t partition_type;
|
||||
uint8_t *fat_type;
|
||||
uint8_t *partition_entry;
|
||||
uint32_t first_cylinder, first_head, first_sector;
|
||||
uint32_t last_cylinder, last_head, last_sector;
|
||||
imgtool_class imgclass = { fat_get_info };
|
||||
imgtoolerr_t (*fat_partition_create)(imgtool::image &image, uint64_t first_block, uint64_t block_count);
|
||||
|
||||
/* sanity checks */
|
||||
assert((partition_index >= 0) && (partition_index <= 3));
|
||||
|
||||
/* compute geometry */
|
||||
pc_chd_locate_block(image, first_block, &first_cylinder, &first_head, &first_sector);
|
||||
pc_chd_locate_block(image, first_block + block_count - 1, &last_cylinder, &last_head, &last_sector);
|
||||
|
||||
/* load fat_partition_create */
|
||||
fat_partition_create = (imgtoolerr_t (*)(imgtool::image &, uint64_t, uint64_t))
|
||||
imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_CREATE_PARTITION);
|
||||
|
||||
/* first create the actual partition */
|
||||
err = fat_partition_create(image, first_block, block_count);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
/* read the first block of the partition, to determine the type of FAT */
|
||||
err = image.read_block(first_block, partition_block);
|
||||
if (err)
|
||||
goto done;
|
||||
fat_type = &partition_block[54];
|
||||
if (!memcmp(fat_type, fat8_string, sizeof(fat8_string)))
|
||||
partition_type = 0x01;
|
||||
else if (!memcmp(fat_type, fat12_string, sizeof(fat12_string)))
|
||||
partition_type = 0x01;
|
||||
else if ((!memcmp(fat_type, fat16_string, sizeof(fat16_string))) && (block_count < 32*1024*1024/FAT_SECLEN))
|
||||
partition_type = 0x04;
|
||||
else if ((!memcmp(fat_type, fat16_string, sizeof(fat16_string))) && (block_count >= 32*1024*1024/FAT_SECLEN))
|
||||
partition_type = 0x06;
|
||||
else
|
||||
partition_type = 0x0B;
|
||||
|
||||
/* read the partition header */
|
||||
err = image.read_block(0, header_block);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
/* fill out the partition entry */
|
||||
partition_entry = &header_block[446 + (partition_index * 16)];
|
||||
place_integer_le(partition_entry, 0, 1, 0x80);
|
||||
place_integer_le(partition_entry, 1, 1, first_head);
|
||||
place_integer_le(partition_entry, 2, 1, ((first_sector & 0x3F) | (first_cylinder >> 8 << 2)));
|
||||
place_integer_le(partition_entry, 3, 1, first_cylinder);
|
||||
place_integer_le(partition_entry, 4, 1, partition_type);
|
||||
place_integer_le(partition_entry, 5, 1, last_head);
|
||||
place_integer_le(partition_entry, 6, 1, ((last_sector & 0x3F) | (last_cylinder >> 8 << 2)));
|
||||
place_integer_le(partition_entry, 7, 1, last_cylinder);
|
||||
place_integer_le(partition_entry, 8, 4, first_block);
|
||||
place_integer_le(partition_entry, 12, 4, block_count);
|
||||
|
||||
/* write the partition header */
|
||||
err = image.write_block(0, header_block);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
done:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t pc_chd_read_partition_header(imgtool::image &image)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
const uint8_t *partition_info;
|
||||
pc_chd_image_info *info;
|
||||
uint8_t buffer[FAT_SECLEN];
|
||||
|
||||
info = pc_chd_get_image_info(image);
|
||||
|
||||
/* read the initial block */
|
||||
err = image.read_block(0, buffer);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* magic bytes present? */
|
||||
if ((buffer[510] != 0x55) || (buffer[511] != 0xAA))
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
|
||||
for (int i = 0; i < std::size(info->partitions); i++)
|
||||
{
|
||||
partition_info = &buffer[446 + i * 16];
|
||||
|
||||
info->partitions[i].partition_type = partition_info[4];
|
||||
info->partitions[i].starting_head = partition_info[1];
|
||||
info->partitions[i].starting_track = ((partition_info[2] << 2) & 0xFF00) | partition_info[3];
|
||||
info->partitions[i].starting_sector = partition_info[2] & 0x3F;
|
||||
info->partitions[i].ending_head = partition_info[5];
|
||||
info->partitions[i].ending_track = ((partition_info[6] << 2) & 0xFF00) | partition_info[7];
|
||||
info->partitions[i].ending_sector = partition_info[6] & 0x3F;
|
||||
|
||||
info->partitions[i].sector_index = pick_integer_le(partition_info, 8, 4);
|
||||
info->partitions[i].total_sectors = pick_integer_le(partition_info, 12, 4);
|
||||
|
||||
if (info->partitions[i].starting_track > info->partitions[i].ending_track)
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
}
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t pc_chd_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
uint32_t cylinders, heads, sectors;
|
||||
pc_chd_image_info *info;
|
||||
uint8_t header_block[FAT_SECLEN];
|
||||
|
||||
cylinders = opts->lookup_int('T');
|
||||
heads = opts->lookup_int('H');
|
||||
sectors = opts->lookup_int('S');
|
||||
|
||||
info = pc_chd_get_image_info(image);
|
||||
|
||||
/* create the hard disk image */
|
||||
err = imghd_create(*stream, 0, cylinders, heads, sectors, FAT_SECLEN);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = imghd_open(*stream, &info->hard_disk);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
/* set up partition header block */
|
||||
memset(header_block, 0, sizeof(header_block));
|
||||
header_block[510] = 0x55;
|
||||
header_block[511] = 0xAA;
|
||||
err = image.write_block(0, header_block);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = pc_chd_partition_create(image, 0, 1, cylinders * heads * sectors - 1);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = pc_chd_read_partition_header(image);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
done:
|
||||
if (err)
|
||||
imghd_close(&info->hard_disk);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t pc_chd_image_open(imgtool::image &image, imgtool::stream::ptr &&stream)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
pc_chd_image_info *info;
|
||||
|
||||
info = pc_chd_get_image_info(image);
|
||||
|
||||
/* open the hard drive */
|
||||
err = imghd_open(*stream, &info->hard_disk);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = pc_chd_read_partition_header(image);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void pc_chd_image_close(imgtool::image &image)
|
||||
{
|
||||
pc_chd_image_info *info;
|
||||
info = pc_chd_get_image_info(image);
|
||||
imghd_close(&info->hard_disk);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t pc_chd_image_get_geometry(imgtool::image &image, uint32_t *tracks, uint32_t *heads, uint32_t *sectors)
|
||||
{
|
||||
pc_chd_image_info *info;
|
||||
const hard_disk_info *hd_info;
|
||||
|
||||
info = pc_chd_get_image_info(image);
|
||||
hd_info = imghd_get_header(&info->hard_disk);
|
||||
|
||||
*tracks = hd_info->cylinders;
|
||||
*heads = hd_info->heads;
|
||||
*sectors = hd_info->sectors;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static uint32_t pc_chd_calc_lbasector(pc_chd_image_info &info, uint32_t track, uint32_t head, uint32_t sector)
|
||||
{
|
||||
uint32_t lbasector;
|
||||
const hard_disk_info *hd_info;
|
||||
|
||||
hd_info = imghd_get_header(&info.hard_disk);
|
||||
lbasector = track;
|
||||
lbasector *= hd_info->heads;
|
||||
lbasector += head;
|
||||
lbasector *= hd_info->sectors;
|
||||
lbasector += sector;
|
||||
return lbasector;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t pc_chd_image_readsector(imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, std::vector<uint8_t> &buffer)
|
||||
{
|
||||
pc_chd_image_info *info = pc_chd_get_image_info(image);
|
||||
|
||||
// get the sector size and resize the buffer
|
||||
uint32_t sector_size = imghd_get_header(&info->hard_disk)->sectorbytes;
|
||||
try { buffer.resize(sector_size); }
|
||||
catch (std::bad_alloc const &) { return IMGTOOLERR_OUTOFMEMORY; }
|
||||
|
||||
// read the data
|
||||
return imghd_read(&info->hard_disk,
|
||||
pc_chd_calc_lbasector(*info, track, head, sector),
|
||||
&buffer[0]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t pc_chd_image_writesector(imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, const void *buffer, size_t len, int ddam)
|
||||
{
|
||||
pc_chd_image_info *info;
|
||||
info = pc_chd_get_image_info(image);
|
||||
return imghd_write(&info->hard_disk,
|
||||
pc_chd_calc_lbasector(*info, track, head, sector),
|
||||
buffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t pc_chd_image_readblock(imgtool::image &image, void *buffer, uint64_t block)
|
||||
{
|
||||
pc_chd_image_info *info;
|
||||
info = pc_chd_get_image_info(image);
|
||||
return imghd_read(&info->hard_disk, block, buffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t pc_chd_image_writeblock(imgtool::image &image, const void *buffer, uint64_t block)
|
||||
{
|
||||
pc_chd_image_info *info;
|
||||
info = pc_chd_get_image_info(image);
|
||||
return imghd_write(&info->hard_disk, block, buffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static imgtoolerr_t pc_chd_list_partitions(imgtool::image &image, std::vector<imgtool::partition_info> &partitions)
|
||||
{
|
||||
pc_chd_image_info *info;
|
||||
size_t i;
|
||||
|
||||
info = pc_chd_get_image_info(image);
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
// what type of partition is this?
|
||||
imgtool_get_info partition_get_info;
|
||||
switch(info->partitions[i].partition_type)
|
||||
{
|
||||
case 0x00: /* Empty Partition */
|
||||
partition_get_info = nullptr;
|
||||
break;
|
||||
|
||||
case 0x01: /* FAT12 */
|
||||
case 0x04: /* FAT16 (-32 MB) */
|
||||
case 0x06: /* FAT16 (32+ MB) */
|
||||
case 0x0B: /* FAT32 */
|
||||
case 0x0C: /* FAT32 (LBA Mapped) */
|
||||
case 0x0E: /* FAT16 (LBA Mapped) */
|
||||
case 0x11: /* OS/2 FAT12 */
|
||||
case 0x14: /* OS/2 FAT16 (-32 MB) */
|
||||
case 0x16: /* OS/2 FAT16 (32+ MB) */
|
||||
case 0x1B: /* Hidden Win95 FAT32 */
|
||||
case 0x1C: /* Hidden Win95 FAT32 (LBA Mapped) */
|
||||
case 0x1D: /* Hidden Win95 FAT16 (LBA Mapped) */
|
||||
case 0xC1: /* DR-DOS FAT12 */
|
||||
case 0xC4: /* DR-DOS FAT16 (-32 MB) */
|
||||
case 0xC6: /* DR-DOS FAT16 (32+ MB) */
|
||||
case 0xD1: /* Old Multiuser DOS FAT12 */
|
||||
case 0xD4: /* Old Multiuser DOS FAT16 (-32 MB) */
|
||||
case 0xD6: /* Old Multiuser DOS FAT16 (32+ MB) */
|
||||
partition_get_info = fat_get_info;
|
||||
break;
|
||||
|
||||
default:
|
||||
partition_get_info = unknown_partition_get_info;
|
||||
break;
|
||||
}
|
||||
|
||||
partitions.emplace_back(
|
||||
partition_get_info,
|
||||
info->partitions[i].sector_index,
|
||||
info->partitions[i].total_sectors);
|
||||
}
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void pc_chd_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
||||
case IMGTOOLINFO_INT_BLOCK_SIZE: info->i = FAT_SECLEN; break;
|
||||
case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(pc_chd_image_info); break;
|
||||
case IMGTOOLINFO_INT_TRACKS_ARE_CALLED_CYLINDERS: info->i = 1; break;
|
||||
|
||||
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
||||
case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "pc_chd"); break;
|
||||
case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "PC CHD disk image"); break;
|
||||
case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "chd"); break;
|
||||
case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), pc_chd_create_optionspec); break;
|
||||
|
||||
/* --- the following bits of info are returned as pointers to data or functions --- */
|
||||
case IMGTOOLINFO_PTR_CREATE: info->create = pc_chd_image_create; break;
|
||||
case IMGTOOLINFO_PTR_OPEN: info->open = pc_chd_image_open; break;
|
||||
case IMGTOOLINFO_PTR_CLOSE: info->close = pc_chd_image_close; break;
|
||||
case IMGTOOLINFO_PTR_READ_SECTOR: info->read_sector = pc_chd_image_readsector; break;
|
||||
case IMGTOOLINFO_PTR_WRITE_SECTOR: info->write_sector = pc_chd_image_writesector; break;
|
||||
case IMGTOOLINFO_PTR_READ_BLOCK: info->read_block = pc_chd_image_readblock; break;
|
||||
case IMGTOOLINFO_PTR_WRITE_BLOCK: info->write_block = pc_chd_image_writeblock; break;
|
||||
case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: info->createimage_optguide = &pc_chd_create_optionguide; break;
|
||||
case IMGTOOLINFO_PTR_GET_GEOMETRY: info->get_geometry = pc_chd_image_get_geometry; break;
|
||||
case IMGTOOLINFO_PTR_LIST_PARTITIONS: info->list_partitions = pc_chd_list_partitions; break;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,698 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Sandro Ronco
|
||||
/***************************************************************************
|
||||
|
||||
Psion Organiser II Datapack
|
||||
|
||||
08/18/2010 Sandro Ronco
|
||||
|
||||
Known file types:
|
||||
0x00 invalid record
|
||||
0x01 - 0x7e deleted record
|
||||
0x7f invalid deleted record
|
||||
0x80 long record
|
||||
0x81 file name
|
||||
0x82 diary
|
||||
0x83 OPL/OB3 procedure
|
||||
0x84 RS232 setup
|
||||
0x85 - 0x8f reserved
|
||||
0x90 MAIN Record
|
||||
0x91 - 0xfe data records from files
|
||||
0xff invalid record
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "imgtool.h"
|
||||
|
||||
#include "opresolv.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#define MAXFILES 256
|
||||
|
||||
struct psion_file
|
||||
{
|
||||
char filename[9];
|
||||
uint8_t type;
|
||||
uint8_t id;
|
||||
|
||||
uint16_t name_rec;
|
||||
uint16_t data_rec;
|
||||
};
|
||||
|
||||
struct psion_pack
|
||||
{
|
||||
imgtool::stream *stream;
|
||||
|
||||
uint16_t eop;
|
||||
struct psion_file pack_index[MAXFILES];
|
||||
};
|
||||
|
||||
struct psion_iter
|
||||
{
|
||||
uint16_t index;
|
||||
};
|
||||
|
||||
static psion_pack *get_psion_pack(imgtool::image &image)
|
||||
{
|
||||
return (psion_pack*)image.extra_bytes();
|
||||
}
|
||||
|
||||
uint16_t head_checksum(uint8_t* data)
|
||||
{
|
||||
uint16_t checksum = 0;
|
||||
|
||||
for (int i=0; i<6; i+=2)
|
||||
checksum += (data[i]<<8 | data[i+1]);
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
uint16_t get_long_rec_size(imgtool::stream &stream)
|
||||
{
|
||||
uint8_t size_h, size_l;
|
||||
|
||||
stream.read(&size_h, 1);
|
||||
stream.read(&size_l, 1);
|
||||
|
||||
return (size_h<<8) | size_l;
|
||||
}
|
||||
|
||||
uint32_t update_pack_index(psion_pack *pack)
|
||||
{
|
||||
uint8_t data, type;
|
||||
uint16_t size;
|
||||
uint16_t index = 0;
|
||||
|
||||
memset(pack->pack_index, 0, sizeof(psion_file) * MAXFILES);
|
||||
|
||||
// start at the first record
|
||||
pack->stream->seek(0x10, SEEK_SET);
|
||||
|
||||
do
|
||||
{
|
||||
pack->stream->read(&data, 1);
|
||||
|
||||
if(data == 0xff)
|
||||
{
|
||||
pack->eop = pack->stream->tell() - 1;
|
||||
return true;
|
||||
}
|
||||
else if (data == 0x02)
|
||||
{
|
||||
// long record without name are ignored
|
||||
pack->stream->read(&data, 1);
|
||||
size = get_long_rec_size(*pack->stream);
|
||||
pack->stream->seek(size, SEEK_CUR);
|
||||
}
|
||||
else
|
||||
{
|
||||
pack->stream->read(&type, 1);
|
||||
|
||||
// deleted record are not listed
|
||||
if (type < 0x90 && (type & 0x80))
|
||||
{
|
||||
pack->pack_index[index].type = type;
|
||||
pack->stream->read(&pack->pack_index[index].filename, 8);
|
||||
pack->stream->read(&pack->pack_index[index].id, 1);
|
||||
pack->pack_index[index].name_rec = pack->stream->tell() - 11;
|
||||
|
||||
//check for data record
|
||||
pack->stream->read(&data, 1);
|
||||
if (data == 0x02)
|
||||
pack->pack_index[index].data_rec = pack->stream->tell() - 1;
|
||||
|
||||
pack->stream->seek(-1, SEEK_CUR);
|
||||
|
||||
index++;
|
||||
}
|
||||
else
|
||||
pack->stream->seek(data, SEEK_CUR);
|
||||
}
|
||||
|
||||
} while (pack->stream->size() > pack->stream->tell());
|
||||
|
||||
// corrupted image
|
||||
return false;
|
||||
}
|
||||
|
||||
int seek_next_record(imgtool::stream &stream, uint8_t id)
|
||||
{
|
||||
uint8_t data, rec_id;
|
||||
uint16_t size;
|
||||
|
||||
do
|
||||
{
|
||||
stream.read(&data, 1);
|
||||
|
||||
if(data == 0xff)
|
||||
break;
|
||||
|
||||
if (data == 2)
|
||||
{
|
||||
stream.read(&rec_id, 1);
|
||||
size = get_long_rec_size(stream);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.read(&rec_id, 1);
|
||||
if (id == rec_id)
|
||||
{
|
||||
stream.seek(-2, SEEK_CUR);
|
||||
return true;
|
||||
}
|
||||
size = data;
|
||||
}
|
||||
|
||||
// next record
|
||||
stream.seek(size, SEEK_CUR);
|
||||
|
||||
} while (stream.size() > stream.tell());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// if there are multiple files with the same name, only the first is found
|
||||
int seek_file_name(psion_pack *pack, const char *filename)
|
||||
{
|
||||
uint16_t index = 0;
|
||||
|
||||
while (pack->pack_index[index].name_rec)
|
||||
{
|
||||
if (!strncmp(filename, pack->pack_index[index].filename, strlen(filename)))
|
||||
return index;
|
||||
index++;
|
||||
}
|
||||
|
||||
// filename not found
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t get_free_file_id(psion_pack *pack)
|
||||
{
|
||||
for (uint8_t file_id=0x91; file_id<0xff; file_id++)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
while (pack->pack_index[index].id != file_id)
|
||||
if (pack->pack_index[index++].name_rec == 0)
|
||||
return file_id;
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
static void put_name_record(imgtool::stream &stream, const char* filename, uint8_t record_type, uint8_t record_id)
|
||||
{
|
||||
char data[0x10];
|
||||
int i = 0;
|
||||
|
||||
data[i++] = 0x09;
|
||||
data[i++] = record_type;
|
||||
|
||||
// filename is 8 char long space filled
|
||||
for (int j=0; j<8; j++)
|
||||
if (j < strlen(filename))
|
||||
data[i++] = filename[j];
|
||||
else
|
||||
data[i++] = 0x20;
|
||||
|
||||
data[i++] = record_id;
|
||||
|
||||
stream.write(data, i);
|
||||
}
|
||||
|
||||
static void update_opk_head(imgtool::stream &stream)
|
||||
{
|
||||
uint16_t size = stream.size() - 6;
|
||||
|
||||
stream.seek(4, SEEK_SET);
|
||||
stream.putc((size>>8) & 0xff);
|
||||
stream.putc(size & 0xff);
|
||||
}
|
||||
|
||||
char *stream_getline(imgtool::stream &source, uint16_t max_len)
|
||||
{
|
||||
uint16_t pos = 0;
|
||||
char data;
|
||||
char *line = (char*)malloc(max_len);
|
||||
memset(line, 0, max_len);
|
||||
|
||||
while (pos < max_len && source.size() > source.tell())
|
||||
{
|
||||
source.read(&data, 1);
|
||||
|
||||
switch(data)
|
||||
{
|
||||
case '\r':
|
||||
source.read(&data, 1);
|
||||
if (data != '\n')
|
||||
source.seek(-1, SEEK_CUR);
|
||||
[[fallthrough]];
|
||||
case '\n':
|
||||
return line;
|
||||
default:
|
||||
line[pos++] = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos)
|
||||
return line;
|
||||
|
||||
free(line);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint16_t put_odb(imgtool::stream &instream, imgtool::stream &outstream, uint8_t file_id)
|
||||
{
|
||||
char *line;
|
||||
uint16_t out_size = 0;
|
||||
|
||||
// reset stream
|
||||
instream.seek(0, SEEK_SET);
|
||||
|
||||
while ((line = stream_getline(instream, 256)))
|
||||
{
|
||||
uint16_t len = strlen(line);
|
||||
|
||||
outstream.putc((uint8_t)len);
|
||||
outstream.putc(file_id);
|
||||
outstream.write(line, len);
|
||||
|
||||
out_size += (len + 1);
|
||||
|
||||
free(line);
|
||||
}
|
||||
|
||||
// end of pack
|
||||
outstream.fill(0xff, 2);
|
||||
|
||||
return out_size + 4;
|
||||
}
|
||||
|
||||
uint16_t put_ob3(imgtool::stream &instream, imgtool::stream &outstream)
|
||||
{
|
||||
uint16_t size = instream.size() - 6;
|
||||
std::vector<uint8_t> buffer(size);
|
||||
|
||||
instream.seek(6, SEEK_SET);
|
||||
instream.read(&buffer[0], size);
|
||||
|
||||
outstream.write(&buffer[0], size);
|
||||
|
||||
// end of pack
|
||||
outstream.fill(0xff, 2);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
uint16_t put_opl(imgtool::stream &instream, imgtool::stream &outstream)
|
||||
{
|
||||
uint16_t out_size = 0;
|
||||
uint32_t rec_start = outstream.tell();
|
||||
char *line;
|
||||
|
||||
// reset stream
|
||||
instream.seek(0, SEEK_SET);
|
||||
|
||||
outstream.fill(0x00, 4);
|
||||
|
||||
// replace all eol with 0x00
|
||||
while ((line = stream_getline(instream, 256)))
|
||||
{
|
||||
// replace tab with space
|
||||
for (int i=0; i<strlen(line); i++)
|
||||
if (line[i] == '\t') line[i] = ' ';
|
||||
|
||||
outstream.write(line, strlen(line));
|
||||
outstream.putc(0x00);
|
||||
out_size += strlen(line) + 1;
|
||||
free(line);
|
||||
}
|
||||
|
||||
// end of pack
|
||||
outstream.fill(0xff, 2);
|
||||
|
||||
// update the size in the head
|
||||
outstream.seek(rec_start + 2, SEEK_SET);
|
||||
outstream.putc((out_size>>8) & 0xff);
|
||||
outstream.putc(out_size & 0xff);
|
||||
|
||||
return out_size + 4;
|
||||
}
|
||||
|
||||
uint16_t get_odb(imgtool::stream &instream, imgtool::stream &outstream, uint8_t type, uint8_t file_id)
|
||||
{
|
||||
uint8_t data, *buffer;
|
||||
uint16_t out_size = 0;
|
||||
|
||||
if (file_id >= 0x90)
|
||||
while (seek_next_record(instream, file_id))
|
||||
{
|
||||
instream.read(&data, 1);
|
||||
instream.seek(1, SEEK_CUR);
|
||||
buffer = (uint8_t*)malloc(data);
|
||||
instream.read(buffer, data);
|
||||
outstream.write(buffer, data);
|
||||
outstream.putc('\r');
|
||||
outstream.putc('\n');
|
||||
free (buffer);
|
||||
out_size += data;
|
||||
}
|
||||
|
||||
return out_size;
|
||||
}
|
||||
|
||||
uint16_t get_ob3(imgtool::stream &instream, imgtool::stream &outstream, uint8_t type, uint8_t file_id)
|
||||
{
|
||||
uint8_t data, *buffer = NULL;
|
||||
uint16_t size = 0;
|
||||
static const char ob3_magic[3] = {'O', 'R', 'G'};
|
||||
|
||||
instream.read(&data, 1);
|
||||
|
||||
if (data == 0x02)
|
||||
{
|
||||
instream.seek(1, SEEK_CUR);
|
||||
size = get_long_rec_size(instream);
|
||||
buffer = (uint8_t*)malloc(size);
|
||||
instream.read(buffer, size);
|
||||
}
|
||||
|
||||
outstream.write(ob3_magic, 3);
|
||||
outstream.putc((size>>8) & 0xff);
|
||||
outstream.putc(size & 0xff);
|
||||
outstream.putc(type | 0x80);
|
||||
|
||||
if (buffer)
|
||||
{
|
||||
outstream.write(buffer, size);
|
||||
free (buffer);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static imgtoolerr_t datapack_open(imgtool::image &image, imgtool::stream::ptr &&stream)
|
||||
{
|
||||
psion_pack *pack = get_psion_pack(image);
|
||||
char opk_magic[4];
|
||||
|
||||
stream->read(opk_magic, 4);
|
||||
|
||||
if(strcmp(opk_magic, "OPK\0"))
|
||||
return IMGTOOLERR_UNEXPECTED;
|
||||
|
||||
pack->stream = stream.get();
|
||||
|
||||
if (update_pack_index(pack))
|
||||
{
|
||||
pack->stream = stream.release();
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
}
|
||||
}
|
||||
|
||||
static imgtoolerr_t datapack_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts)
|
||||
{
|
||||
psion_pack *pack = get_psion_pack(image);
|
||||
static const uint8_t opk_magic[4] = {'O', 'P', 'K', 0x00};
|
||||
uint8_t pack_head[8] = {0x40, 0x00, 0x59, 0x01, 0x01, 0x01, 0x00, 0x00};
|
||||
uint16_t checksum;
|
||||
|
||||
pack_head[0] |= (opts->lookup_int('R')) ? 0x00 : 0x02;
|
||||
pack_head[0] |= (opts->lookup_int('P')) ? 0x04 : 0x00;
|
||||
pack_head[0] |= (opts->lookup_int('W')) ? 0x00 : 0x08;
|
||||
pack_head[0] |= (opts->lookup_int('B')) ? 0x00 : 0x10;
|
||||
pack_head[0] |= (opts->lookup_int('C')) ? 0x20 : 0x00;
|
||||
pack_head[1] = opts->lookup_int('S');
|
||||
|
||||
checksum = head_checksum(pack_head);
|
||||
|
||||
stream->write(opk_magic, 4);
|
||||
stream->fill(0x00, 2);
|
||||
stream->write(pack_head, 8);
|
||||
|
||||
stream->putc((checksum>>8) & 0xff);
|
||||
stream->putc(checksum & 0xff);
|
||||
|
||||
put_name_record(*stream, "MAIN", 0x81, 0x90);
|
||||
|
||||
stream->fill(0xff, 2);
|
||||
|
||||
update_opk_head(*stream);
|
||||
|
||||
pack->stream = stream.get();
|
||||
|
||||
if (update_pack_index(pack))
|
||||
{
|
||||
pack->stream = stream.release();
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
}
|
||||
}
|
||||
|
||||
static void datapack_close(imgtool::image &image)
|
||||
{
|
||||
psion_pack *pack = get_psion_pack(image);
|
||||
|
||||
delete pack->stream;
|
||||
}
|
||||
|
||||
static imgtoolerr_t datapack_begin_enum(imgtool::directory &enumeration, const char *path)
|
||||
{
|
||||
psion_iter *iter = (psion_iter*)enumeration.extra_bytes();
|
||||
iter->index = 0;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t datapack_next_enum(imgtool::directory &enumeration, imgtool_dirent &ent)
|
||||
{
|
||||
imgtool::image &image(enumeration.image());
|
||||
psion_pack *pack = get_psion_pack(image);
|
||||
psion_iter *iter = (psion_iter*)enumeration.extra_bytes();
|
||||
uint8_t data = 0;
|
||||
|
||||
if (!pack->pack_index[iter->index].name_rec)
|
||||
{
|
||||
ent.eof = 1;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
memcpy(ent.filename, pack->pack_index[iter->index].filename, 8);
|
||||
sprintf(ent.attr, "Type: %02x ID: %02x", pack->pack_index[iter->index].type, pack->pack_index[iter->index].id);
|
||||
|
||||
if (pack->pack_index[iter->index].data_rec)
|
||||
{
|
||||
pack->stream->seek(pack->pack_index[iter->index].data_rec + 2, SEEK_SET);
|
||||
ent.filesize = get_long_rec_size(*pack->stream);
|
||||
}
|
||||
|
||||
// seek all file's records
|
||||
if (pack->pack_index[iter->index].id >= 0x90)
|
||||
{
|
||||
pack->stream->seek(0x10, SEEK_SET);
|
||||
while (seek_next_record(*pack->stream, pack->pack_index[iter->index].id))
|
||||
{
|
||||
pack->stream->read(&data, 1);
|
||||
pack->stream->seek(data + 1, SEEK_CUR);
|
||||
ent.filesize +=data;
|
||||
}
|
||||
}
|
||||
|
||||
iter->index++;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t datapack_free_space(imgtool::partition &partition, uint64_t *size)
|
||||
{
|
||||
imgtool::image &image(partition.image());
|
||||
psion_pack *pack = get_psion_pack(image);
|
||||
uint32_t pack_size = 0;
|
||||
|
||||
pack->stream->seek(0x07, SEEK_SET);
|
||||
pack->stream->read(&pack_size, 1);
|
||||
|
||||
if (size)
|
||||
*size = (pack_size * 0x2000) - pack->eop;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t datapack_read_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)
|
||||
{
|
||||
imgtool::image &image(partition.image());
|
||||
psion_pack *pack = get_psion_pack(image);
|
||||
int index = seek_file_name(pack, filename);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
if ((pack->pack_index[index].type & 0x7f) == 0x01)
|
||||
{
|
||||
// ODB files
|
||||
pack->stream->seek(0x10, SEEK_SET);
|
||||
get_odb(*pack->stream, destf, pack->pack_index[index].type, pack->pack_index[index].id);
|
||||
}
|
||||
else if ((pack->pack_index[index].type & 0x7f) == 0x03)
|
||||
{
|
||||
// OB3/OPL files
|
||||
pack->stream->seek(pack->pack_index[index].data_rec, SEEK_SET);
|
||||
get_ob3(*pack->stream, destf, pack->pack_index[index].type, pack->pack_index[index].id);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Other files
|
||||
return IMGTOOLERR_UNIMPLEMENTED;
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
else
|
||||
return IMGTOOLERR_FILENOTFOUND;
|
||||
}
|
||||
|
||||
static imgtoolerr_t datapack_write_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts)
|
||||
{
|
||||
imgtool::image &image(partition.image());
|
||||
psion_pack *pack = get_psion_pack(image);
|
||||
static const uint8_t data_head[4] = {0x02, 0x80, 0x00, 0x00};
|
||||
uint8_t head[3];
|
||||
uint16_t size = 0;
|
||||
uint8_t type = opts->lookup_int('T');
|
||||
uint8_t file_id = opts->lookup_int('I');
|
||||
|
||||
if (!pack->eop)
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
|
||||
// if not file_id is specified get the first free (for ODB only)
|
||||
if (file_id == 0 && type == 3)
|
||||
{
|
||||
file_id = get_free_file_id(pack);
|
||||
|
||||
if (file_id == 0xff)
|
||||
return IMGTOOLERR_NOSPACE;
|
||||
}
|
||||
|
||||
sourcef.read(head, 3);
|
||||
pack->stream->seek(pack->eop, SEEK_SET);
|
||||
|
||||
if (type == 0)
|
||||
type = (!strncmp((char*)head, "ORG", 3)) ? 1 : 2;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 1: //OB3 file
|
||||
put_name_record(*pack->stream, filename, 0x83, file_id);
|
||||
pack->stream->write(data_head, 4);
|
||||
size = put_ob3(sourcef, *pack->stream);
|
||||
break;
|
||||
case 2: //OPL file
|
||||
put_name_record(*pack->stream, filename, 0x83, file_id);
|
||||
pack->stream->write(data_head, 4);
|
||||
size = put_opl(sourcef, *pack->stream);
|
||||
break;
|
||||
case 3: //ODB file
|
||||
put_name_record(*pack->stream, filename, 0x81, file_id);
|
||||
size = put_odb(sourcef, *pack->stream, file_id);
|
||||
break;
|
||||
}
|
||||
|
||||
if (type != 3)
|
||||
{
|
||||
// update the OB3/OPL long record size
|
||||
pack->stream->seek(pack->eop + 13, SEEK_SET);
|
||||
pack->stream->putc((size>>8) & 0xff);
|
||||
pack->stream->putc(size & 0xff);
|
||||
}
|
||||
|
||||
update_opk_head(*pack->stream);
|
||||
|
||||
if (update_pack_index(pack))
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
else
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
}
|
||||
|
||||
static imgtoolerr_t datapack_delete_file(imgtool::partition &partition, const char *filename)
|
||||
{
|
||||
imgtool::image &image(partition.image());
|
||||
psion_pack *pack = get_psion_pack(image);
|
||||
int index = seek_file_name(pack, filename);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
// clear the bit 7 of the file type to mark the file as deleted
|
||||
pack->stream->seek(pack->pack_index[index].name_rec + 1, SEEK_SET);
|
||||
pack->stream->putc(pack->pack_index[index].type & 0x7f);
|
||||
|
||||
if (update_pack_index(pack))
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
else
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
}
|
||||
else
|
||||
return IMGTOOLERR_FILENOTFOUND;
|
||||
}
|
||||
|
||||
OPTION_GUIDE_START( psion_create_optguide )
|
||||
OPTION_ENUM_START( 'S', "size", "datapack size" )
|
||||
OPTION_ENUM( 1, "8k", "8 kbyte" )
|
||||
OPTION_ENUM( 2, "16k", "16 kbyts" )
|
||||
OPTION_ENUM( 4, "32k", "32 kbyte" )
|
||||
OPTION_ENUM( 8, "64k", "64 kbyte" )
|
||||
OPTION_ENUM( 16, "128k", "128 kbyte" ) // only paged datapacks can have this size
|
||||
OPTION_ENUM_END
|
||||
OPTION_INT('R', "ram", "EPROM/RAM datapack" )
|
||||
OPTION_INT('P', "paged", "linear/paged datapack" )
|
||||
OPTION_INT('W', "protect", "write-protected datapack" )
|
||||
OPTION_INT('B', "boot", "bootable datapack" )
|
||||
OPTION_INT('C', "copy", "copyable datapack" )
|
||||
OPTION_GUIDE_END
|
||||
|
||||
OPTION_GUIDE_START( psion_write_optguide )
|
||||
OPTION_ENUM_START( 'T', "type", "file type" )
|
||||
OPTION_ENUM( 1, "OB3", "OB3 files" )
|
||||
OPTION_ENUM( 2, "OPL", "OPL files" )
|
||||
OPTION_ENUM( 3, "ODB", "ODB or text files" )
|
||||
OPTION_ENUM_END
|
||||
OPTION_INT( 'I', "id", "File ID" )
|
||||
OPTION_GUIDE_END
|
||||
|
||||
void psion_get_info( const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
// --- the following bits of info are returned as 64-bit signed integers ---
|
||||
case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES : info->i = sizeof(psion_pack); break;
|
||||
case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES : info->i = sizeof(psion_iter); break;
|
||||
|
||||
// --- the following bits of info are returned as pointers to data or functions ---
|
||||
case IMGTOOLINFO_PTR_OPEN : info->open = datapack_open; break;
|
||||
case IMGTOOLINFO_PTR_CREATE : info->create = datapack_create; break;
|
||||
case IMGTOOLINFO_PTR_CLOSE : info->close = datapack_close; break;
|
||||
case IMGTOOLINFO_PTR_BEGIN_ENUM : info->begin_enum = datapack_begin_enum; break;
|
||||
case IMGTOOLINFO_PTR_NEXT_ENUM : info->next_enum = datapack_next_enum; break;
|
||||
case IMGTOOLINFO_PTR_FREE_SPACE : info->free_space = datapack_free_space; break;
|
||||
case IMGTOOLINFO_PTR_READ_FILE : info->read_file = datapack_read_file; break;
|
||||
case IMGTOOLINFO_PTR_WRITE_FILE : info->write_file = datapack_write_file; break;
|
||||
case IMGTOOLINFO_PTR_DELETE_FILE : info->delete_file = datapack_delete_file; break;
|
||||
case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE : info->createimage_optguide = &psion_create_optguide; break;
|
||||
case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE : info->createimage_optguide = &psion_write_optguide; break;
|
||||
|
||||
// --- the following bits of info are returned as NULL-terminated strings ---
|
||||
case IMGTOOLINFO_STR_NAME : strcpy( info->s = imgtool_temp_str(), "psionpack"); break;
|
||||
case IMGTOOLINFO_STR_DESCRIPTION : strcpy( info->s = imgtool_temp_str(), "Psion Organiser II Datapack"); break;
|
||||
case IMGTOOLINFO_STR_FILE : strcpy( info->s = imgtool_temp_str(), __FILE__); break;
|
||||
case IMGTOOLINFO_STR_FILE_EXTENSIONS : strcpy( info->s = imgtool_temp_str(), "opk"); break;
|
||||
case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC : strcpy( info->s = imgtool_temp_str(), "S1/2/[4]/8/16;R[0]/1;P[0]/1;W0/[1];B[0]/1;C0/[1]"); break;
|
||||
case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC : strcpy( info->s = imgtool_temp_str(), "T[1]/2/3;I[0]/145-255"); break;
|
||||
}
|
||||
}
|
@ -1,666 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/****************************************************************************
|
||||
|
||||
rsdos.cpp
|
||||
|
||||
CoCo RS-DOS disk images
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "imgtool.h"
|
||||
#include "filter.h"
|
||||
#include "iflopimg.h"
|
||||
|
||||
#include "formats/coco_dsk.h"
|
||||
#include "corestr.h"
|
||||
#include "opresolv.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
/* this structure mirrors the structure of an RS-DOS directory entry on disk */
|
||||
struct rsdos_dirent
|
||||
{
|
||||
char filename[11];
|
||||
char ftype;
|
||||
char asciiflag;
|
||||
unsigned char first_granule;
|
||||
unsigned char lastsectorbytes_msb;
|
||||
unsigned char lastsectorbytes_lsb;
|
||||
unsigned char unused[16];
|
||||
};
|
||||
|
||||
struct rsdos_direnum
|
||||
{
|
||||
int index;
|
||||
bool eof;
|
||||
};
|
||||
|
||||
#define RSDOS_OPTIONS_FTYPE 'T'
|
||||
#define RSDOS_OPTIONS_ASCII 'M'
|
||||
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
Imgtool module code
|
||||
*********************************************************************/
|
||||
|
||||
#define MAX_DIRENTS ((18-2)*(256/32))
|
||||
#define MAX_GRANULEMAP_SIZE 256
|
||||
|
||||
//-------------------------------------------------
|
||||
// get_rsdos_dirent
|
||||
//-------------------------------------------------
|
||||
|
||||
static floperr_t get_rsdos_dirent(imgtool::image &f, int index_loc, rsdos_dirent &ent)
|
||||
{
|
||||
return floppy_read_sector(imgtool_floppy(f), 0, 17, 3, index_loc * 32, (void *) &ent, sizeof(ent));
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// put_rsdos_dirent
|
||||
//-------------------------------------------------
|
||||
|
||||
static floperr_t put_rsdos_dirent(imgtool::image &f, int index_loc, const rsdos_dirent &ent)
|
||||
{
|
||||
if (index_loc >= MAX_DIRENTS)
|
||||
return (floperr_t)IMGTOOLERR_FILENOTFOUND;
|
||||
return floppy_write_sector(imgtool_floppy(f), 0, 17, 3, index_loc * 32, (void *) &ent, sizeof(ent), 0); /* TODO: pass ddam argument from imgtool */
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// get_dirent_fname
|
||||
//-------------------------------------------------
|
||||
|
||||
static std::string get_dirent_fname(const rsdos_dirent &ent)
|
||||
{
|
||||
return extract_padded_filename(ent.filename, 8, 3);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// lookup_rsdos_file
|
||||
//-------------------------------------------------
|
||||
|
||||
static imgtoolerr_t lookup_rsdos_file(imgtool::image &f, const char *fname, rsdos_dirent &ent, int *position = nullptr)
|
||||
{
|
||||
int i;
|
||||
floperr_t ferr;
|
||||
std::string fnamebuf;
|
||||
|
||||
i = 0;
|
||||
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
ferr = get_rsdos_dirent(f, i++, ent);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
}
|
||||
while(ent.filename[0] == '\0');
|
||||
|
||||
|
||||
if (ent.filename[0] != -1)
|
||||
fnamebuf = get_dirent_fname(ent);
|
||||
}
|
||||
while((ent.filename[0] != -1) && core_stricmp(fnamebuf.c_str(), fname));
|
||||
|
||||
if (ent.filename[0] == -1)
|
||||
return IMGTOOLERR_FILENOTFOUND;
|
||||
|
||||
if (position)
|
||||
*position = i - 1;
|
||||
return (imgtoolerr_t)0;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// get_granule_count
|
||||
//-------------------------------------------------
|
||||
|
||||
static uint8_t get_granule_count(imgtool::image &img)
|
||||
{
|
||||
uint16_t tracks;
|
||||
uint16_t granules;
|
||||
|
||||
tracks = floppy_get_tracks_per_disk(imgtool_floppy(img));
|
||||
granules = (tracks - 1) * 2;
|
||||
return (granules > 255) ? 255 : (uint8_t) granules;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// get_granule_map
|
||||
//-------------------------------------------------
|
||||
|
||||
static floperr_t get_granule_map(imgtool::image &img, uint8_t *granule_map, uint8_t granule_count[MAX_GRANULEMAP_SIZE])
|
||||
{
|
||||
uint8_t count;
|
||||
|
||||
count = get_granule_count(img);
|
||||
if (granule_count)
|
||||
*granule_count = count;
|
||||
|
||||
return floppy_read_sector(imgtool_floppy(img), 0, 17, 2, 0, granule_map, count);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// put_granule_map
|
||||
//-------------------------------------------------
|
||||
|
||||
static floperr_t put_granule_map(imgtool::image &img, const uint8_t *granule_map, uint8_t granule_count)
|
||||
{
|
||||
return floppy_write_sector(imgtool_floppy(img), 0, 17, 2, 0, granule_map, granule_count, 0); /* TODO: pass ddam argument from imgtool */
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// transfer_granule
|
||||
//-------------------------------------------------
|
||||
|
||||
static imgtoolerr_t transfer_granule(imgtool::image &img, uint8_t granule, int length, imgtool::stream &f, imgtoolerr_t (*proc)(imgtool::image &, int, int, int, int, size_t, imgtool::stream &))
|
||||
{
|
||||
imgtoolerr_t err = IMGTOOLERR_SUCCESS;
|
||||
uint8_t track, sector;
|
||||
|
||||
track = granule / 2;
|
||||
if (track >= 17)
|
||||
track++;
|
||||
|
||||
sector = (granule % 2) ? 10 : 1;
|
||||
|
||||
if (length > 0)
|
||||
err = proc(img, 0, track, sector, 0, length, f);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// transfer_from_granule
|
||||
//-------------------------------------------------
|
||||
|
||||
static imgtoolerr_t transfer_from_granule(imgtool::image &img, uint8_t granule, int length, imgtool::stream &destf)
|
||||
{
|
||||
return transfer_granule(img, granule, length, destf, imgtool_floppy_read_sector_to_stream);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// transfer_to_granule
|
||||
//-------------------------------------------------
|
||||
|
||||
static imgtoolerr_t transfer_to_granule(imgtool::image &img, uint8_t granule, int length, imgtool::stream &sourcef)
|
||||
{
|
||||
return transfer_granule(img, granule, length, sourcef, imgtool_floppy_write_sector_from_stream);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// process_rsdos_file
|
||||
//-------------------------------------------------
|
||||
|
||||
static imgtoolerr_t process_rsdos_file(struct rsdos_dirent *ent, imgtool::image &img, imgtool::stream *destf, size_t &size)
|
||||
{
|
||||
floperr_t ferr;
|
||||
size_t s, lastgransize;
|
||||
uint8_t granule_count;
|
||||
unsigned char i = 0, granule;
|
||||
uint8_t usedmap[MAX_GRANULEMAP_SIZE]; // used to detect infinite loops
|
||||
uint8_t granule_map[MAX_GRANULEMAP_SIZE];
|
||||
|
||||
ferr = get_granule_map(img, granule_map, &granule_count);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
memset(usedmap, 0, granule_count);
|
||||
|
||||
lastgransize = ent->lastsectorbytes_lsb + (((int) ent->lastsectorbytes_msb) << 8);
|
||||
s = 0;
|
||||
granule = ent->first_granule;
|
||||
|
||||
while(!usedmap[granule] && ((i = granule_map[granule]) < granule_count))
|
||||
{
|
||||
usedmap[granule] = 1;
|
||||
if (destf)
|
||||
transfer_from_granule(img, granule, 9*256, *destf);
|
||||
|
||||
/* i is the next granule */
|
||||
s += (256 * 9);
|
||||
granule = i;
|
||||
}
|
||||
|
||||
if ((i < 0xc0) || (i > 0xc9))
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
|
||||
if (lastgransize)
|
||||
i--;
|
||||
lastgransize += (256 * (i - 0xc0));
|
||||
|
||||
if (destf)
|
||||
transfer_from_granule(img, granule, lastgransize, *destf);
|
||||
|
||||
size = s + lastgransize;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// prepare_dirent - create a new directory entry
|
||||
// with a specified name
|
||||
//-------------------------------------------------
|
||||
|
||||
static imgtoolerr_t prepare_dirent(rsdos_dirent &ent, const char *fname)
|
||||
{
|
||||
const char *fname_end;
|
||||
const char *fname_ext;
|
||||
int fname_ext_len;
|
||||
|
||||
memset(&ent, '\0', sizeof(ent));
|
||||
memset(ent.filename, ' ', sizeof(ent.filename));
|
||||
|
||||
fname_end = strchr(fname, '.');
|
||||
if (fname_end)
|
||||
fname_ext = fname_end + 1;
|
||||
else
|
||||
fname_end = fname_ext = fname + strlen(fname);
|
||||
|
||||
fname_ext_len = strlen(fname_ext);
|
||||
|
||||
// we had better be an 8.3 filename
|
||||
if (((fname_end - fname) > 8) || (fname_ext_len > 3))
|
||||
return IMGTOOLERR_BADFILENAME;
|
||||
|
||||
memcpy(&ent.filename[0], fname, fname_end - fname);
|
||||
memcpy(&ent.filename[8], fname_ext, fname_ext_len);
|
||||
|
||||
// for now, all files are type 2 binary files
|
||||
ent.ftype = 2;
|
||||
ent.asciiflag = 0;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// rsdos_diskimage_nextenum
|
||||
//-------------------------------------------------
|
||||
|
||||
static imgtoolerr_t rsdos_diskimage_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent)
|
||||
{
|
||||
floperr_t ferr;
|
||||
imgtoolerr_t err;
|
||||
size_t filesize;
|
||||
rsdos_direnum *rsenum;
|
||||
rsdos_dirent rsent;
|
||||
|
||||
imgtool::image &image(enumeration.image());
|
||||
rsenum = (rsdos_direnum *) enumeration.extra_bytes();
|
||||
|
||||
/* Did we hit the end of file before? */
|
||||
if (rsenum->eof)
|
||||
goto eof;
|
||||
|
||||
do
|
||||
{
|
||||
if (rsenum->index >= MAX_DIRENTS)
|
||||
goto eof;
|
||||
|
||||
ferr = get_rsdos_dirent(image, rsenum->index++, rsent);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
}
|
||||
while(rsent.filename[0] == '\0');
|
||||
|
||||
// now are we at the eof point?
|
||||
if (rsent.filename[0] == -1)
|
||||
{
|
||||
rsenum->eof = 1;
|
||||
eof:
|
||||
ent.eof = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Note the end of file */
|
||||
err = process_rsdos_file(&rsent, image, nullptr, filesize);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (filesize == ((size_t) -1))
|
||||
{
|
||||
/* corrupt! */
|
||||
ent.filesize = 0;
|
||||
ent.corrupt = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent.filesize = filesize;
|
||||
ent.corrupt = 0;
|
||||
}
|
||||
ent.eof = 0;
|
||||
|
||||
std::string fname = get_dirent_fname(rsent);
|
||||
|
||||
snprintf(ent.filename, std::size(ent.filename), "%s", fname.c_str());
|
||||
snprintf(ent.attr, std::size(ent.attr), "%d %c", (int) rsent.ftype, (char) (rsent.asciiflag + 'B'));
|
||||
}
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// rsdos_diskimage_freespace
|
||||
//-------------------------------------------------
|
||||
|
||||
static imgtoolerr_t rsdos_diskimage_freespace(imgtool::partition &partition, uint64_t *size)
|
||||
{
|
||||
floperr_t ferr;
|
||||
uint8_t i;
|
||||
size_t s = 0;
|
||||
uint8_t granule_count;
|
||||
uint8_t granule_map[MAX_GRANULEMAP_SIZE];
|
||||
imgtool::image &image(partition.image());
|
||||
|
||||
ferr = get_granule_map(image, granule_map, &granule_count);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
for (i = 0; i < granule_count; i++)
|
||||
{
|
||||
if (granule_map[i] == 0xff)
|
||||
s += (9 * 256);
|
||||
}
|
||||
*size = s;
|
||||
return (imgtoolerr_t)FLOPPY_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// delete_entry
|
||||
//-------------------------------------------------
|
||||
|
||||
static imgtoolerr_t delete_entry(imgtool::image &img, rsdos_dirent &ent, int pos)
|
||||
{
|
||||
floperr_t ferr;
|
||||
unsigned char g, i;
|
||||
uint8_t granule_count;
|
||||
uint8_t granule_map[MAX_GRANULEMAP_SIZE];
|
||||
|
||||
// write a NUL in the filename, marking it deleted
|
||||
ent.filename[0] = 0;
|
||||
ferr = put_rsdos_dirent(img, pos, ent);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
ferr = get_granule_map(img, granule_map, &granule_count);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
// now free up the granules
|
||||
g = ent.first_granule;
|
||||
while (g < granule_count)
|
||||
{
|
||||
i = granule_map[g];
|
||||
granule_map[g] = 0xff;
|
||||
g = i;
|
||||
}
|
||||
|
||||
ferr = put_granule_map(img, granule_map, granule_count);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// rsdos_diskimage_readfile
|
||||
//-------------------------------------------------
|
||||
|
||||
static imgtoolerr_t rsdos_diskimage_readfile(imgtool::partition &partition, const char *fname, const char *fork, imgtool::stream &destf)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
struct rsdos_dirent ent;
|
||||
size_t size;
|
||||
imgtool::image &img(partition.image());
|
||||
|
||||
err = lookup_rsdos_file(img, fname, ent);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = process_rsdos_file(&ent, img, &destf, size);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (size == (size_t) -1)
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
|
||||
return (imgtoolerr_t)0;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// rsdos_diskimage_writefile
|
||||
//-------------------------------------------------
|
||||
|
||||
static imgtoolerr_t rsdos_diskimage_writefile(imgtool::partition &partition, const char *fname, const char *fork, imgtool::stream &sourcef, util::option_resolution *writeoptions)
|
||||
{
|
||||
floperr_t ferr;
|
||||
imgtoolerr_t err;
|
||||
imgtool::image &img(partition.image());
|
||||
struct rsdos_dirent ent, ent2;
|
||||
size_t i;
|
||||
uint64_t sz;
|
||||
uint64_t freespace = 0;
|
||||
unsigned char g;
|
||||
unsigned char *gptr;
|
||||
uint8_t granule_count;
|
||||
uint8_t granule_map[MAX_GRANULEMAP_SIZE];
|
||||
|
||||
// can we write to this image?
|
||||
if (floppy_is_read_only(imgtool_floppy(img)))
|
||||
return IMGTOOLERR_READONLY;
|
||||
|
||||
err = rsdos_diskimage_freespace(partition, &freespace);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
// is there enough space?
|
||||
sz = sourcef.size();
|
||||
if (sz > freespace)
|
||||
return IMGTOOLERR_NOSPACE;
|
||||
|
||||
// setup our directory entry
|
||||
err = prepare_dirent(ent, fname);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ent.ftype = writeoptions->lookup_int(RSDOS_OPTIONS_FTYPE);
|
||||
ent.asciiflag = uint8_t(writeoptions->lookup_int(RSDOS_OPTIONS_ASCII)) - 1;
|
||||
ent.lastsectorbytes_lsb = sz % 256;
|
||||
ent.lastsectorbytes_msb = (((sz % 256) == 0) && (sz > 0)) ? 1 : 0;
|
||||
gptr = &ent.first_granule;
|
||||
|
||||
ferr = get_granule_map(img, granule_map, &granule_count);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
g = 0x00;
|
||||
|
||||
do
|
||||
{
|
||||
while (granule_map[g] != 0xff)
|
||||
{
|
||||
g++;
|
||||
if ((g >= granule_count) || (g == 0))
|
||||
return IMGTOOLERR_UNEXPECTED; // we should have already verified that there is enough space
|
||||
}
|
||||
*gptr = g;
|
||||
gptr = &granule_map[g];
|
||||
|
||||
|
||||
i = std::min(sz, uint64_t(9*256));
|
||||
err = transfer_to_granule(img, g, i, sourcef);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
sz -= i;
|
||||
|
||||
// go to next granule
|
||||
g++;
|
||||
}
|
||||
while(sz > 0);
|
||||
|
||||
// now that we are done with the file, we need to specify the final entry
|
||||
// in the file allocation table
|
||||
*gptr = 0xc0 + ((i + 255) / 256);
|
||||
|
||||
// now we need to find an empty directory entry
|
||||
i = -1;
|
||||
do
|
||||
{
|
||||
ferr = get_rsdos_dirent(img, ++i, ent2);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
}
|
||||
while((ent2.filename[0] != '\0') && strcmp(ent.filename, ent2.filename) && (ent2.filename[0] != -1));
|
||||
|
||||
// delete file if it already exists
|
||||
if (ent2.filename[0] && (ent2.filename[0] != -1))
|
||||
{
|
||||
err = delete_entry(img, ent2, i);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
ferr = put_rsdos_dirent(img, i, ent);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
// write the granule map back out
|
||||
ferr = put_granule_map(img, granule_map, granule_count);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// rsdos_diskimage_deletefile
|
||||
//-------------------------------------------------
|
||||
|
||||
static imgtoolerr_t rsdos_diskimage_deletefile(imgtool::partition &partition, const char *fname)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
imgtool::image &image(partition.image());
|
||||
int pos;
|
||||
struct rsdos_dirent ent;
|
||||
|
||||
err = lookup_rsdos_file(image, fname, ent, &pos);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return delete_entry(image, ent, pos);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// rsdos_diskimage_suggesttransfer
|
||||
//-------------------------------------------------
|
||||
|
||||
static imgtoolerr_t rsdos_diskimage_suggesttransfer(imgtool::partition &partition, const char *fname, imgtool_transfer_suggestion *suggestions, size_t suggestions_length)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
imgtool::image &image(partition.image());
|
||||
struct rsdos_dirent ent;
|
||||
int pos;
|
||||
|
||||
if (fname)
|
||||
{
|
||||
err = lookup_rsdos_file(image, fname, ent, &pos);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (ent.asciiflag == (char) 0xFF)
|
||||
{
|
||||
/* ASCII file */
|
||||
suggestions[0].viability = SUGGESTION_RECOMMENDED;
|
||||
suggestions[0].filter = filter_eoln_getinfo;
|
||||
suggestions[1].viability = SUGGESTION_POSSIBLE;
|
||||
suggestions[1].filter = NULL;
|
||||
}
|
||||
else if (ent.ftype == 0)
|
||||
{
|
||||
/* tokenized BASIC file */
|
||||
suggestions[0].viability = SUGGESTION_RECOMMENDED;
|
||||
suggestions[0].filter = NULL;
|
||||
suggestions[1].viability = SUGGESTION_POSSIBLE;
|
||||
suggestions[1].filter = filter_cocobas_getinfo;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
suggestions[0].viability = SUGGESTION_RECOMMENDED;
|
||||
suggestions[0].filter = NULL;
|
||||
suggestions[1].viability = SUGGESTION_POSSIBLE;
|
||||
suggestions[1].filter = filter_eoln_getinfo;
|
||||
suggestions[2].viability = SUGGESTION_POSSIBLE;
|
||||
suggestions[2].filter = filter_cocobas_getinfo;
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
Imgtool module declaration
|
||||
*********************************************************************/
|
||||
|
||||
OPTION_GUIDE_START( coco_rsdos_writefile_optionguide )
|
||||
OPTION_ENUM_START( RSDOS_OPTIONS_FTYPE, "ftype", "File type" )
|
||||
OPTION_ENUM( 0, "basic", "Basic" )
|
||||
OPTION_ENUM( 1, "data", "Data" )
|
||||
OPTION_ENUM( 2, "binary", "Binary" )
|
||||
OPTION_ENUM( 3, "assembler", "Assembler Source" )
|
||||
OPTION_ENUM_END
|
||||
OPTION_ENUM_START( RSDOS_OPTIONS_ASCII, "ascii", "Ascii flag" )
|
||||
OPTION_ENUM( 0, "ascii", "Ascii" )
|
||||
OPTION_ENUM( 1, "binary", "Binary" )
|
||||
OPTION_ENUM_END
|
||||
OPTION_GUIDE_END
|
||||
|
||||
|
||||
|
||||
void rsdos_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
||||
case IMGTOOLINFO_INT_PREFER_UCASE: info->i = 1; break;
|
||||
case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(struct rsdos_direnum); break;
|
||||
|
||||
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
||||
case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "rsdos"); break;
|
||||
case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "RS-DOS format"); break;
|
||||
case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break;
|
||||
case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break;
|
||||
case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), "T0-[2]-3;M0-[1]"); break;
|
||||
|
||||
/* --- the following bits of info are returned as pointers to data or functions --- */
|
||||
case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break;
|
||||
case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = rsdos_diskimage_nextenum; break;
|
||||
case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = rsdos_diskimage_freespace; break;
|
||||
case IMGTOOLINFO_PTR_READ_FILE: info->read_file = rsdos_diskimage_readfile; break;
|
||||
case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = rsdos_diskimage_writefile; break;
|
||||
case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = rsdos_diskimage_deletefile; break;
|
||||
case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = rsdos_diskimage_suggesttransfer; break;
|
||||
case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE: info->writefile_optguide = &coco_rsdos_writefile_optionguide; break;
|
||||
case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_coco; break;
|
||||
}
|
||||
}
|
@ -1,690 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Sergey Svishchev
|
||||
/****************************************************************************
|
||||
|
||||
rt11.cpp
|
||||
|
||||
DEC RT-11 disk images
|
||||
|
||||
References:
|
||||
|
||||
VaFFM -- bitsavers://pdf/dec/pdp11/rt11/v5.6_Aug91/AA-PD6PA-TC_RT-11_Volume_and_File_Formats_Manual_Aug91.pdf
|
||||
DHM -- bitsavers://pdf/dec/pdp11/rt11/v5.6_Aug91/AA-PE7VA-TC_RT-11_Device_Handlers_Manual_Aug91.pdf
|
||||
SSM -- bitsavers://pdf/dec/pdp11/rt11/v5.0_Mar83/AA-H379B-TC_5.0_SWsuppMar83.pdf
|
||||
TSX+ -- bitsavers://pdf/dec/pdp11/tsxPlus/manuals_6.31/TSX-Plus_UsersRef_Jan88.pdf
|
||||
PUTR -- http://www.dbit.com/pub/putr/putr.asm
|
||||
|
||||
To do:
|
||||
- filter for text files
|
||||
- read-write support
|
||||
- report empty 'last modified' time if date field is all zeros
|
||||
- report free space
|
||||
- arbitrary sized images
|
||||
- don't crash when strings in home block have non-ascii chars (charconverter does not apply)
|
||||
- do something about bootblock bug in imgtool (commit aca90520)
|
||||
|
||||
LBN Contents
|
||||
--- --------
|
||||
0 Reserved (primary bootstrap)
|
||||
1 Reserved (home block)
|
||||
2-5 Reserved (secondary bootstrap)
|
||||
6-7 Directory segment 1
|
||||
... Directory segment 2-n
|
||||
... Data
|
||||
|
||||
Home block
|
||||
----------
|
||||
000-201 Bad block replacement table
|
||||
202-203 ?
|
||||
204-251 INITIALIZE/RESTORE data area
|
||||
252-273 BUP information area
|
||||
274-677 ?
|
||||
700-701 (Reserved for Digital, must be zero)
|
||||
702-703 (Reserved for Digital, must be zero)
|
||||
704-721 ?
|
||||
722-723 Pack cluster size (= 1)
|
||||
724-725 Block number of first directory segment
|
||||
726-727 System version (RAD50)
|
||||
730-742 Volume Identification
|
||||
744-757 Owner name
|
||||
760-773 System Identification
|
||||
776-777 Checksum
|
||||
|
||||
Directory segment header
|
||||
------------------------
|
||||
0 The total number of segments in this directory.
|
||||
1 The segment number of the next logical directory segment. If this word is 0, there are no more segments in the list.
|
||||
2 The number of the highest segment currently in use. Valid only in the first directory segment.
|
||||
3 The number of extra bytes per directory entry, always an unsigned, even octal number.
|
||||
4 The block number on the volume where the actual stored data identified by this segment begins.
|
||||
|
||||
Directory entry
|
||||
---------------
|
||||
0 Status word
|
||||
1 File name 1-3 (RAD50)
|
||||
2 File name 4-6 (RAD50)
|
||||
3 File type 1-3 (RAD50)
|
||||
4 Total file length (blocks)
|
||||
5 Job#, Channel# (RT-11 uses this information only for tentative files)
|
||||
6 Creation date
|
||||
7- Optional extra words
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "imgtool.h"
|
||||
#include "formats/imageutl.h"
|
||||
#include "iflopimg.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
struct rt11_diskinfo
|
||||
{
|
||||
uint16_t directory_start;
|
||||
uint16_t total_segments;
|
||||
uint16_t last_segment;
|
||||
uint16_t dirent_size;
|
||||
int dirents_per_block;
|
||||
// autodetected
|
||||
int tracks;
|
||||
int heads;
|
||||
int sectors;
|
||||
uint32_t sector_size;
|
||||
// cache
|
||||
int map[16];
|
||||
int cached_track;
|
||||
int cached_head;
|
||||
};
|
||||
|
||||
enum misc_t
|
||||
{
|
||||
HOME_BLOCK = 1,
|
||||
BLOCK_SIZE = 512
|
||||
};
|
||||
|
||||
struct rt11_direnum
|
||||
{
|
||||
uint8_t segment_data[2 * BLOCK_SIZE];
|
||||
uint16_t segment;
|
||||
uint16_t index;
|
||||
uint16_t data;
|
||||
};
|
||||
|
||||
struct rt11_dirent
|
||||
{
|
||||
uint16_t status;
|
||||
uint16_t filename[3];
|
||||
uint16_t time;
|
||||
// synthetic
|
||||
uint64_t filesize;
|
||||
uint16_t data;
|
||||
};
|
||||
|
||||
enum rt11_status
|
||||
{
|
||||
E_PRE = 0000020,
|
||||
E_TENT = 0000400,
|
||||
E_MPTY = 0001000,
|
||||
E_PERM = 0002000,
|
||||
E_EOS = 0004000,
|
||||
E_READ = 0040000,
|
||||
E_PROT = 0100000
|
||||
};
|
||||
|
||||
enum creation_policy_t
|
||||
{
|
||||
CREATE_NONE,
|
||||
CREATE_FILE,
|
||||
};
|
||||
|
||||
|
||||
util::arbitrary_datetime _rt11_crack_time(uint16_t rt11_time)
|
||||
{
|
||||
util::arbitrary_datetime dt;
|
||||
|
||||
dt.second = 0;
|
||||
dt.minute = 0;
|
||||
dt.hour = 0;
|
||||
dt.day_of_month = (rt11_time >> 5) & 31;
|
||||
dt.month = (rt11_time >> 10) & 15;
|
||||
dt.year = 1972 + (rt11_time & 31) + 32 * ((rt11_time >> 14) & 3);
|
||||
|
||||
return dt;
|
||||
}
|
||||
|
||||
imgtool::datetime rt11_crack_time(uint16_t rt11_time)
|
||||
{
|
||||
util::arbitrary_datetime dt;
|
||||
imgtool::datetime it;
|
||||
|
||||
if (rt11_time == 0)
|
||||
{
|
||||
return imgtool::datetime(imgtool::datetime::datetime_type::NONE, dt);
|
||||
}
|
||||
|
||||
dt = _rt11_crack_time(rt11_time);
|
||||
|
||||
it = imgtool::datetime(imgtool::datetime::datetime_type::LOCAL, dt);
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
void rt11_from_rad50(char *ascii, uint16_t *rad50, int num)
|
||||
{
|
||||
const char rad[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$.%0123456789:";
|
||||
|
||||
for (int i = 0, j = 0; i < num; i++)
|
||||
{
|
||||
ascii[j++] = rad[ rad50[i] / (050 * 050)];
|
||||
ascii[j++] = rad[(rad50[i] / 050) % 050];
|
||||
ascii[j++] = rad[ rad50[i] % 050];
|
||||
}
|
||||
}
|
||||
|
||||
void rt11_filename_from_rad50(char *ascii, uint16_t *rad50)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
rt11_from_rad50(&ascii[0], &rad50[0], 2);
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
if (ascii[i] == ' ') break;
|
||||
}
|
||||
ascii[i++] = '.';
|
||||
rt11_from_rad50(&ascii[i], &rad50[2], 1);
|
||||
for (j = i; j < i + 3; j++)
|
||||
{
|
||||
if (ascii[j] == ' ') break;
|
||||
}
|
||||
ascii[j] = '\0';
|
||||
}
|
||||
|
||||
int is_file_storagetype(uint16_t status)
|
||||
{
|
||||
return !(status & (E_MPTY | E_EOS | E_TENT));
|
||||
}
|
||||
|
||||
rt11_diskinfo *get_rt11_info(imgtool::image &image)
|
||||
{
|
||||
return (rt11_diskinfo *)imgtool_floppy_extrabytes(image);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static imgtoolerr_t rt11_image_get_geometry(imgtool::image &image, uint32_t *tracks, uint32_t *heads, uint32_t *sectors)
|
||||
{
|
||||
const rt11_diskinfo *di = get_rt11_info(image);
|
||||
|
||||
*sectors = di->sectors;
|
||||
*heads = di->heads;
|
||||
*tracks = di->tracks;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t rt11_get_sector_position(imgtool::image &image, uint32_t sector_index,
|
||||
uint32_t &head, uint32_t &track, uint32_t §or)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
rt11_diskinfo *di = get_rt11_info(image);
|
||||
uint32_t tracks, heads, sectors;
|
||||
|
||||
err = image.get_geometry(&tracks, &heads, §ors);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
track = sector_index / sectors / heads;
|
||||
head = (sector_index / sectors) % heads;
|
||||
|
||||
// map 1-based sector numbers, possibly interleaved, to sector indexes
|
||||
if (track != di->cached_track || head != di->cached_head)
|
||||
{
|
||||
memset(di->map, -1, sizeof(di->map));
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
int sector_id;
|
||||
|
||||
if (floppy_get_indexed_sector_info(imgtool_floppy(image), head, track, i, NULL, NULL, §or_id, NULL, NULL))
|
||||
continue;
|
||||
|
||||
if (sector_id > 0 && sector_id <= sectors)
|
||||
di->map[sector_id - 1] = i;
|
||||
}
|
||||
di->cached_track = track;
|
||||
di->cached_head = head;
|
||||
}
|
||||
|
||||
if (di->map[(sector_index % sectors)] < 0)
|
||||
{
|
||||
return IMGTOOLERR_SEEKERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
sector = di->map[(sector_index % sectors)];
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static imgtoolerr_t rt11_image_readblock(imgtool::image &image, void *buffer, uint64_t block)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
floperr_t ferr;
|
||||
const rt11_diskinfo *di = get_rt11_info(image);
|
||||
uint32_t track, head, sector;
|
||||
unsigned long flags;
|
||||
|
||||
if (di->sector_size == 256)
|
||||
{
|
||||
err = rt11_get_sector_position(image, block * 2, head, track, sector);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ferr = floppy_read_indexed_sector(imgtool_floppy(image), head, track, sector, 0, buffer, 256);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), head, track, sector, NULL, NULL, NULL, NULL, &flags);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
if (flags)
|
||||
return IMGTOOLERR_READERROR;
|
||||
|
||||
err = rt11_get_sector_position(image, (block * 2) + 1, head, track, sector);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ferr = floppy_read_indexed_sector(imgtool_floppy(image), head, track, sector, 0, ((uint8_t *) buffer) + 256, 256);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), head, track, sector, NULL, NULL, NULL, NULL, &flags);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
if (flags)
|
||||
return IMGTOOLERR_READERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = rt11_get_sector_position(image, block, head, track, sector);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ferr = floppy_read_indexed_sector(imgtool_floppy(image), head, track, sector, 0, buffer, BLOCK_SIZE);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), head, track, sector, NULL, NULL, NULL, NULL, &flags);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
if (flags)
|
||||
return IMGTOOLERR_READERROR;
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t rt11_probe_geometry(imgtool::image &image)
|
||||
{
|
||||
floperr_t ferr;
|
||||
rt11_diskinfo *di = get_rt11_info(image);
|
||||
|
||||
// MX (11x256 byte sectors) or MY (10x512 byte sectors)?
|
||||
ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), 0, 0, 0, NULL, NULL, NULL, &di->sector_size, NULL);
|
||||
if (ferr)
|
||||
return imgtool_floppy_error(ferr);
|
||||
|
||||
if (di->sector_size == 256)
|
||||
di->sectors = 11;
|
||||
else
|
||||
di->sectors = 10;
|
||||
|
||||
// double- or single-sided?
|
||||
ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), 1, 0, 0, NULL, NULL, NULL, NULL, NULL);
|
||||
if (ferr)
|
||||
di->heads = 1;
|
||||
else
|
||||
di->heads = 2;
|
||||
|
||||
// 80 or 40 tracks?
|
||||
ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), 0, 50, 0, NULL, NULL, NULL, NULL, NULL);
|
||||
if (ferr)
|
||||
di->tracks = 40;
|
||||
else
|
||||
di->tracks = 80;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t rt11_image_open(imgtool::image &image, imgtool::stream::ptr &&dummy)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
rt11_diskinfo *di = get_rt11_info(image);
|
||||
|
||||
di->cached_head = -1;
|
||||
di->cached_track = -1;
|
||||
memset(di->map, -1, sizeof(di->map));
|
||||
// dummy values for rt11_image_readblock
|
||||
di->tracks = 40;
|
||||
di->heads = 1;
|
||||
|
||||
err = rt11_probe_geometry(image);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* load home block */
|
||||
err = rt11_image_readblock(image, buffer, HOME_BLOCK);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
di->directory_start = pick_integer_le(buffer, 0724, 2);
|
||||
|
||||
// real-world images seem to never have a valid checksum, but directory_start is always 6
|
||||
#if 0
|
||||
uint16_t tmp, cksum;
|
||||
|
||||
tmp = 0;
|
||||
cksum = pick_integer_le(buffer, 0776, 2);
|
||||
for (int i = 0; i < 510; i+=2)
|
||||
{
|
||||
tmp += pick_integer_le(buffer, i, 2);
|
||||
}
|
||||
|
||||
/* sanity check these values */
|
||||
if (cksum != tmp)
|
||||
{
|
||||
fprintf(stderr, "cksum stored:computed %04x:%04x\n", cksum, tmp);
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
}
|
||||
#endif
|
||||
if (di->directory_start != 6)
|
||||
{
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
}
|
||||
|
||||
/* load first directory segment */
|
||||
err = rt11_image_readblock(image, buffer, di->directory_start);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
di->total_segments = pick_integer_le(buffer, 0, 2);
|
||||
di->last_segment = pick_integer_le(buffer, 4, 2);
|
||||
di->dirent_size = (pick_integer_le(buffer, 6, 2) + 7) * 2;
|
||||
di->dirents_per_block = (2 * BLOCK_SIZE - 10) / di->dirent_size;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static void rt11_image_info(imgtool::image &image, std::ostream &stream)
|
||||
{
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
char system[4];
|
||||
char vid[13], oid[13], sid[13];
|
||||
|
||||
rt11_image_readblock(image, buffer, HOME_BLOCK);
|
||||
rt11_from_rad50(system, (uint16_t *)&buffer[0726], 1);
|
||||
system[3] = '\0';
|
||||
|
||||
memcpy(vid, &buffer[0730], 12);
|
||||
memcpy(oid, &buffer[0744], 12);
|
||||
memcpy(sid, &buffer[0760], 12);
|
||||
vid[12] = '\0';
|
||||
oid[12] = '\0';
|
||||
sid[12] = '\0';
|
||||
|
||||
stream << "System version: '" << system << "', System ID: '" << sid << "', Volume ID: '" << vid << "', Owner: '" << oid << "'";
|
||||
}
|
||||
|
||||
// directory operations
|
||||
|
||||
static imgtoolerr_t rt11_enum_seek(imgtool::image &image,
|
||||
rt11_direnum *rt11enum, uint16_t segment, uint16_t index)
|
||||
{
|
||||
const rt11_diskinfo *di = get_rt11_info(image);
|
||||
imgtoolerr_t err;
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
|
||||
if (rt11enum->segment != segment)
|
||||
{
|
||||
if (segment != 0)
|
||||
{
|
||||
err = rt11_image_readblock(image, buffer, di->directory_start + (segment - 1) * 2);
|
||||
if (err)
|
||||
return err;
|
||||
memcpy(rt11enum->segment_data, buffer, sizeof(buffer));
|
||||
|
||||
err = rt11_image_readblock(image, buffer, di->directory_start + (segment - 1) * 2 + 1);
|
||||
if (err)
|
||||
return err;
|
||||
memcpy(&rt11enum->segment_data[BLOCK_SIZE], buffer, sizeof(buffer));
|
||||
|
||||
rt11enum->data = pick_integer_le(rt11enum->segment_data, 8, 2);
|
||||
}
|
||||
rt11enum->segment = segment;
|
||||
}
|
||||
|
||||
rt11enum->index = index;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t rt11_get_next_dirent(imgtool::image &image,
|
||||
rt11_direnum *rt11enum, rt11_dirent &rt_ent)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
const rt11_diskinfo *di = get_rt11_info(image);
|
||||
uint32_t next_segment, next_index;
|
||||
uint32_t offset;
|
||||
|
||||
memset(&rt_ent, 0, sizeof(rt_ent));
|
||||
|
||||
/* have we hit the end of the file? */
|
||||
if (rt11enum->segment == 0)
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
|
||||
/* populate the resulting dirent */
|
||||
offset = (rt11enum->index * di->dirent_size) + 10;
|
||||
rt_ent.status = pick_integer_le(rt11enum->segment_data, offset, 2);
|
||||
memcpy(rt_ent.filename, &rt11enum->segment_data[offset + 2], 6);
|
||||
rt_ent.filesize = pick_integer_le(rt11enum->segment_data, offset + 8, 2) * BLOCK_SIZE;
|
||||
rt_ent.data = rt11enum->data;
|
||||
rt_ent.time = pick_integer_le(rt11enum->segment_data, offset + 12, 2);
|
||||
rt11enum->data += pick_integer_le(rt11enum->segment_data, offset + 8, 2);
|
||||
|
||||
/* identify next entry */
|
||||
next_segment = rt11enum->segment;
|
||||
next_index = rt11enum->index + 1;
|
||||
if (next_index >= di->dirents_per_block || (rt_ent.status & E_EOS))
|
||||
{
|
||||
next_segment = pick_integer_le(rt11enum->segment_data, 2, 2);
|
||||
next_index = 0;
|
||||
}
|
||||
|
||||
if (next_segment > di->total_segments || next_segment > di->last_segment)
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
|
||||
/* seek next segment */
|
||||
err = rt11_enum_seek(image, rt11enum, next_segment, next_index);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t rt11_image_beginenum(imgtool::directory &enumeration, const char *path)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
imgtool::image &image(enumeration.image());
|
||||
|
||||
/* seek initial block */
|
||||
err = rt11_enum_seek(image, (rt11_direnum *) enumeration.extra_bytes(), 1, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t rt11_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
imgtool::image &image(enumeration.image());
|
||||
rt11_direnum *rt11enum = (rt11_direnum *)enumeration.extra_bytes();
|
||||
rt11_dirent rt_ent;
|
||||
|
||||
do
|
||||
{
|
||||
err = rt11_get_next_dirent(image, rt11enum, rt_ent);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
while (rt11enum->segment && !is_file_storagetype(rt_ent.status));
|
||||
|
||||
/* end of file? */
|
||||
if (rt11enum->segment == 0)
|
||||
{
|
||||
ent.eof = 1;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
ent.directory = 0;
|
||||
ent.lastmodified_time = rt11_crack_time(rt_ent.time);
|
||||
ent.filesize = rt_ent.filesize;
|
||||
|
||||
rt11_filename_from_rad50(ent.filename, rt_ent.filename);
|
||||
|
||||
snprintf(ent.attr, sizeof(ent.attr), "%c%c%c %4d %06o",
|
||||
rt_ent.status & E_PROT ? 'P' : '.',
|
||||
rt_ent.time == 0 ? 'B' : '.',
|
||||
rt_ent.status & E_TENT ? 'T' : '.',
|
||||
rt_ent.data, rt_ent.status);
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
// file operations
|
||||
|
||||
static imgtoolerr_t rt11_lookup_path(imgtool::image &image, const char *path,
|
||||
creation_policy_t create, rt11_direnum *direnum, rt11_dirent *rt_ent)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
|
||||
rt11_direnum my_direnum;
|
||||
if (!direnum)
|
||||
direnum = &my_direnum;
|
||||
|
||||
memset(direnum, 0, sizeof(*direnum));
|
||||
err = rt11_enum_seek(image, direnum, 1, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
uint16_t this_segment;
|
||||
uint32_t this_index;
|
||||
char filename[16];
|
||||
do
|
||||
{
|
||||
this_segment = direnum->segment;
|
||||
this_index = direnum->index;
|
||||
|
||||
err = rt11_get_next_dirent(image, direnum, *rt_ent);
|
||||
if (err)
|
||||
return err;
|
||||
rt11_filename_from_rad50(filename, rt_ent->filename);
|
||||
}
|
||||
while(direnum->segment && (strcmp(path, filename) ||
|
||||
!is_file_storagetype(rt_ent->status)));
|
||||
|
||||
if (!direnum->segment)
|
||||
{
|
||||
/* did not find file; maybe we need to create it */
|
||||
if (create == CREATE_NONE)
|
||||
return IMGTOOLERR_FILENOTFOUND;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we've found the file; seek that dirent */
|
||||
err = rt11_enum_seek(image, direnum, this_segment, this_index);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t rt11_read_bootblock(imgtool::partition &partition, imgtool::stream &stream)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
uint8_t block[BLOCK_SIZE];
|
||||
|
||||
err = rt11_image_readblock(partition.image(), block, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
stream.write(block, sizeof(block));
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t rt11_image_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)
|
||||
{
|
||||
imgtoolerr_t err;
|
||||
imgtool::image &image(partition.image());
|
||||
rt11_dirent rt_ent;
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
|
||||
if (filename == FILENAME_BOOTBLOCK)
|
||||
return rt11_read_bootblock(partition, destf);
|
||||
|
||||
err = rt11_lookup_path(image, filename, CREATE_NONE, NULL, &rt_ent);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for (uint16_t i = rt_ent.data; i < rt_ent.data + (rt_ent.filesize / BLOCK_SIZE); i++)
|
||||
{
|
||||
err = rt11_image_readblock(image, buffer, i);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
destf.write(buffer, BLOCK_SIZE);
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void rt11_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
||||
case IMGTOOLINFO_INT_PREFER_UCASE: info->i = 1; break;
|
||||
case IMGTOOLINFO_INT_OPEN_IS_STRICT: info->i = 1; break;
|
||||
case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME: info->i = 1; break;
|
||||
case IMGTOOLINFO_INT_SUPPORTS_BOOTBLOCK: info->i = 1; break;
|
||||
case IMGTOOLINFO_INT_BLOCK_SIZE: info->i = BLOCK_SIZE; break;
|
||||
case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(rt11_diskinfo); break;
|
||||
case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(rt11_direnum); break;
|
||||
|
||||
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
||||
case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "rt11"); break;
|
||||
case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "RT11 format"); break;
|
||||
case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break;
|
||||
case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), EOLN_CRLF); break;
|
||||
|
||||
/* --- the following bits of info are returned as pointers to data or functions --- */
|
||||
case IMGTOOLINFO_PTR_INFO: info->info = rt11_image_info; break;
|
||||
case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break;
|
||||
case IMGTOOLINFO_PTR_GET_GEOMETRY: info->get_geometry = rt11_image_get_geometry; break;
|
||||
case IMGTOOLINFO_PTR_READ_BLOCK: info->read_block = rt11_image_readblock; break;
|
||||
case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = rt11_image_beginenum; break;
|
||||
case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = rt11_image_nextenum; break;
|
||||
case IMGTOOLINFO_PTR_READ_FILE: info->read_file = rt11_image_readfile; break;
|
||||
|
||||
case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = rt11_image_open; break;
|
||||
case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_default; break;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,974 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Dirk Best
|
||||
/****************************************************************************
|
||||
|
||||
vzdos.cpp
|
||||
|
||||
Laser/VZ disk images
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "imgtool.h"
|
||||
#include "filter.h"
|
||||
#include "iflopimg.h"
|
||||
|
||||
#include "formats/vt_dsk_legacy.h"
|
||||
|
||||
#include "formats/imageutl.h"
|
||||
#include "corestr.h"
|
||||
#include "opresolv.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
/*
|
||||
|
||||
sector format
|
||||
|
||||
| GAP1 | IDAM | GAP2 | DATA | CHECKSUM | ...
|
||||
|
||||
GAP1 = 80 80 80 80 80 00 6
|
||||
IDAM = FE E7 18 C3 Track# Sector# Checksum-8 of prev. 2 bytes 7
|
||||
GAP2 = 80 80 80 80 80 80 00 C3 18 E7 FE 11
|
||||
DATA = 126 bytes actual data 126
|
||||
= nextTrack# nextSector# 2
|
||||
CHECKSUM = Checksum-16 of the previous 128 bytes 2
|
||||
---
|
||||
154 bytes
|
||||
|
||||
sectors 0 to 14 are used for the disk directory. sector 15 is used
|
||||
as track map, with one bit for each sector used.
|
||||
|
||||
*/
|
||||
|
||||
#define DATA_SIZE (126)
|
||||
#define SECTOR_SIZE (0x9b)
|
||||
#define MAX_DIRENTS (15*8)
|
||||
|
||||
/* vzdos directry entry */
|
||||
struct vzdos_dirent
|
||||
{
|
||||
char ftype;
|
||||
char delimitor;
|
||||
char fname[8];
|
||||
uint8_t start_track;
|
||||
uint8_t start_sector;
|
||||
uint16_t start_address;
|
||||
uint16_t end_address;
|
||||
|
||||
vzdos_dirent()
|
||||
{
|
||||
ftype = delimitor = '\0';
|
||||
fname[0] = '\0';
|
||||
start_track = start_sector = 0;
|
||||
start_address = end_address = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct vz_iterator
|
||||
{
|
||||
int index;
|
||||
int eof;
|
||||
};
|
||||
|
||||
static const uint8_t sector_order[] =
|
||||
{
|
||||
0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13
|
||||
};
|
||||
|
||||
/*********************************************************************
|
||||
Internal functions
|
||||
*********************************************************************/
|
||||
|
||||
/* get length of filename without trailing spaces */
|
||||
static int vzdos_get_fname_len(const char *fname)
|
||||
{
|
||||
int len;
|
||||
|
||||
for (len = 7; len > 0; len--)
|
||||
if (fname[len] != 0x20)
|
||||
break;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* calculate checksum-16 of buffer */
|
||||
static uint16_t chksum16(uint8_t *buffer, int len)
|
||||
{
|
||||
int i;
|
||||
uint16_t sum = 0;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
sum += buffer[i];
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* returns the offset where the actual sector data starts */
|
||||
static imgtoolerr_t vzdos_get_data_start(imgtool::image &img, int track, int sector, int *start)
|
||||
{
|
||||
imgtoolerr_t ret;
|
||||
uint8_t buffer[25]; /* enough to read the sector header */
|
||||
|
||||
ret = (imgtoolerr_t)floppy_read_sector(imgtool_floppy(img), 0, track, sector_order[sector], 0, &buffer, sizeof(buffer));
|
||||
if (ret) return ret;
|
||||
|
||||
/* search for start of data */
|
||||
if (memcmp(buffer + 19, "\xC3\x18\xE7\xFE", 4) == 0)
|
||||
*start = 23;
|
||||
else if (memcmp(buffer + 20, "\xC3\x18\xE7\xFE", 4) == 0)
|
||||
*start = 24;
|
||||
else if (memcmp(buffer + 21, "\xC3\x18\xE7\xFE", 4) == 0)
|
||||
*start = 25;
|
||||
else
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* return the actual data of a sector */
|
||||
static imgtoolerr_t vzdos_read_sector_data(imgtool::image &img, int track, int sector, uint8_t *data)
|
||||
{
|
||||
int ret, data_start;
|
||||
uint8_t buffer[DATA_SIZE + 4]; /* data + checksum */
|
||||
|
||||
ret = vzdos_get_data_start(img, track, sector, &data_start);
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
|
||||
ret = floppy_read_sector(imgtool_floppy(img), 0, track, sector_order[sector], data_start, &buffer, sizeof(buffer));
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
|
||||
/* verify sector checksums */
|
||||
if (pick_integer_le(buffer, DATA_SIZE + 2, 2) != chksum16(buffer, DATA_SIZE + 2))
|
||||
return IMGTOOLERR_CORRUPTFILE;
|
||||
|
||||
memcpy(data, &buffer, DATA_SIZE + 2);
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* write data to sector */
|
||||
static imgtoolerr_t vzdos_write_sector_data(imgtool::image &img, int track, int sector, uint8_t *data)
|
||||
{
|
||||
int ret, data_start;
|
||||
uint8_t buffer[DATA_SIZE + 4]; /* data + checksum */
|
||||
|
||||
ret = vzdos_get_data_start(img, track, sector, &data_start);
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
|
||||
memcpy(buffer, data, DATA_SIZE + 2);
|
||||
place_integer_le(buffer, DATA_SIZE + 2, 2, chksum16(data, DATA_SIZE + 2));
|
||||
|
||||
ret = floppy_write_sector(imgtool_floppy(img), 0, track, sector_order[sector], data_start, buffer, sizeof(buffer), 0); /* TODO: pass ddam argument from imgtool */
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* write formatted empty sector */
|
||||
static imgtoolerr_t vzdos_clear_sector(imgtool::image &img, int track, int sector)
|
||||
{
|
||||
uint8_t data[DATA_SIZE + 2];
|
||||
|
||||
memset(data, 0x00, sizeof(data));
|
||||
|
||||
return vzdos_write_sector_data(img, track, sector, data);
|
||||
}
|
||||
|
||||
/* return a directory entry for an index */
|
||||
static imgtoolerr_t vzdos_get_dirent(imgtool::image &img, int index, vzdos_dirent *ent)
|
||||
{
|
||||
int ret, entry;
|
||||
uint8_t buffer[DATA_SIZE + 2];
|
||||
|
||||
ret = vzdos_read_sector_data(img, 0, (int) index / 8, buffer);
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
|
||||
entry = ((index % 8) * sizeof(vzdos_dirent));
|
||||
|
||||
memcpy(ent, &buffer[entry], 10);
|
||||
ent->start_track = pick_integer_le(&buffer[entry], 10, 1);
|
||||
ent->start_sector = pick_integer_le(&buffer[entry], 11, 1);
|
||||
ent->start_address = pick_integer_le(&buffer[entry], 12, 2);
|
||||
ent->end_address = pick_integer_le(&buffer[entry], 14, 2);
|
||||
|
||||
if (ent->ftype == 0x00)
|
||||
return IMGTOOLERR_FILENOTFOUND;
|
||||
|
||||
/* check values */
|
||||
if (ent->start_track > 39)
|
||||
return IMGTOOLERR_CORRUPTFILE;
|
||||
|
||||
if (ent->start_sector > 15)
|
||||
return IMGTOOLERR_CORRUPTFILE;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* save a directory entry to disk */
|
||||
static imgtoolerr_t vzdos_set_dirent(imgtool::image &img, int index, vzdos_dirent ent)
|
||||
{
|
||||
int ret, entry;
|
||||
uint8_t buffer[DATA_SIZE + 2];
|
||||
|
||||
/* read current sector with entries */
|
||||
ret = vzdos_read_sector_data(img, 0, (int) index / 8, buffer);
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
|
||||
entry = ((index % 8) * sizeof(vzdos_dirent));
|
||||
|
||||
memcpy(&buffer[entry], &ent, 10);
|
||||
place_integer_le(buffer, entry + 10, 1, ent.start_track);
|
||||
place_integer_le(buffer, entry + 11, 1, ent.start_sector);
|
||||
place_integer_le(buffer, entry + 12, 2, ent.start_address);
|
||||
place_integer_le(buffer, entry + 14, 2, ent.end_address);
|
||||
|
||||
/* save new sector */
|
||||
ret = vzdos_write_sector_data(img, 0, (int) index / 8, buffer);
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* clear a directory entry */
|
||||
static imgtoolerr_t vzdos_clear_dirent(imgtool::image &img, int index)
|
||||
{
|
||||
int ret;
|
||||
vzdos_dirent entry;
|
||||
|
||||
memset(&entry, 0x00, sizeof(vzdos_dirent));
|
||||
|
||||
ret = vzdos_set_dirent(img, index, entry);
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* search the index for a directory entry */
|
||||
static imgtoolerr_t vzdos_searchentry(imgtool::image &image, const char *fname, int *entry) {
|
||||
int i, len, ret;
|
||||
vzdos_dirent ent;
|
||||
char filename[9];
|
||||
|
||||
/* check for invalid filenames */
|
||||
if (strlen(fname) > 8)
|
||||
return IMGTOOLERR_BADFILENAME;
|
||||
|
||||
/* TODO: check for invalid characters */
|
||||
|
||||
*entry = -1;
|
||||
|
||||
for (i = 0; i < MAX_DIRENTS; i++) {
|
||||
ret = vzdos_get_dirent(image, i, &ent);
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
|
||||
len = vzdos_get_fname_len(ent.fname) + 1;
|
||||
|
||||
if (strlen(fname) != len)
|
||||
continue;
|
||||
|
||||
memset(filename, 0x00, sizeof(filename));
|
||||
memcpy(filename, ent.fname, len);
|
||||
|
||||
if (!core_stricmp(fname, filename)) {
|
||||
*entry = i;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (*entry == -1)
|
||||
return IMGTOOLERR_FILENOTFOUND;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* return a directory entry for a filename */
|
||||
static imgtoolerr_t vzdos_get_dirent_fname(imgtool::image &img, const char *fname, vzdos_dirent *ent)
|
||||
{
|
||||
int ret, index;
|
||||
|
||||
ret = vzdos_searchentry(img, fname, &index);
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
|
||||
ret = vzdos_get_dirent(img, index, ent);
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t vzdos_toggle_trackmap(imgtool::image &img, int track, int sector, int clear)
|
||||
{
|
||||
int ret, value, bit;
|
||||
uint8_t buffer[DATA_SIZE + 2];
|
||||
|
||||
/* get trackmap from image */
|
||||
ret = vzdos_read_sector_data(img, 0, 15, buffer);
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
|
||||
value = (int) (floor(((double)track * 16 + sector) / 8) - 1);
|
||||
bit = 0x01 << ((track * 16 + sector) % 8);
|
||||
|
||||
if (clear)
|
||||
buffer[value-1] &= ~bit;
|
||||
else
|
||||
buffer[value-1] |= bit;
|
||||
|
||||
ret = vzdos_write_sector_data(img, 0, 15, buffer);
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* clear a trackmap entry */
|
||||
static imgtoolerr_t vzdos_clear_trackmap(imgtool::image &img, int track, int sector)
|
||||
{
|
||||
return vzdos_toggle_trackmap(img, track, sector, 1);
|
||||
}
|
||||
|
||||
/* enable a trackmap entry */
|
||||
static imgtoolerr_t vzdos_set_trackmap(imgtool::image &img, int track, int sector)
|
||||
{
|
||||
return vzdos_toggle_trackmap(img, track, sector, 0);
|
||||
}
|
||||
|
||||
/* return the status of a trackmap entry */
|
||||
static imgtoolerr_t vzdos_get_trackmap(imgtool::image &img, int track, int sector, int *used)
|
||||
{
|
||||
int ret, value, bit;
|
||||
uint8_t buffer[DATA_SIZE + 2];
|
||||
|
||||
/* get trackmap from image */
|
||||
ret = vzdos_read_sector_data(img, 0, 15, buffer);
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
|
||||
value = (int) (floor(((double)track * 16 + sector) / 8) - 1);
|
||||
bit = 0x01 << ((track * 16 + sector) % 8);
|
||||
|
||||
if (buffer[value-1] & bit)
|
||||
*used = 1;
|
||||
else
|
||||
*used = 0;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* return the next free sector */
|
||||
static imgtoolerr_t vzdos_free_trackmap(imgtool::image &img, int *track, int *sector)
|
||||
{
|
||||
int ret, used = 0;
|
||||
|
||||
for (*track = 1; *track < 40; (*track)++) {
|
||||
for (*sector = 0; *sector < 16; (*sector)++) {
|
||||
ret = vzdos_get_trackmap(img, *track, *sector, &used);
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
if (!used) return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return IMGTOOLERR_NOSPACE;
|
||||
}
|
||||
|
||||
static imgtoolerr_t vzdos_write_formatted_sector(imgtool::image &img, int track, int sector)
|
||||
{
|
||||
int ret;
|
||||
uint8_t sector_data[DATA_SIZE + 4 + 24];
|
||||
|
||||
static const uint8_t sector_header[24] = {
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0xFE, 0xE7,
|
||||
0x18, 0xC3, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x00, 0xC3, 0x18, 0xE7, 0xFE
|
||||
};
|
||||
|
||||
memset(sector_data, 0x00, sizeof(sector_data));
|
||||
memcpy(sector_data, sector_header, sizeof(sector_header));
|
||||
|
||||
sector_data[10] = (uint8_t) track; /* current track */
|
||||
sector_data[11] = (uint8_t) sector; /* current sector */
|
||||
sector_data[12] = (uint8_t) track + sector; /* checksum-8 */
|
||||
|
||||
ret = floppy_write_sector(imgtool_floppy(img), 0, track, sector_order[sector], 0, sector_data, sizeof(sector_data), 0); /* TODO: pass ddam argument from imgtool */
|
||||
if (ret) return (imgtoolerr_t)ret;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
Imgtool module code
|
||||
*********************************************************************/
|
||||
|
||||
static imgtoolerr_t vzdos_diskimage_beginenum(imgtool::directory &enumeration, const char *path)
|
||||
{
|
||||
vz_iterator *iter;
|
||||
|
||||
iter = (vz_iterator *) enumeration.extra_bytes();
|
||||
if (!iter) return IMGTOOLERR_OUTOFMEMORY;
|
||||
|
||||
iter->index = 1;
|
||||
iter->eof = 0;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t vzdos_diskimage_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent)
|
||||
{
|
||||
vz_iterator *iter = (vz_iterator *) enumeration.extra_bytes();
|
||||
|
||||
if (iter->eof == 1 || iter->index > MAX_DIRENTS) {
|
||||
ent.eof = 1;
|
||||
|
||||
} else {
|
||||
const char *type;
|
||||
int ret, len;
|
||||
vzdos_dirent dirent;
|
||||
|
||||
ret = vzdos_get_dirent(enumeration.image(), iter->index - 1, &dirent);
|
||||
|
||||
if (ret == IMGTOOLERR_FILENOTFOUND)
|
||||
{
|
||||
iter->eof = 1;
|
||||
ent.eof = 1;
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
if (ret == IMGTOOLERR_CORRUPTFILE)
|
||||
ent.corrupt = 1;
|
||||
|
||||
/* kill trailing spaces */
|
||||
for (len = 7; len > 0; len--) {
|
||||
if (dirent.fname[len] != 0x20) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(ent.filename, &dirent.fname, len + 1);
|
||||
ent.filesize = dirent.end_address - dirent.start_address;
|
||||
|
||||
switch (dirent.ftype)
|
||||
{
|
||||
case 0x01: type = "Deleted"; break;
|
||||
case 'T': type = "Basic"; break;
|
||||
case 'B': type = "Binary"; break;
|
||||
case 'D': type = "Data"; break;
|
||||
case 'F': type = "Quickwrite"; break;
|
||||
case 'A': type = "Assembler"; break;
|
||||
case 'S': type = "Diskops"; break;
|
||||
case 'W': type = "Wordpro"; break;
|
||||
default: type = "Unknown";
|
||||
}
|
||||
|
||||
snprintf(ent.attr, std::size(ent.attr), "%s", type);
|
||||
|
||||
iter->index++;
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* TRK 0 sector 15 is used to hold the track map of the disk with one bit
|
||||
corresponding to a sector used. */
|
||||
static imgtoolerr_t vzdos_diskimage_freespace(imgtool::partition &partition, uint64_t *size)
|
||||
{
|
||||
imgtoolerr_t ret;
|
||||
int i;
|
||||
imgtool::image &image(partition.image());
|
||||
uint8_t c, v, buffer[DATA_SIZE + 2];
|
||||
*size = 0;
|
||||
|
||||
ret = vzdos_read_sector_data(image, 0, 15, buffer);
|
||||
if (ret) return ret;
|
||||
|
||||
for (i = 0; i < DATA_SIZE; i++) {
|
||||
v = buffer[i];
|
||||
for (c = 0; v; c++) {
|
||||
v &= v - 1;
|
||||
}
|
||||
*size += c * DATA_SIZE;
|
||||
}
|
||||
*size = (DATA_SIZE * 16 * 39) - *size;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t vzdos_diskimage_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)
|
||||
{
|
||||
imgtoolerr_t ret;
|
||||
imgtool::image &image(partition.image());
|
||||
int filesize, track, sector;
|
||||
vzdos_dirent ent;
|
||||
uint8_t buffer[DATA_SIZE + 2];
|
||||
|
||||
ret = vzdos_get_dirent_fname(image, filename, &ent);
|
||||
if (ret) return ret;
|
||||
|
||||
filesize = ent.end_address - ent.start_address;
|
||||
track = ent.start_track;
|
||||
sector = ent.start_sector;
|
||||
|
||||
while (filesize > 0) {
|
||||
int towrite;
|
||||
|
||||
ret = vzdos_read_sector_data(image, track, sector, buffer);
|
||||
if (ret) return ret;
|
||||
|
||||
/* detect sectors pointing to themselfs */
|
||||
if ((track == (int)pick_integer_le(buffer, DATA_SIZE, 1)) && (sector == (int)pick_integer_le(buffer, DATA_SIZE + 1, 1)))
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
|
||||
/* load next track and sector values */
|
||||
track = pick_integer_le(buffer, DATA_SIZE, 1);
|
||||
sector = pick_integer_le(buffer, DATA_SIZE + 1, 1);
|
||||
|
||||
/* track 0 is invalid */
|
||||
if ((track == 0) && (filesize > DATA_SIZE))
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
|
||||
/* write either DATA_SIZE or the remaining bytes */
|
||||
towrite = filesize > DATA_SIZE ? DATA_SIZE : filesize;
|
||||
|
||||
if (destf.write(buffer, towrite) != towrite)
|
||||
return IMGTOOLERR_WRITEERROR;
|
||||
|
||||
filesize -= DATA_SIZE;
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* deletes directory entry, clears trackmap entries and sectors */
|
||||
static imgtoolerr_t vzdos_diskimage_deletefile(imgtool::partition &partition, const char *fname)
|
||||
{
|
||||
imgtoolerr_t ret;
|
||||
imgtool::image &img(partition.image());
|
||||
int index, filesize, track, sector, next_track, next_sector;
|
||||
vzdos_dirent entry, next_entry;
|
||||
uint8_t buffer[DATA_SIZE + 2];
|
||||
|
||||
ret = vzdos_get_dirent_fname(img, fname, &entry);
|
||||
if (ret) return ret;
|
||||
|
||||
ret = (imgtoolerr_t)floppy_read_sector(imgtool_floppy(img), 0, entry.start_track, sector_order[entry.start_sector], 24, &buffer, DATA_SIZE + 2);
|
||||
if (ret) return ret;
|
||||
|
||||
filesize = entry.end_address - entry.start_address;
|
||||
track = entry.start_track;
|
||||
sector = entry.start_sector;
|
||||
|
||||
/* delete directory entry */
|
||||
ret = vzdos_searchentry(img, fname, &index);
|
||||
if (ret) return ret;
|
||||
|
||||
ret = vzdos_get_dirent(img, index + 1, &next_entry);
|
||||
|
||||
if (ret == IMGTOOLERR_FILENOTFOUND) {
|
||||
/* we are the last directory entry, just delete it */
|
||||
ret = vzdos_clear_dirent(img, index);
|
||||
if (ret) return ret;
|
||||
|
||||
} else if (ret) {
|
||||
/* an error occurred */
|
||||
return ret;
|
||||
|
||||
} else {
|
||||
ret = vzdos_set_dirent(img, index++, next_entry);
|
||||
if (ret) return ret;
|
||||
|
||||
while ((ret = vzdos_get_dirent(img, index + 1, &next_entry)) != IMGTOOLERR_FILENOTFOUND) {
|
||||
if (ret) return ret;
|
||||
ret = vzdos_set_dirent(img, index++, next_entry);
|
||||
if (ret) return ret;
|
||||
}
|
||||
|
||||
ret = vzdos_clear_dirent(img, index);
|
||||
if (ret) return ret;
|
||||
|
||||
}
|
||||
|
||||
/* clear sectors and trackmap entries */
|
||||
while (filesize > 0) {
|
||||
filesize -= DATA_SIZE;
|
||||
|
||||
/* clear trackmap entry */
|
||||
ret = vzdos_clear_trackmap(img, track, sector);
|
||||
if (ret) return ret;
|
||||
|
||||
/* load next track and sector values */
|
||||
next_track = pick_integer_le(buffer, DATA_SIZE, 1);
|
||||
next_sector = pick_integer_le(buffer, DATA_SIZE + 1, 1);
|
||||
|
||||
/* overwrite sector with default values */
|
||||
ret = vzdos_clear_sector(img, track, sector);
|
||||
if (ret) return ret;
|
||||
|
||||
/* read next sector */
|
||||
track = next_track;
|
||||
sector = next_sector;
|
||||
|
||||
if (filesize > 0) {
|
||||
ret = (imgtoolerr_t)floppy_read_sector(imgtool_floppy(img), 0, track, sector_order[sector], 24, &buffer, DATA_SIZE + 2);
|
||||
if (ret) return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t vzdos_writefile(imgtool::partition &partition, int offset, imgtool::stream &sourcef, vzdos_dirent *entry)
|
||||
{
|
||||
imgtoolerr_t ret;
|
||||
imgtool::image &img(partition.image());
|
||||
int index, track, sector, toread, next_track, next_sector;
|
||||
vzdos_dirent temp_entry;
|
||||
uint64_t filesize = 0, freespace = 0;
|
||||
uint8_t buffer[DATA_SIZE + 2];
|
||||
char filename[9];
|
||||
|
||||
/* is the image writeable? */
|
||||
if (floppy_is_read_only(imgtool_floppy(img)))
|
||||
return IMGTOOLERR_READONLY;
|
||||
|
||||
/* check for already existing filename -> overwrite */
|
||||
strcpy(filename, entry->fname);
|
||||
filename[vzdos_get_fname_len(entry->fname) + 1] = 0x00;
|
||||
ret = vzdos_get_dirent_fname(img, filename, &temp_entry);
|
||||
if (!ret) {
|
||||
/* file already exists, delete it */
|
||||
ret = vzdos_diskimage_deletefile(partition, filename);
|
||||
if (ret) return ret;
|
||||
} else if (ret != IMGTOOLERR_FILENOTFOUND) {
|
||||
/* another error occurred, return it */
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = (imgtoolerr_t) sourcef.seek(offset, SEEK_SET);
|
||||
if (ret) return ret;
|
||||
|
||||
/* check if there is enough space */
|
||||
filesize = sourcef.size() - offset;
|
||||
|
||||
ret = vzdos_diskimage_freespace(partition, &freespace);
|
||||
if (ret) return ret;
|
||||
|
||||
if (filesize > freespace)
|
||||
return IMGTOOLERR_NOSPACE;
|
||||
|
||||
/* get next free track and sector */
|
||||
ret = vzdos_free_trackmap(img, &track, §or);
|
||||
if (ret) return ret;
|
||||
|
||||
entry->end_address = entry->start_address + (unsigned int) filesize;
|
||||
entry->start_track = track;
|
||||
entry->start_sector = sector;
|
||||
|
||||
/* search for next free directory entry */
|
||||
for (index = 0; index < MAX_DIRENTS; index++) {
|
||||
ret = vzdos_get_dirent(img, index, &temp_entry);
|
||||
if (ret == IMGTOOLERR_FILENOTFOUND)
|
||||
break;
|
||||
else if (ret)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/* write directory entry to disk */
|
||||
ret = vzdos_set_dirent(img, index, *entry);
|
||||
if (ret) return ret;
|
||||
|
||||
next_track = 0;
|
||||
next_sector = 0;
|
||||
|
||||
/* write data to disk */
|
||||
while (filesize > 0) {
|
||||
toread = filesize > DATA_SIZE ? DATA_SIZE : filesize;
|
||||
sourcef.read(buffer, toread);
|
||||
|
||||
filesize -= toread;
|
||||
|
||||
/* mark sector as used */
|
||||
ret = vzdos_set_trackmap(img, track, sector);
|
||||
if (ret) return ret;
|
||||
|
||||
/* get track and sector for next sector */
|
||||
if (filesize > 0) {
|
||||
ret = vzdos_free_trackmap(img, &next_track, &next_sector);
|
||||
if (ret) return ret;
|
||||
} else {
|
||||
next_track = 0;
|
||||
next_sector = 0;
|
||||
}
|
||||
buffer[DATA_SIZE] = next_track;
|
||||
buffer[DATA_SIZE + 1] = next_sector;
|
||||
|
||||
/* write sector to disk */
|
||||
ret = vzdos_write_sector_data(img, track, sector, buffer);
|
||||
if (ret) return ret;
|
||||
|
||||
track = next_track;
|
||||
sector = next_sector;
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
/* create a new file or overwrite a file */
|
||||
static imgtoolerr_t vzdos_diskimage_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts)
|
||||
{
|
||||
imgtoolerr_t ret;
|
||||
int ftype;
|
||||
vzdos_dirent entry;
|
||||
|
||||
/* TODO: check for leading spaces and strip */
|
||||
if (strlen(filename) > 8 || strlen(filename) < 1)
|
||||
return IMGTOOLERR_BADFILENAME;
|
||||
|
||||
/* prepare directory entry */
|
||||
ftype = opts->lookup_int('T');
|
||||
|
||||
switch (ftype) {
|
||||
case 0:
|
||||
entry.ftype = 'T';
|
||||
entry.start_address = 31465;
|
||||
break;
|
||||
case 1:
|
||||
entry.ftype = 'B';
|
||||
entry.start_address = 31465; /* ??? */
|
||||
break;
|
||||
case 2:
|
||||
entry.ftype = 'D';
|
||||
entry.start_address = 31465; /* ??? */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
entry.delimitor = ':';
|
||||
memset(&entry.fname, 0x20, 8); /* pad with spaces */
|
||||
memcpy(&entry.fname, filename, strlen(filename));
|
||||
|
||||
/* write file to disk */
|
||||
ret = vzdos_writefile(partition, 0, sourcef, &entry);
|
||||
if (ret) return ret;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t vzdos_diskimage_suggesttransfer(imgtool::partition &partition, const char *fname, imgtool_transfer_suggestion *suggestions, size_t suggestions_length)
|
||||
{
|
||||
imgtoolerr_t ret;
|
||||
imgtool::image &image(partition.image());
|
||||
vzdos_dirent entry;
|
||||
|
||||
if (fname) {
|
||||
ret = vzdos_get_dirent_fname(image, fname, &entry);
|
||||
if (ret) return ret;
|
||||
|
||||
switch (entry.ftype) {
|
||||
case 'B':
|
||||
suggestions[0].viability = SUGGESTION_RECOMMENDED;
|
||||
suggestions[0].filter = filter_vzsnapshot_getinfo;
|
||||
suggestions[0].description = "VZ Snapshot";
|
||||
suggestions[1].viability = SUGGESTION_POSSIBLE;
|
||||
suggestions[1].filter = NULL;
|
||||
suggestions[1].description = "Raw";
|
||||
break;
|
||||
case 'T':
|
||||
suggestions[0].viability = SUGGESTION_RECOMMENDED;
|
||||
suggestions[0].filter = filter_vzsnapshot_getinfo;
|
||||
suggestions[0].description = "VZ Snapshot";
|
||||
suggestions[1].viability = SUGGESTION_POSSIBLE;
|
||||
suggestions[1].filter = NULL;
|
||||
suggestions[1].description = "Raw";
|
||||
suggestions[2].viability = SUGGESTION_POSSIBLE;
|
||||
suggestions[2].filter = filter_vzbas_getinfo;
|
||||
suggestions[2].description = "Tokenized Basic";
|
||||
break;
|
||||
default:
|
||||
suggestions[0].viability = SUGGESTION_RECOMMENDED;
|
||||
suggestions[0].filter = NULL;
|
||||
suggestions[0].description = "Raw";
|
||||
}
|
||||
|
||||
} else {
|
||||
suggestions[0].viability = SUGGESTION_RECOMMENDED;
|
||||
suggestions[0].filter = NULL;
|
||||
suggestions[0].description = "Raw";
|
||||
suggestions[1].viability = SUGGESTION_POSSIBLE;
|
||||
suggestions[1].filter = filter_vzsnapshot_getinfo;
|
||||
suggestions[1].description = "VZ Snapshot";
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t vzdos_diskimage_create(imgtool::image &img, imgtool::stream::ptr &&dummy, util::option_resolution *opts)
|
||||
{
|
||||
imgtoolerr_t ret;
|
||||
int track, sector;
|
||||
|
||||
for (track = 0; track < 40; track++) {
|
||||
for (sector = 0; sector < 16; sector++) {
|
||||
ret = vzdos_write_formatted_sector(img, track, sector);
|
||||
if (ret) return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
Imgtool vz filter code
|
||||
*********************************************************************/
|
||||
|
||||
static imgtoolerr_t vzsnapshot_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)
|
||||
{
|
||||
imgtoolerr_t ret;
|
||||
imgtool::image &image(partition.image());
|
||||
vzdos_dirent entry;
|
||||
uint8_t header[24];
|
||||
|
||||
/* get directory entry from disk */
|
||||
ret = vzdos_get_dirent_fname(image, filename, &entry);
|
||||
if (ret) return ret;
|
||||
|
||||
/* prepare header */
|
||||
header[0] = 'V';
|
||||
header[1] = 'Z';
|
||||
header[2] = 'F';
|
||||
|
||||
switch (entry.ftype) {
|
||||
case 'B':
|
||||
header[3] = '1';
|
||||
header[21] = 0xF1;
|
||||
break;
|
||||
case 'T':
|
||||
header[3] = '0';
|
||||
header[21] = 0xF0;
|
||||
break;
|
||||
default:
|
||||
memset(header, 0x00, 4);
|
||||
header[21] = 0x00;
|
||||
}
|
||||
|
||||
memset(header + 4, 0x00, 17);
|
||||
memcpy(header + 4, entry.fname, vzdos_get_fname_len(entry.fname) + 1);
|
||||
place_integer_le(header, 22, 2, entry.start_address);
|
||||
|
||||
/* write header to file */
|
||||
destf.write(header, sizeof(header));
|
||||
|
||||
/* write data to file */
|
||||
ret = vzdos_diskimage_readfile(partition, filename, "", destf);
|
||||
if (ret) return ret;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
static imgtoolerr_t vzsnapshot_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts)
|
||||
{
|
||||
imgtoolerr_t ret;
|
||||
int fnameopt;
|
||||
vzdos_dirent entry;
|
||||
uint8_t header[24];
|
||||
|
||||
/* get header infos from file */
|
||||
sourcef.read(header, sizeof(header));
|
||||
|
||||
/* prepare directory entry */
|
||||
entry.ftype = header[21] == 0xF1 ? 'B' : 'T';
|
||||
entry.delimitor = ':';
|
||||
entry.start_address = pick_integer_le(header, 22, 2);
|
||||
|
||||
/* filename from header or directly? */
|
||||
fnameopt = opts->lookup_int('F');
|
||||
|
||||
if (fnameopt == 0) {
|
||||
memcpy(&entry.fname, &header[4], 8);
|
||||
} else {
|
||||
/* TODO: check for leading spaces and strip */
|
||||
if (strlen(filename) > 8 || strlen(filename) < 1)
|
||||
return IMGTOOLERR_BADFILENAME;
|
||||
|
||||
memcpy(&entry.fname, filename, strlen(filename) - 3);
|
||||
}
|
||||
|
||||
/* write file to disk */
|
||||
ret = vzdos_writefile(partition, 24, sourcef, &entry);
|
||||
if (ret) return ret;
|
||||
|
||||
return IMGTOOLERR_SUCCESS;
|
||||
}
|
||||
|
||||
void filter_vzsnapshot_getinfo(uint32_t state, union filterinfo *info)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case FILTINFO_STR_NAME: info->s = "vzsnapshot"; break;
|
||||
case FILTINFO_STR_HUMANNAME: info->s = "VZ Snapshot"; break;
|
||||
case FILTINFO_STR_EXTENSION: info->s = "vz"; break;
|
||||
case FILTINFO_PTR_READFILE: info->read_file = vzsnapshot_readfile; break;
|
||||
case FILTINFO_PTR_WRITEFILE: info->write_file = vzsnapshot_writefile; break;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
Imgtool module declaration
|
||||
*********************************************************************/
|
||||
|
||||
OPTION_GUIDE_START(vzdos_writefile_optionguide)
|
||||
OPTION_ENUM_START( 'T', "ftype", "File type" )
|
||||
OPTION_ENUM( 0, "basic", "Basic" )
|
||||
OPTION_ENUM( 1, "binary", "Binary" )
|
||||
OPTION_ENUM( 2, "data", "Data" )
|
||||
OPTION_ENUM_END
|
||||
OPTION_ENUM_START( 'F', "fname", "Filename" )
|
||||
OPTION_ENUM( 0, "intern", "Filename from VZ-Header" )
|
||||
OPTION_ENUM( 1, "extern", "Actual filename" )
|
||||
OPTION_ENUM_END
|
||||
OPTION_GUIDE_END
|
||||
|
||||
/*
|
||||
T Basic Editor File
|
||||
B Binary File
|
||||
D Sequential Access Program Data File
|
||||
F Quickwrite Document
|
||||
A Russell Harrison's Edit Ass. File
|
||||
S Dave Mitchell/Mark Hardwood Edit Ass. File Start Addr A280H
|
||||
S Quickwrite/Diskops System File/Label Except Above
|
||||
W Edit Ass. with Diskops File Start Addr A813H
|
||||
W E & F Word Pro with Patch 3.3 File End Addr D000H
|
||||
W Russell Harrison Word Pro File Except above two
|
||||
*/
|
||||
|
||||
void vzdos_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
||||
case IMGTOOLINFO_INT_PREFER_UCASE: info->i = 1; break;
|
||||
case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(vz_iterator); break;
|
||||
|
||||
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
||||
case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "vzdos"); break;
|
||||
case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "VZ-DOS format"); break;
|
||||
case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break;
|
||||
case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), "T[0]-2;F[0]-1"); break;
|
||||
case IMGTOOLINFO_STR_EOLN: info->s = NULL; break;
|
||||
|
||||
/* --- the following bits of info are returned as pointers to data or functions --- */
|
||||
case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break;
|
||||
case IMGTOOLINFO_PTR_OPEN: info->open = NULL; break;
|
||||
case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = vzdos_diskimage_beginenum; break;
|
||||
case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = vzdos_diskimage_nextenum; break;
|
||||
case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = vzdos_diskimage_freespace; break;
|
||||
case IMGTOOLINFO_PTR_READ_FILE: info->read_file = vzdos_diskimage_readfile; break;
|
||||
case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = vzdos_diskimage_writefile; break;
|
||||
case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = vzdos_diskimage_deletefile; break;
|
||||
case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = vzdos_diskimage_create; break;
|
||||
case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE: info->writefile_optguide = &vzdos_writefile_optionguide; break;
|
||||
case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = vzdos_diskimage_suggesttransfer; break;
|
||||
case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_vz; break;
|
||||
}
|
||||
}
|
@ -1,694 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/***************************************************************************
|
||||
|
||||
stream.cpp
|
||||
|
||||
Code for implementing Imgtool streams
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "stream.h"
|
||||
|
||||
#include "imgtool.h"
|
||||
|
||||
#include "corefile.h"
|
||||
#include "ioprocs.h"
|
||||
#include "unzip.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <zlib.h> // for crc32
|
||||
|
||||
|
||||
namespace imgtool {
|
||||
|
||||
namespace {
|
||||
|
||||
class stream_read_wrapper : public virtual util::random_read
|
||||
{
|
||||
public:
|
||||
stream_read_wrapper(stream::ptr &&stream, std::uint8_t filler) noexcept
|
||||
: m_stream(stream.release())
|
||||
, m_filler(filler)
|
||||
, m_close(true)
|
||||
{
|
||||
assert(m_stream);
|
||||
}
|
||||
|
||||
stream_read_wrapper(stream &stream, std::uint8_t filler) noexcept
|
||||
: m_stream(&stream)
|
||||
, m_filler(filler)
|
||||
, m_close(false)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~stream_read_wrapper()
|
||||
{
|
||||
if (m_close)
|
||||
delete m_stream;
|
||||
}
|
||||
|
||||
virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override
|
||||
{
|
||||
m_stream->seek(offset, whence);
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
virtual std::error_condition tell(std::uint64_t &result) noexcept override
|
||||
{
|
||||
result = m_stream->tell();
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
virtual std::error_condition length(std::uint64_t &result) noexcept override
|
||||
{
|
||||
result = m_stream->size();
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
|
||||
{
|
||||
actual = m_stream->read(buffer, length);
|
||||
if (actual < length)
|
||||
std::memset(reinterpret_cast<std::uint8_t *>(buffer) + actual, m_filler, length - actual);
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
|
||||
{
|
||||
std::uint64_t const pos = m_stream->tell();
|
||||
m_stream->seek(offset, SEEK_SET);
|
||||
actual = m_stream->read(buffer, length);
|
||||
m_stream->seek(pos, SEEK_SET);
|
||||
if (actual < length)
|
||||
std::memset(reinterpret_cast<std::uint8_t *>(buffer) + actual, m_filler, length - actual);
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
protected:
|
||||
stream *const m_stream;
|
||||
std::uint8_t m_filler;
|
||||
bool const m_close;
|
||||
};
|
||||
|
||||
|
||||
class stream_read_write_wrapper : public stream_read_wrapper, public util::random_read_write
|
||||
{
|
||||
public:
|
||||
using stream_read_wrapper::stream_read_wrapper;
|
||||
|
||||
virtual std::error_condition finalize() noexcept override
|
||||
{
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
virtual std::error_condition flush() noexcept override
|
||||
{
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
|
||||
{
|
||||
std::uint64_t const pos = m_stream->tell();
|
||||
std::uint64_t size = m_stream->size();
|
||||
if (size < pos)
|
||||
{
|
||||
m_stream->seek(size, SEEK_SET);
|
||||
size += m_stream->fill(m_filler, pos - size);
|
||||
}
|
||||
actual = (size >= pos) ? m_stream->write(buffer, length) : 0U;
|
||||
return (actual == length) ? std::error_condition() : std::errc::io_error;
|
||||
}
|
||||
|
||||
virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
|
||||
{
|
||||
std::uint64_t const pos = m_stream->tell();
|
||||
std::uint64_t size = m_stream->size();
|
||||
if (offset > size)
|
||||
{
|
||||
m_stream->seek(size, SEEK_SET);
|
||||
size += m_stream->fill(m_filler, offset - size);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stream->seek(offset, SEEK_SET);
|
||||
}
|
||||
actual = (size >= offset) ? m_stream->write(buffer, length) : 0U;
|
||||
m_stream->seek(pos, SEEK_SET);
|
||||
return (actual == length) ? std::error_condition() : std::errc::io_error;
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
util::random_read::ptr stream_read(stream::ptr &&s, std::uint8_t filler) noexcept
|
||||
{
|
||||
util::random_read::ptr result;
|
||||
if (s)
|
||||
result.reset(new (std::nothrow) stream_read_wrapper(std::move(s), filler));
|
||||
return result;
|
||||
}
|
||||
|
||||
util::random_read::ptr stream_read(stream &s, std::uint8_t filler) noexcept
|
||||
{
|
||||
util::random_read::ptr result(new (std::nothrow) stream_read_wrapper(s, filler));
|
||||
return result;
|
||||
}
|
||||
|
||||
util::random_read_write::ptr stream_read_write(stream::ptr &&s, std::uint8_t filler) noexcept
|
||||
{
|
||||
util::random_read_write::ptr result;
|
||||
if (s)
|
||||
result.reset(new (std::nothrow) stream_read_write_wrapper(std::move(s), filler));
|
||||
return result;
|
||||
}
|
||||
|
||||
util::random_read_write::ptr stream_read_write(stream &s, std::uint8_t filler) noexcept
|
||||
{
|
||||
util::random_read_write::ptr result(new (std::nothrow) stream_read_write_wrapper(s, filler));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
stream::stream(bool wp)
|
||||
: imgtype(IMG_FILE)
|
||||
, write_protect(wp)
|
||||
, position(0)
|
||||
, filesize(0)
|
||||
, file()
|
||||
, buffer(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
stream::stream(bool wp, util::core_file::ptr &&f)
|
||||
: imgtype(IMG_FILE)
|
||||
, write_protect(wp)
|
||||
, position(0)
|
||||
, filesize(0)
|
||||
, file(std::move(f))
|
||||
, buffer(nullptr)
|
||||
{
|
||||
file->length(filesize); // FIXME: check error return
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
stream::stream(bool wp, std::size_t size)
|
||||
: imgtype(IMG_MEM)
|
||||
, write_protect(wp)
|
||||
, position(0)
|
||||
, filesize(size)
|
||||
, file()
|
||||
, buffer(reinterpret_cast<std::uint8_t *>(malloc(size)))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
stream::stream(bool wp, std::size_t size, void *buf)
|
||||
: imgtype(IMG_MEM)
|
||||
, write_protect(wp)
|
||||
, position(0)
|
||||
, filesize(size)
|
||||
, file()
|
||||
, buffer(reinterpret_cast<std::uint8_t *>(buf))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// dtor
|
||||
//-------------------------------------------------
|
||||
|
||||
stream::~stream()
|
||||
{
|
||||
if (buffer)
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// open_zip
|
||||
//-------------------------------------------------
|
||||
|
||||
stream::ptr stream::open_zip(const std::string &zipname, const char *subname, int read_or_write)
|
||||
{
|
||||
if (read_or_write)
|
||||
return stream::ptr();
|
||||
|
||||
/* check to see if the file exists */
|
||||
FILE *f = fopen(zipname.c_str(), "r");
|
||||
if (!f)
|
||||
return stream::ptr();
|
||||
fclose(f);
|
||||
|
||||
stream::ptr imgfile(new stream(true));
|
||||
|
||||
imgfile->imgtype = IMG_MEM;
|
||||
|
||||
util::archive_file::ptr z;
|
||||
util::archive_file::open_zip(zipname, z);
|
||||
if (!z)
|
||||
return stream::ptr();
|
||||
|
||||
int zipent = z->first_file();
|
||||
while ((zipent >= 0) && subname && strcmp(subname, z->current_name().c_str()))
|
||||
zipent = z->next_file();
|
||||
if (zipent < 0)
|
||||
return stream::ptr();
|
||||
|
||||
imgfile->filesize = z->current_uncompressed_length();
|
||||
imgfile->buffer = reinterpret_cast<std::uint8_t *>(malloc(z->current_uncompressed_length()));
|
||||
if (!imgfile->buffer)
|
||||
return stream::ptr();
|
||||
|
||||
if (z->decompress(imgfile->buffer, z->current_uncompressed_length()))
|
||||
return stream::ptr();
|
||||
|
||||
return imgfile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// open
|
||||
//-------------------------------------------------
|
||||
|
||||
stream::ptr stream::open(const std::string &filename, int read_or_write)
|
||||
{
|
||||
static const uint32_t write_modes[] =
|
||||
{
|
||||
OPEN_FLAG_READ,
|
||||
OPEN_FLAG_WRITE | OPEN_FLAG_CREATE,
|
||||
OPEN_FLAG_READ | OPEN_FLAG_WRITE,
|
||||
OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE
|
||||
};
|
||||
stream::ptr s;
|
||||
char c;
|
||||
|
||||
// maybe we are just a ZIP?
|
||||
if (core_filename_ends_with(filename, ".zip"))
|
||||
return open_zip(filename, nullptr, read_or_write);
|
||||
|
||||
util::core_file::ptr f = nullptr;
|
||||
auto const filerr = util::core_file::open(filename, write_modes[read_or_write], f);
|
||||
if (filerr)
|
||||
{
|
||||
if (!read_or_write)
|
||||
{
|
||||
int const len = filename.size();
|
||||
|
||||
// can't open the file; try opening ZIP files with other names
|
||||
std::vector<char> buf(len + 1);
|
||||
strcpy(&buf[0], filename.c_str());
|
||||
|
||||
for (int i = len-1; !s && (i >= 0); i--)
|
||||
{
|
||||
if ((buf[i] == '\\') || (buf[i] == '/'))
|
||||
{
|
||||
c = buf[i];
|
||||
buf[i] = '\0';
|
||||
s = open_zip(&buf[0], &buf[i + 1], read_or_write);
|
||||
buf[i] = c;
|
||||
}
|
||||
}
|
||||
|
||||
if (s)
|
||||
return s;
|
||||
}
|
||||
|
||||
// ah well, it was worth a shot
|
||||
return stream::ptr();
|
||||
}
|
||||
|
||||
stream::ptr imgfile(new stream(read_or_write ? false : true, std::move(f)));
|
||||
|
||||
// normal file
|
||||
return imgfile;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// open_write_stream
|
||||
//-------------------------------------------------
|
||||
|
||||
stream::ptr stream::open_write_stream(int size)
|
||||
{
|
||||
stream::ptr imgfile(new stream(false, size));
|
||||
if (!imgfile->buffer)
|
||||
return stream::ptr();
|
||||
|
||||
return imgfile;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// open_mem
|
||||
//-------------------------------------------------
|
||||
|
||||
stream::ptr stream::open_mem(void *buf, size_t sz)
|
||||
{
|
||||
stream::ptr imgfile(new stream(false, sz, buf));
|
||||
|
||||
return imgfile;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// core_file
|
||||
//-------------------------------------------------
|
||||
|
||||
util::core_file *stream::core_file()
|
||||
{
|
||||
return (imgtype == IMG_FILE) ? file.get() : nullptr;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// read
|
||||
//-------------------------------------------------
|
||||
|
||||
uint32_t stream::read(void *buf, uint32_t sz)
|
||||
{
|
||||
size_t result = 0;
|
||||
|
||||
switch(imgtype)
|
||||
{
|
||||
case IMG_FILE:
|
||||
if (!file->seek(position, SEEK_SET))
|
||||
file->read(buf, sz, result); // FIXME: check error return
|
||||
break;
|
||||
|
||||
case IMG_MEM:
|
||||
// do we have to limit sz?
|
||||
if (sz > (filesize - position))
|
||||
sz = uint32_t(filesize - position);
|
||||
|
||||
memcpy(buf, buffer + position, sz);
|
||||
result = sz;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
position += result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// write
|
||||
//-------------------------------------------------
|
||||
|
||||
uint32_t stream::write(const void *buf, uint32_t sz)
|
||||
{
|
||||
size_t result = 0;
|
||||
|
||||
switch(imgtype)
|
||||
{
|
||||
case IMG_MEM:
|
||||
if (!write_protect)
|
||||
{
|
||||
// do we have to expand the buffer?
|
||||
const uint64_t end = position + sz;
|
||||
if ((filesize < end) && (size_t(end) == end))
|
||||
{
|
||||
// try to expand the buffer
|
||||
void *const new_buffer = realloc(buffer, size_t(end));
|
||||
if (new_buffer)
|
||||
{
|
||||
buffer = reinterpret_cast<uint8_t *>(new_buffer);
|
||||
filesize = end;
|
||||
}
|
||||
}
|
||||
|
||||
// do we have to limit sz?
|
||||
if (sz > (filesize - position))
|
||||
sz = uint32_t(filesize - position);
|
||||
|
||||
memcpy(buffer + position, buf, sz);
|
||||
result = sz;
|
||||
}
|
||||
break;
|
||||
|
||||
case IMG_FILE:
|
||||
if (!file->seek(position, SEEK_SET))
|
||||
file->write(buf, sz, result); // FIXME: check error return
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
// advance the file pointer
|
||||
position += result;
|
||||
|
||||
// did we grow the file
|
||||
if (position > filesize)
|
||||
filesize = position;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// size
|
||||
//-------------------------------------------------
|
||||
|
||||
uint64_t stream::size() const
|
||||
{
|
||||
return filesize;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// getptr
|
||||
//-------------------------------------------------
|
||||
|
||||
void *stream::getptr()
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
switch(imgtype)
|
||||
{
|
||||
case IMG_MEM:
|
||||
ptr = buffer;
|
||||
break;
|
||||
|
||||
default:
|
||||
ptr = nullptr;
|
||||
break;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// seek
|
||||
//-------------------------------------------------
|
||||
|
||||
int stream::seek(int64_t pos, int where)
|
||||
{
|
||||
switch(where)
|
||||
{
|
||||
case SEEK_CUR:
|
||||
pos += position;
|
||||
break;
|
||||
case SEEK_END:
|
||||
pos += size();
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos < 0)
|
||||
position = 0;
|
||||
else
|
||||
position = std::min(size(), uint64_t(pos));
|
||||
|
||||
if (position < pos)
|
||||
fill('\0', pos - position);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// tell
|
||||
//-------------------------------------------------
|
||||
|
||||
uint64_t stream::tell()
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// transfer
|
||||
//-------------------------------------------------
|
||||
|
||||
uint64_t stream::transfer(stream &dest, stream &source, uint64_t sz)
|
||||
{
|
||||
uint64_t result = 0;
|
||||
uint64_t readsz;
|
||||
char buf[1024];
|
||||
|
||||
while(sz && (readsz = source.read(buf, std::min(sz, uint64_t(sizeof(buf))))))
|
||||
{
|
||||
dest.write(buf, readsz);
|
||||
sz -= readsz;
|
||||
result += readsz;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// transfer_all
|
||||
//-------------------------------------------------
|
||||
|
||||
uint64_t stream::transfer_all(stream &dest, stream &source)
|
||||
{
|
||||
return transfer(dest, source, source.size());
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// crc
|
||||
//-------------------------------------------------
|
||||
|
||||
int stream::crc(unsigned long *result)
|
||||
{
|
||||
size_t sz;
|
||||
void *ptr;
|
||||
|
||||
switch(imgtype)
|
||||
{
|
||||
case IMG_MEM:
|
||||
*result = crc32(0, (unsigned char *) buffer, (size_t) filesize);
|
||||
break;
|
||||
|
||||
default:
|
||||
sz = size();
|
||||
ptr = malloc(sz);
|
||||
if (!ptr)
|
||||
return IMGTOOLERR_OUTOFMEMORY;
|
||||
seek(0, SEEK_SET);
|
||||
if (read(ptr, sz) != sz)
|
||||
{
|
||||
free(ptr);
|
||||
return IMGTOOLERR_READERROR;
|
||||
}
|
||||
*result = crc32(0, (const Bytef*)ptr, sz);
|
||||
free(ptr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// file_crc
|
||||
//-------------------------------------------------
|
||||
|
||||
int stream::file_crc(const char *fname, unsigned long *result)
|
||||
{
|
||||
int err;
|
||||
stream::ptr f;
|
||||
|
||||
f = stream::open(fname, OSD_FOPEN_READ);
|
||||
if (!f)
|
||||
return IMGTOOLERR_FILENOTFOUND;
|
||||
|
||||
err = f->crc(result);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// fill
|
||||
//-------------------------------------------------
|
||||
|
||||
uint64_t stream::fill(unsigned char b, uint64_t sz)
|
||||
{
|
||||
uint64_t outsz;
|
||||
char buf[1024];
|
||||
|
||||
outsz = 0;
|
||||
memset(buf, b, (std::min<uint64_t>)(sz, sizeof(buf)));
|
||||
|
||||
while (sz)
|
||||
{
|
||||
outsz += write(buf, (std::min<uint64_t>)(sz, sizeof(buf)));
|
||||
sz -= (std::min<uint64_t>)(sz, sizeof(buf));
|
||||
}
|
||||
return outsz;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// is_read_only
|
||||
//-------------------------------------------------
|
||||
|
||||
bool stream::is_read_only()
|
||||
{
|
||||
return write_protect;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// putc
|
||||
//-------------------------------------------------
|
||||
|
||||
uint32_t stream::putc(char c)
|
||||
{
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// puts
|
||||
//-------------------------------------------------
|
||||
|
||||
uint32_t stream::puts(const char *s)
|
||||
{
|
||||
return write(s, strlen(s));
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// printf
|
||||
//-------------------------------------------------
|
||||
|
||||
uint32_t stream::printf(const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
char buf[256];
|
||||
|
||||
va_start(va, fmt);
|
||||
vsprintf(buf, fmt, va);
|
||||
va_end(va);
|
||||
|
||||
return puts(buf);
|
||||
}
|
||||
|
||||
} // namespace imgtool
|
@ -1,95 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/***************************************************************************
|
||||
|
||||
stream.h
|
||||
|
||||
Code for implementing Imgtool streams
|
||||
|
||||
***************************************************************************/
|
||||
#ifndef MAME_TOOLS_IMGTOOL_STREAM_H
|
||||
#define MAME_TOOLS_IMGTOOL_STREAM_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utilfwd.h"
|
||||
|
||||
#include "osdcomm.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace imgtool {
|
||||
|
||||
class stream
|
||||
{
|
||||
public:
|
||||
typedef std::unique_ptr<stream> ptr;
|
||||
|
||||
~stream();
|
||||
|
||||
static imgtool::stream::ptr open(const std::string &filename, int read_or_write); /* similar params to mame_fopen */
|
||||
static imgtool::stream::ptr open_write_stream(int filesize);
|
||||
static imgtool::stream::ptr open_mem(void *buf, size_t sz);
|
||||
|
||||
util::core_file *core_file();
|
||||
uint32_t read(void *buf, uint32_t sz);
|
||||
uint32_t write(const void *buf, uint32_t sz);
|
||||
uint64_t size() const;
|
||||
int seek(int64_t pos, int where);
|
||||
uint64_t tell();
|
||||
void *getptr();
|
||||
uint32_t putc(char c);
|
||||
uint32_t puts(const char *s);
|
||||
uint32_t printf(const char *fmt, ...) ATTR_PRINTF(2, 3);
|
||||
|
||||
// transfers sz bytes from source to dest
|
||||
static uint64_t transfer(imgtool::stream &dest, imgtool::stream &source, uint64_t sz);
|
||||
static uint64_t transfer_all(imgtool::stream &dest, imgtool::stream &source);
|
||||
|
||||
// fills sz bytes with b
|
||||
uint64_t fill(unsigned char b, uint64_t sz);
|
||||
|
||||
// returns the CRC of a file
|
||||
int crc(unsigned long *result);
|
||||
static int file_crc(const char *fname, unsigned long *result);
|
||||
|
||||
// returns whether a stream is read only or not
|
||||
bool is_read_only();
|
||||
|
||||
private:
|
||||
enum imgtype_t
|
||||
{
|
||||
IMG_FILE,
|
||||
IMG_MEM
|
||||
};
|
||||
|
||||
imgtype_t imgtype;
|
||||
bool write_protect;
|
||||
std::uint64_t position;
|
||||
std::uint64_t filesize;
|
||||
|
||||
std::unique_ptr<util::core_file> file;
|
||||
std::uint8_t *buffer;
|
||||
|
||||
// ctors
|
||||
stream(bool wp);
|
||||
stream(bool wp, std::unique_ptr<util::core_file> &&f);
|
||||
stream(bool wp, std::size_t size);
|
||||
stream(bool wp, std::size_t size, void *buf);
|
||||
|
||||
// private methods
|
||||
static stream::ptr open_zip(const std::string &zipname, const char *subname, int read_or_write);
|
||||
};
|
||||
|
||||
|
||||
std::unique_ptr<util::random_read> stream_read(stream::ptr &&s, std::uint8_t filler) noexcept;
|
||||
std::unique_ptr<util::random_read> stream_read(stream &s, std::uint8_t filler) noexcept;
|
||||
std::unique_ptr<util::random_read_write> stream_read_write(stream::ptr &&s, std::uint8_t filler) noexcept;
|
||||
std::unique_ptr<util::random_read_write> stream_read_write(stream &s, std::uint8_t filler) noexcept;
|
||||
|
||||
} // namespace imgtool
|
||||
|
||||
#endif // MAME_TOOLS_IMGTOOL_STREAM_H
|
File diff suppressed because it is too large
Load Diff
@ -1,575 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Aaron Giles
|
||||
/***************************************************************************
|
||||
|
||||
ldresample.c
|
||||
|
||||
Laserdisc audio synchronizer and resampler.
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "avhuff.h"
|
||||
#include "bitmap.h"
|
||||
#include "chd.h"
|
||||
#include "vbiparse.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <new>
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// CONSTANTS
|
||||
//**************************************************************************
|
||||
|
||||
// size of window where we scan ahead to find maximum; this should be large enough to
|
||||
// catch peaks of even slow waves
|
||||
const uint32_t MAXIMUM_WINDOW_SIZE = 40;
|
||||
|
||||
// number of standard deviations away from silence that we consider a real signal
|
||||
const uint32_t SIGNAL_DEVIATIONS = 100;
|
||||
|
||||
// number of standard deviations away from silence that we consider the start of a signal
|
||||
const uint32_t SIGNAL_START_DEVIATIONS = 5;
|
||||
|
||||
// number of consecutive entries of signal before we consider that we found it
|
||||
const uint32_t MINIMUM_SIGNAL_COUNT = 20;
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// TYPE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
struct movie_info
|
||||
{
|
||||
double framerate;
|
||||
int iframerate;
|
||||
int numfields;
|
||||
int width;
|
||||
int height;
|
||||
int samplerate;
|
||||
int channels;
|
||||
int interlaced;
|
||||
bitmap_yuy16 bitmap;
|
||||
std::vector<int16_t> lsound;
|
||||
std::vector<int16_t> rsound;
|
||||
uint32_t samples;
|
||||
};
|
||||
|
||||
|
||||
// ======================> chd_resample_compressor
|
||||
|
||||
class chd_resample_compressor : public chd_file_compressor
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
chd_resample_compressor(chd_file &source, movie_info &info, int64_t ioffset, int64_t islope)
|
||||
: m_source(source),
|
||||
m_info(info),
|
||||
m_ioffset(ioffset),
|
||||
m_islope(islope) { }
|
||||
|
||||
// read interface
|
||||
virtual uint32_t read_data(void *_dest, uint64_t offset, uint32_t length)
|
||||
{
|
||||
assert(offset % m_source.hunk_bytes() == 0);
|
||||
assert(length % m_source.hunk_bytes() == 0);
|
||||
|
||||
uint32_t startfield = offset / m_source.hunk_bytes();
|
||||
uint32_t endfield = startfield + length / m_source.hunk_bytes();
|
||||
uint8_t *dest = reinterpret_cast<uint8_t *>(_dest);
|
||||
|
||||
for (uint32_t fieldnum = startfield; fieldnum < endfield; fieldnum++)
|
||||
{
|
||||
generate_one_frame(dest, m_source.hunk_bytes(), fieldnum);
|
||||
dest += m_source.hunk_bytes();
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
private:
|
||||
// internal helpers
|
||||
void generate_one_frame(uint8_t *dest, uint32_t datasize, uint32_t fieldnum);
|
||||
|
||||
// internal state
|
||||
chd_file & m_source;
|
||||
movie_info & m_info;
|
||||
int64_t m_ioffset;
|
||||
int64_t m_islope;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// INLINE FUNCTIONS
|
||||
//**************************************************************************
|
||||
|
||||
//-------------------------------------------------
|
||||
// field_to_sample_number - given a field number
|
||||
// compute the absolute sample number for the
|
||||
// first sample of that field
|
||||
//-------------------------------------------------
|
||||
|
||||
inline uint32_t field_to_sample_number(const movie_info &info, uint32_t field)
|
||||
{
|
||||
return (uint64_t(info.samplerate) * uint64_t(field) * uint64_t(1000000) + info.iframerate - 1) / uint64_t(info.iframerate);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// sample_number_to_field - given a sample number
|
||||
// compute the field where it is located and
|
||||
// the offset within the field
|
||||
//-------------------------------------------------
|
||||
|
||||
inline uint32_t sample_number_to_field(const movie_info &info, uint32_t samplenum, uint32_t &offset)
|
||||
{
|
||||
uint32_t guess = (uint64_t(samplenum) * uint64_t(info.iframerate) + (uint64_t(info.samplerate) * uint64_t(1000000) - 1)) / (uint64_t(info.samplerate) * uint64_t(1000000));
|
||||
while (1)
|
||||
{
|
||||
uint32_t fieldstart = field_to_sample_number(info, guess);
|
||||
uint32_t fieldend = field_to_sample_number(info, guess + 1);
|
||||
if (samplenum >= fieldstart && samplenum < fieldend)
|
||||
{
|
||||
offset = samplenum - fieldstart;
|
||||
return guess;
|
||||
}
|
||||
else if (samplenum < fieldstart)
|
||||
guess--;
|
||||
else
|
||||
guess++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// CHD HANDLING
|
||||
//**************************************************************************
|
||||
|
||||
//-------------------------------------------------
|
||||
// open_chd - open a CHD file and return
|
||||
// information about it
|
||||
//-------------------------------------------------
|
||||
|
||||
static std::error_condition open_chd(chd_file &file, const char *filename, movie_info &info)
|
||||
{
|
||||
// open the file
|
||||
std::error_condition chderr = file.open(filename);
|
||||
if (chderr)
|
||||
{
|
||||
fprintf(stderr, "Error opening CHD file: %s\n", chderr.message().c_str());
|
||||
return chderr;
|
||||
}
|
||||
|
||||
// get the metadata
|
||||
std::string metadata;
|
||||
chderr = file.read_metadata(AV_METADATA_TAG, 0, metadata);
|
||||
if (chderr)
|
||||
{
|
||||
fprintf(stderr, "Error getting A/V metadata: %s\n", chderr.message().c_str());
|
||||
return chderr;
|
||||
}
|
||||
|
||||
// extract the info
|
||||
int fps, fpsfrac, width, height, interlaced, channels, rate;
|
||||
if (sscanf(metadata.c_str(), AV_METADATA_FORMAT, &fps, &fpsfrac, &width, &height, &interlaced, &channels, &rate) != 7)
|
||||
{
|
||||
fprintf(stderr, "Improperly formatted metadata\n");
|
||||
return chd_file::error::INVALID_METADATA;
|
||||
}
|
||||
|
||||
// extract movie info
|
||||
info.iframerate = fps * 1000000 + fpsfrac;
|
||||
info.framerate = info.iframerate / 1000000.0;
|
||||
info.numfields = file.hunk_count();
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.interlaced = interlaced;
|
||||
info.samplerate = rate;
|
||||
info.channels = channels;
|
||||
|
||||
// allocate buffers
|
||||
info.bitmap.resize(info.width, info.height);
|
||||
info.lsound.resize(info.samplerate);
|
||||
info.rsound.resize(info.samplerate);
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// create_chd - create a new CHD file
|
||||
//-------------------------------------------------
|
||||
|
||||
static std::error_condition create_chd(chd_file_compressor &file, const char *filename, chd_file &source, const movie_info &info)
|
||||
{
|
||||
// create the file
|
||||
chd_codec_type compression[4] = { CHD_CODEC_AVHUFF };
|
||||
std::error_condition chderr = file.create(filename, source.logical_bytes(), source.hunk_bytes(), source.unit_bytes(), compression);
|
||||
if (chderr)
|
||||
{
|
||||
fprintf(stderr, "Error creating new CHD file: %s\n", chderr.message().c_str());
|
||||
return chderr;
|
||||
}
|
||||
|
||||
// clone the metadata
|
||||
chderr = file.clone_all_metadata(source);
|
||||
if (chderr)
|
||||
{
|
||||
fprintf(stderr, "Error cloning metadata: %s\n", chderr.message().c_str());
|
||||
return chderr;
|
||||
}
|
||||
|
||||
// begin compressing
|
||||
file.compress_begin();
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// read_chd - read a field from a CHD file
|
||||
//-------------------------------------------------
|
||||
|
||||
static bool read_chd(chd_file &file, uint32_t field, movie_info &info, uint32_t soundoffs)
|
||||
{
|
||||
// configure the codec
|
||||
avhuff_decoder::config avconfig;
|
||||
avconfig.video = &info.bitmap;
|
||||
avconfig.maxsamples = info.lsound.size();
|
||||
avconfig.actsamples = &info.samples;
|
||||
avconfig.audio[0] = &info.lsound[soundoffs];
|
||||
avconfig.audio[1] = &info.rsound[soundoffs];
|
||||
|
||||
// configure the decompressor for this field
|
||||
file.codec_configure(CHD_CODEC_AVHUFF, AVHUFF_CODEC_DECOMPRESS_CONFIG, &avconfig);
|
||||
|
||||
// read the field
|
||||
std::error_condition chderr = file.read_hunk(field, nullptr);
|
||||
return !chderr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// CORE IMPLEMENTATION
|
||||
//**************************************************************************
|
||||
|
||||
//-------------------------------------------------
|
||||
// find_edge_near_field - given a field number,
|
||||
// load +/- 1/2 second on either side and find
|
||||
// an audio edge
|
||||
//-------------------------------------------------
|
||||
|
||||
static bool find_edge_near_field(chd_file &srcfile, uint32_t fieldnum, movie_info &info, bool report_best_field, int32_t &delta)
|
||||
{
|
||||
// clear the sound buffers
|
||||
memset(&info.lsound[0], 0, info.lsound.size() * 2);
|
||||
memset(&info.rsound[0], 0, info.rsound.size() * 2);
|
||||
|
||||
// read 1 second around the target area
|
||||
int fields_to_read = info.iframerate / 1000000;
|
||||
int32_t firstfield = fieldnum - (fields_to_read / 2);
|
||||
uint32_t targetsoundstart = 0;
|
||||
uint32_t firstfieldend = 0;
|
||||
uint32_t fieldstart[100];
|
||||
uint32_t soundend = 0;
|
||||
for (int32_t curfield = 0; curfield < fields_to_read; curfield++)
|
||||
{
|
||||
// remember the start of each field
|
||||
fieldstart[curfield] = soundend;
|
||||
|
||||
// remember the sound offset where the initial fieldnum is
|
||||
if (firstfield + curfield == fieldnum)
|
||||
targetsoundstart = soundend;
|
||||
|
||||
// read the frame and samples
|
||||
if (firstfield + curfield >= 0)
|
||||
{
|
||||
read_chd(srcfile, firstfield + curfield, info, soundend);
|
||||
soundend += info.samples;
|
||||
|
||||
// also remember the offset at the end of the first field
|
||||
if (firstfieldend == 0)
|
||||
firstfieldend = soundend;
|
||||
}
|
||||
}
|
||||
|
||||
// compute absolute deltas across the samples
|
||||
for (uint32_t sampnum = 0; sampnum < soundend; sampnum++)
|
||||
{
|
||||
info.lsound[sampnum] = labs(info.lsound[sampnum + 1] - info.lsound[sampnum]);
|
||||
info.rsound[sampnum] = labs(info.rsound[sampnum + 1] - info.rsound[sampnum]);
|
||||
}
|
||||
|
||||
// for each sample in the collection, find the highest deltas over the
|
||||
// next few samples, and take the nth highest value (to remove outliers)
|
||||
for (uint32_t sampnum = 0; sampnum < soundend - MAXIMUM_WINDOW_SIZE; sampnum++)
|
||||
{
|
||||
// scan forward over the maximum window
|
||||
uint32_t lmax = 0, rmax = 0;
|
||||
for (uint32_t scannum = 0; scannum < MAXIMUM_WINDOW_SIZE; scannum++)
|
||||
{
|
||||
if (info.lsound[sampnum + scannum] > lmax)
|
||||
lmax = info.lsound[sampnum + scannum];
|
||||
if (info.rsound[sampnum + scannum] > rmax)
|
||||
rmax = info.rsound[sampnum + scannum];
|
||||
}
|
||||
|
||||
// replace this sample with the maximum value found
|
||||
info.lsound[sampnum] = lmax;
|
||||
info.rsound[sampnum] = rmax;
|
||||
}
|
||||
|
||||
// now compute the average over the first field, which is assumed to be silence
|
||||
uint32_t firstlavg = 0;
|
||||
uint32_t firstravg = 0;
|
||||
for (uint32_t sampnum = 0; sampnum < firstfieldend; sampnum++)
|
||||
{
|
||||
firstlavg += info.lsound[sampnum];
|
||||
firstravg += info.rsound[sampnum];
|
||||
}
|
||||
firstlavg /= firstfieldend;
|
||||
firstravg /= firstfieldend;
|
||||
|
||||
// then compute the standard deviation over the first field
|
||||
uint32_t firstldev = 0;
|
||||
uint32_t firstrdev = 0;
|
||||
for (uint32_t sampnum = 0; sampnum < firstfieldend; sampnum++)
|
||||
{
|
||||
firstldev += (info.lsound[sampnum] - firstlavg) * (info.lsound[sampnum] - firstlavg);
|
||||
firstrdev += (info.rsound[sampnum] - firstravg) * (info.rsound[sampnum] - firstravg);
|
||||
}
|
||||
firstldev = sqrt(double(firstldev) / firstfieldend);
|
||||
firstrdev = sqrt(double(firstrdev) / firstfieldend);
|
||||
|
||||
// scan forward through the samples, counting consecutive samples more than
|
||||
// SIGNAL_DEVIATIONS standard deviations away from silence
|
||||
uint32_t lcount = 0;
|
||||
uint32_t rcount = 0;
|
||||
uint32_t sampnum = 0;
|
||||
for (sampnum = 0; sampnum < soundend; sampnum++)
|
||||
{
|
||||
// left speaker
|
||||
if (info.lsound[sampnum] > firstlavg + SIGNAL_DEVIATIONS * firstldev)
|
||||
lcount++;
|
||||
else
|
||||
lcount = 0;
|
||||
|
||||
// right speaker
|
||||
if (info.rsound[sampnum] > firstravg + SIGNAL_DEVIATIONS * firstrdev)
|
||||
rcount++;
|
||||
else
|
||||
rcount = 0;
|
||||
|
||||
// stop if we find enough
|
||||
if (lcount > MINIMUM_SIGNAL_COUNT || rcount > MINIMUM_SIGNAL_COUNT)
|
||||
break;
|
||||
}
|
||||
|
||||
// if we didn't find any, return failure
|
||||
if (sampnum >= soundend)
|
||||
{
|
||||
if (!report_best_field)
|
||||
printf("Field %5d: Unable to find edge\n", fieldnum);
|
||||
return false;
|
||||
}
|
||||
|
||||
// scan backwards to find the start of the signal
|
||||
for ( ; sampnum > 0; sampnum--)
|
||||
if (info.lsound[sampnum - 1] < firstlavg + SIGNAL_START_DEVIATIONS * firstldev ||
|
||||
info.rsound[sampnum - 1] < firstravg + SIGNAL_START_DEVIATIONS * firstrdev)
|
||||
break;
|
||||
|
||||
// if we're to report the best field, figure out which field we are in
|
||||
if (report_best_field)
|
||||
{
|
||||
int32_t curfield;
|
||||
for (curfield = 0; curfield < fields_to_read - 1; curfield++)
|
||||
if (sampnum < fieldstart[curfield + 1])
|
||||
break;
|
||||
printf("Field %5d: Edge found at offset %d (frame %.1f)\n", firstfield + curfield, sampnum - fieldstart[curfield], (double)(firstfield + curfield) * 0.5);
|
||||
}
|
||||
|
||||
// otherwise, compute the delta from the provided field number
|
||||
else
|
||||
{
|
||||
printf("Field %5d: Edge at offset %d from expected (found at %d, expected %d)\n", fieldnum, sampnum - targetsoundstart, sampnum, targetsoundstart);
|
||||
delta = sampnum - targetsoundstart;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// generate_one_frame - generate a single
|
||||
// resampled frame
|
||||
//-------------------------------------------------
|
||||
|
||||
void chd_resample_compressor::generate_one_frame(uint8_t *dest, uint32_t datasize, uint32_t fieldnum)
|
||||
{
|
||||
// determine the first field needed to cover this range of samples
|
||||
uint32_t srcbegin = field_to_sample_number(m_info, fieldnum);
|
||||
int64_t dstbegin = (int64_t(srcbegin) << 24) + m_ioffset + m_islope * fieldnum;
|
||||
uint32_t dstbeginoffset;
|
||||
int32_t dstbeginfield;
|
||||
if (dstbegin >= 0)
|
||||
dstbeginfield = sample_number_to_field(m_info, dstbegin >> 24, dstbeginoffset);
|
||||
else
|
||||
{
|
||||
dstbeginfield = -1 - sample_number_to_field(m_info, -dstbegin >> 24, dstbeginoffset);
|
||||
dstbeginoffset = (field_to_sample_number(m_info, -dstbeginfield) - field_to_sample_number(m_info, -dstbeginfield - 1)) - dstbeginoffset;
|
||||
}
|
||||
|
||||
// determine the last field needed to cover this range of samples
|
||||
uint32_t srcend = field_to_sample_number(m_info, fieldnum + 1);
|
||||
int64_t dstend = (int64_t(srcend) << 24) + m_ioffset + m_islope * (fieldnum + 1);
|
||||
uint32_t dstendoffset;
|
||||
int32_t dstendfield;
|
||||
if (dstend >= 0)
|
||||
dstendfield = sample_number_to_field(m_info, dstend >> 24, dstendoffset);
|
||||
else
|
||||
{
|
||||
dstendfield = -1 - -sample_number_to_field(m_info, -dstend >> 24, dstendoffset);
|
||||
dstendoffset = (field_to_sample_number(m_info, -dstendfield) - field_to_sample_number(m_info, -dstendfield - 1)) - dstendoffset;
|
||||
}
|
||||
/*
|
||||
printf("%5d: start=%10d (%5d.%03d) end=%10d (%5d.%03d)\n",
|
||||
fieldnum,
|
||||
(int32_t)(dstbegin >> 24), dstbeginfield, dstbeginoffset,
|
||||
(int32_t)(dstend >> 24), dstendfield, dstendoffset);
|
||||
*/
|
||||
// read all samples required into the end of the sound buffers
|
||||
uint32_t dstoffset = srcend - srcbegin;
|
||||
for (int32_t dstfield = dstbeginfield; dstfield <= dstendfield; dstfield++)
|
||||
{
|
||||
if (dstfield >= 0)
|
||||
read_chd(m_source, dstfield, m_info, dstoffset);
|
||||
else
|
||||
{
|
||||
m_info.samples = field_to_sample_number(m_info, -dstfield) - field_to_sample_number(m_info, -dstfield - 1);
|
||||
memset(&m_info.lsound[dstoffset], 0, m_info.samples * sizeof(m_info.lsound[0]));
|
||||
memset(&m_info.rsound[dstoffset], 0, m_info.samples * sizeof(m_info.rsound[0]));
|
||||
}
|
||||
dstoffset += m_info.samples;
|
||||
}
|
||||
|
||||
// resample the destination samples to the source
|
||||
dstoffset = srcend - srcbegin;
|
||||
int64_t dstpos = dstbegin;
|
||||
int64_t dststep = (dstend - dstbegin) / int64_t(srcend - srcbegin);
|
||||
for (uint32_t srcoffset = 0; srcoffset < srcend - srcbegin; srcoffset++)
|
||||
{
|
||||
m_info.lsound[srcoffset] = m_info.lsound[(int)(dstoffset + dstbeginoffset + (dstpos >> 24) - (dstbegin >> 24))];
|
||||
m_info.rsound[srcoffset] = m_info.rsound[(int)(dstoffset + dstbeginoffset + (dstpos >> 24) - (dstbegin >> 24))];
|
||||
dstpos += dststep;
|
||||
}
|
||||
|
||||
// read the original frame, pointing the sound buffer past where we've calculated
|
||||
read_chd(m_source, fieldnum, m_info, srcend - srcbegin);
|
||||
|
||||
// assemble the final frame
|
||||
std::vector<uint8_t> buffer;
|
||||
int16_t *sampledata[2] = { &m_info.lsound[0], &m_info.rsound[0] };
|
||||
avhuff_encoder::assemble_data(buffer, m_info.bitmap, m_info.channels, m_info.samples, sampledata);
|
||||
memcpy(dest, &buffer[0], std::min(buffer.size(), size_t(datasize)));
|
||||
if (buffer.size() < datasize)
|
||||
memset(&dest[buffer.size()], 0, datasize - buffer.size());
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// usage - display program usage
|
||||
//-------------------------------------------------
|
||||
|
||||
static int usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: \n");
|
||||
fprintf(stderr, " ldresample source.chd\n");
|
||||
fprintf(stderr, " ldresample source.chd output.chd offset [slope]\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Where offset and slope make a linear equation f(x) which\n");
|
||||
fprintf(stderr, "describes the sample offset from the source as a function\n");
|
||||
fprintf(stderr, "of field number.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// main - main entry point
|
||||
//-------------------------------------------------
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// verify arguments
|
||||
if (argc < 2)
|
||||
return usage();
|
||||
const char *srcfilename = argv[1];
|
||||
const char *dstfilename = (argc < 3) ? nullptr : argv[2];
|
||||
double offset = (argc < 4) ? 0.0 : atof(argv[3]);
|
||||
double slope = (argc < 5) ? 1.0 : atof(argv[4]);
|
||||
|
||||
// print basic information
|
||||
printf("Input file: %s\n", srcfilename);
|
||||
if (dstfilename != nullptr)
|
||||
{
|
||||
printf("Output file: %s\n", dstfilename);
|
||||
printf("Offset: %f\n", offset);
|
||||
printf("Slope: %f\n", slope);
|
||||
}
|
||||
|
||||
// open the source file
|
||||
chd_file srcfile;
|
||||
movie_info info;
|
||||
std::error_condition err = open_chd(srcfile, srcfilename, info);
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr, "Unable to open file '%s'\n", srcfilename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// output some basics
|
||||
printf("Video dimensions: %dx%d\n", info.width, info.height);
|
||||
printf("Video frame rate: %.2fHz\n", info.framerate);
|
||||
printf("Sample rate: %dHz\n", info.samplerate);
|
||||
printf("Total fields: %d\n", info.numfields);
|
||||
|
||||
// if we don't have a destination file, scan for edges
|
||||
if (dstfilename == nullptr)
|
||||
{
|
||||
for (uint32_t fieldnum = 60; fieldnum < info.numfields - 60; fieldnum += 30)
|
||||
{
|
||||
fprintf(stderr, "Field %5d\r", fieldnum);
|
||||
int32_t delta;
|
||||
find_edge_near_field(srcfile, fieldnum, info, true, delta);
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, resample the source to the destination
|
||||
else
|
||||
{
|
||||
// open the destination file
|
||||
chd_resample_compressor dstfile(srcfile, info, int64_t(offset * 65536.0 * 256.0), int64_t(slope * 65536.0 * 256.0));
|
||||
err = create_chd(dstfile, dstfilename, srcfile, info);
|
||||
if (!dstfile.opened())
|
||||
{
|
||||
fprintf(stderr, "Unable to create file '%s'\n", dstfilename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// loop over all the fields in the source file
|
||||
double progress, ratio;
|
||||
osd_ticks_t last_update = 0;
|
||||
while (dstfile.compress_continue(progress, ratio) == chd_file::error::COMPRESSING)
|
||||
if (osd_ticks() - last_update > osd_ticks_per_second() / 4)
|
||||
{
|
||||
last_update = osd_ticks();
|
||||
printf("Processing, %.1f%% complete....\r", progress * 100.0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1,795 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Aaron Giles
|
||||
/***************************************************************************
|
||||
|
||||
ldverify.c
|
||||
|
||||
Laserdisc AVI/CHD verifier.
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "avhuff.h"
|
||||
#include "aviio.h"
|
||||
#include "bitmap.h"
|
||||
#include "chd.h"
|
||||
#include "coretmpl.h"
|
||||
#include "vbiparse.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <new>
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// CONSTANTS
|
||||
//**************************************************************************
|
||||
|
||||
const int REPORT_BLANKS_THRESHOLD = 50;
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// TYPE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
struct movie_info
|
||||
{
|
||||
double framerate;
|
||||
int numframes;
|
||||
int width;
|
||||
int height;
|
||||
int samplerate;
|
||||
int channels;
|
||||
};
|
||||
|
||||
struct video_info
|
||||
{
|
||||
int first_whitefield;
|
||||
bool saw_leadin;
|
||||
bool saw_leadout;
|
||||
int last_frame;
|
||||
int last_chapter;
|
||||
int cadence;
|
||||
uint32_t cadence_history;
|
||||
int prev_whitefield;
|
||||
int min_overall;
|
||||
int max_overall;
|
||||
int first_blank_frame;
|
||||
int first_blank_field;
|
||||
int num_blank_fields;
|
||||
int first_low_frame;
|
||||
int first_low_field;
|
||||
int num_low_fields;
|
||||
int first_high_frame;
|
||||
int first_high_field;
|
||||
int num_high_fields;
|
||||
};
|
||||
|
||||
struct audio_info
|
||||
{
|
||||
int min_lsample;
|
||||
int min_rsample;
|
||||
int max_lsample;
|
||||
int max_rsample;
|
||||
int min_lsample_count;
|
||||
int min_rsample_count;
|
||||
int max_lsample_count;
|
||||
int max_rsample_count;
|
||||
int sample_count;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// GLOBAL VARIABLES
|
||||
//**************************************************************************
|
||||
|
||||
static bool chdinterlaced;
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// AVI HANDLING
|
||||
//**************************************************************************
|
||||
|
||||
//-------------------------------------------------
|
||||
// open_avi - open an AVI file and return
|
||||
// information about it
|
||||
//-------------------------------------------------
|
||||
|
||||
static void *open_avi(const char *filename, movie_info &info)
|
||||
{
|
||||
// open the file
|
||||
avi_file::ptr avi;
|
||||
avi_file::error avierr = avi_file::open(filename, avi);
|
||||
if (avierr != avi_file::error::NONE)
|
||||
{
|
||||
fprintf(stderr, "Error opening AVI file: %s\n", avi_file::error_string(avierr));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// extract movie info
|
||||
const avi_file::movie_info &aviinfo = avi->get_movie_info();
|
||||
info.framerate = (double)aviinfo.video_timescale / (double)aviinfo.video_sampletime;
|
||||
info.numframes = aviinfo.video_numsamples;
|
||||
info.width = aviinfo.video_width;
|
||||
info.height = aviinfo.video_height;
|
||||
info.samplerate = aviinfo.audio_samplerate;
|
||||
info.channels = aviinfo.audio_channels;
|
||||
return avi.release();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// read_avi - read a frame from an AVI file
|
||||
//-------------------------------------------------
|
||||
|
||||
static bool read_avi(void *file, int frame, bitmap_yuy16 &bitmap, int16_t *lsound, int16_t *rsound, int &samples)
|
||||
{
|
||||
avi_file *avifile = reinterpret_cast<avi_file *>(file);
|
||||
|
||||
// read the frame
|
||||
avi_file::error avierr = avifile->read_video_frame(frame, bitmap);
|
||||
if (avierr != avi_file::error::NONE)
|
||||
return false;
|
||||
|
||||
// read the samples
|
||||
const avi_file::movie_info &aviinfo = avifile->get_movie_info();
|
||||
uint32_t firstsample = (uint64_t(aviinfo.audio_samplerate) * uint64_t(frame) * uint64_t(aviinfo.video_sampletime) + aviinfo.video_timescale - 1) / uint64_t(aviinfo.video_timescale);
|
||||
uint32_t lastsample = (uint64_t(aviinfo.audio_samplerate) * uint64_t(frame + 1) * uint64_t(aviinfo.video_sampletime) + aviinfo.video_timescale - 1) / uint64_t(aviinfo.video_timescale);
|
||||
avierr = avifile->read_sound_samples(0, firstsample, lastsample - firstsample, lsound);
|
||||
avierr = avifile->read_sound_samples(1, firstsample, lastsample - firstsample, rsound);
|
||||
if (avierr != avi_file::error::NONE)
|
||||
return false;
|
||||
samples = lastsample - firstsample;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// close_avi - close an AVI file
|
||||
//-------------------------------------------------
|
||||
|
||||
static void close_avi(void *file)
|
||||
{
|
||||
avi_file *avifile = reinterpret_cast<avi_file *>(file);
|
||||
delete avifile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// CHD HANDLING
|
||||
//**************************************************************************
|
||||
|
||||
//-------------------------------------------------
|
||||
// open_chd - open a CHD file and return
|
||||
// information about it
|
||||
//-------------------------------------------------
|
||||
|
||||
static void *open_chd(const char *filename, movie_info &info)
|
||||
{
|
||||
auto chd = new chd_file;
|
||||
|
||||
// open the file
|
||||
std::error_condition chderr = chd->open(filename);
|
||||
if (chderr)
|
||||
{
|
||||
fprintf(stderr, "Error opening CHD file: %s\n", chderr.message().c_str());
|
||||
delete chd;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// get the metadata
|
||||
std::string metadata;
|
||||
chderr = chd->read_metadata(AV_METADATA_TAG, 0, metadata);
|
||||
if (chderr)
|
||||
{
|
||||
fprintf(stderr, "Error getting A/V metadata: %s\n", chderr.message().c_str());
|
||||
delete chd;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// extract the info
|
||||
int fps, fpsfrac, width, height, interlaced, channels, rate;
|
||||
if (sscanf(metadata.c_str(), AV_METADATA_FORMAT, &fps, &fpsfrac, &width, &height, &interlaced, &channels, &rate) != 7)
|
||||
{
|
||||
fprintf(stderr, "Improperly formatted metadata\n");
|
||||
delete chd;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// extract movie info
|
||||
info.framerate = (fps * 1000000 + fpsfrac) / 1000000.0;
|
||||
info.numframes = chd->hunk_count();
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.samplerate = rate;
|
||||
info.channels = channels;
|
||||
|
||||
// convert to an interlaced frame
|
||||
chdinterlaced = interlaced;
|
||||
if (interlaced)
|
||||
{
|
||||
info.framerate /= 2;
|
||||
info.numframes = (info.numframes + 1) / 2;
|
||||
info.height *= 2;
|
||||
}
|
||||
return chd;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// read_chd - read a frame from a CHD file
|
||||
//-------------------------------------------------
|
||||
|
||||
static int read_chd(void *file, int frame, bitmap_yuy16 &bitmap, int16_t *lsound, int16_t *rsound, int &samples)
|
||||
{
|
||||
chd_file *chdfile = reinterpret_cast<chd_file *>(file);
|
||||
|
||||
// loop over fields
|
||||
int interlace_factor = chdinterlaced ? 2 : 1;
|
||||
samples = 0;
|
||||
for (int fieldnum = 0; fieldnum < interlace_factor; fieldnum++)
|
||||
{
|
||||
// make a fake bitmap for this field
|
||||
bitmap_yuy16 video;
|
||||
video.wrap(&bitmap.pix(fieldnum), bitmap.width(), bitmap.height() / interlace_factor, bitmap.rowpixels() * interlace_factor);
|
||||
avhuff_decoder::config avconfig;
|
||||
avconfig.video = &video;
|
||||
|
||||
// configure the codec
|
||||
uint32_t numsamples;
|
||||
avconfig.maxsamples = 48000;
|
||||
avconfig.actsamples = &numsamples;
|
||||
avconfig.audio[0] = &lsound[samples];
|
||||
avconfig.audio[1] = &rsound[samples];
|
||||
|
||||
// configure the decompressor for this frame
|
||||
chdfile->codec_configure(CHD_CODEC_AVHUFF, AVHUFF_CODEC_DECOMPRESS_CONFIG, &avconfig);
|
||||
|
||||
// read the frame
|
||||
std::error_condition chderr = chdfile->read_hunk(frame * interlace_factor + fieldnum, nullptr);
|
||||
if (chderr)
|
||||
return false;
|
||||
|
||||
// account for samples read
|
||||
samples += numsamples;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// close_chd - close a CHD file
|
||||
//-------------------------------------------------
|
||||
|
||||
static void close_chd(void *file)
|
||||
{
|
||||
chd_file *chdfile = reinterpret_cast<chd_file *>(file);
|
||||
delete chdfile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// CORE IMPLEMENTATION
|
||||
//**************************************************************************
|
||||
|
||||
//-------------------------------------------------
|
||||
// init_video - init video info structure
|
||||
//-------------------------------------------------
|
||||
|
||||
static void init_video(video_info &video)
|
||||
{
|
||||
video.first_whitefield = -1;
|
||||
video.saw_leadin = false;
|
||||
video.saw_leadout = false;
|
||||
video.last_frame = -1;
|
||||
video.last_chapter = -1;
|
||||
video.cadence = -1;
|
||||
video.cadence_history = 0;
|
||||
video.prev_whitefield = -1;
|
||||
video.min_overall = 255;
|
||||
video.max_overall = 0;
|
||||
video.first_blank_frame = -1;
|
||||
video.first_blank_field = -1;
|
||||
video.num_blank_fields = -1;
|
||||
video.first_low_frame = -1;
|
||||
video.first_low_field = -1;
|
||||
video.num_low_fields = -1;
|
||||
video.first_high_frame = -1;
|
||||
video.first_high_field = -1;
|
||||
video.num_high_fields = -1;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// verify_video - verify video frame
|
||||
//-------------------------------------------------
|
||||
|
||||
static void verify_video(video_info &video, int frame, bitmap_yuy16 &bitmap)
|
||||
{
|
||||
// loop over fields
|
||||
const int fields_per_frame = 2;
|
||||
for (int fieldnum = 0; fieldnum < fields_per_frame; fieldnum++)
|
||||
{
|
||||
// output status
|
||||
if (frame % 10 == 0 && fieldnum == 0)
|
||||
fprintf(stderr, "%6d.%d...\r", frame, fieldnum);
|
||||
|
||||
// parse the VBI data
|
||||
vbi_metadata metadata;
|
||||
vbi_parse_all(&bitmap.pix(fieldnum), bitmap.rowpixels() * 2, bitmap.width(), 8, &metadata);
|
||||
|
||||
// if we have data in both 17 and 18, it should match
|
||||
if (metadata.line17 != 0 && metadata.line18 != 0 && metadata.line17 != metadata.line18)
|
||||
{
|
||||
printf("%6d.%d: line 17 and 18 data does not match (17=%06X 18=%06X) (WARNING)\n", frame, fieldnum, metadata.line17, metadata.line18);
|
||||
printf("%6d.%d: selected %06X based on bit confidence\n", frame, fieldnum, metadata.line1718);
|
||||
}
|
||||
|
||||
// is this a lead-in code?
|
||||
if (metadata.line1718 == VBI_CODE_LEADIN)
|
||||
{
|
||||
// if we haven't seen lead-in yet, detect it
|
||||
if (!video.saw_leadin)
|
||||
{
|
||||
video.saw_leadin = true;
|
||||
printf("%6d.%d: lead-in code detected\n", frame, fieldnum);
|
||||
}
|
||||
|
||||
// if we've previously seen chapters/frames, that's weird
|
||||
if (video.last_frame != -1 || video.last_chapter != -1)
|
||||
printf("%6d.%d: lead-in code detected after frame/chapter data (WARNING)\n", frame, fieldnum);
|
||||
}
|
||||
|
||||
// is this a lead-out code?
|
||||
if (metadata.line1718 == VBI_CODE_LEADOUT)
|
||||
{
|
||||
// if we haven't seen lead-in yet, detect it
|
||||
if (!video.saw_leadout)
|
||||
{
|
||||
video.saw_leadout = true;
|
||||
printf("%6d.%d: lead-out code detected\n", frame, fieldnum);
|
||||
if (video.last_frame != -1)
|
||||
printf("%6d.%d: final frame number was %d\n", frame, fieldnum, video.last_frame);
|
||||
else
|
||||
printf("%6d.%d: never detected any frame numbers (ERROR)\n", frame, fieldnum);
|
||||
}
|
||||
|
||||
// if we've previously seen chapters/frames, that's weird
|
||||
if (video.last_frame == -1)
|
||||
printf("%6d.%d: lead-out code detected with no frames detected beforehand (WARNING)\n", frame, fieldnum);
|
||||
}
|
||||
|
||||
// is this a frame code?
|
||||
if ((metadata.line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE)
|
||||
{
|
||||
int framenum = VBI_CAV_PICTURE(metadata.line1718);
|
||||
|
||||
// did we see any leadin?
|
||||
if (!video.saw_leadin)
|
||||
{
|
||||
printf("%6d.%d: detected frame number but never saw any lead-in (WARNING)\n", frame, fieldnum);
|
||||
video.saw_leadin = true;
|
||||
}
|
||||
|
||||
// if this is the first frame, make sure it's 1
|
||||
if (video.last_frame == -1)
|
||||
{
|
||||
if (framenum == 0)
|
||||
printf("%6d.%d: detected frame 0\n", frame, fieldnum);
|
||||
else if (framenum == 1)
|
||||
printf("%6d.%d: detected frame 1\n", frame, fieldnum);
|
||||
else
|
||||
printf("%6d.%d: first frame number is not 0 or 1 (%d) (ERROR)\n", frame, fieldnum, framenum);
|
||||
}
|
||||
|
||||
// print an update every 10000 frames
|
||||
if (framenum != 0 && framenum % 10000 == 0)
|
||||
printf("%6d.%d: detected frame %d\n", frame, fieldnum, framenum);
|
||||
|
||||
// if this frame is not consecutive, it's an error
|
||||
if (video.last_frame != -1 && framenum != video.last_frame + 1)
|
||||
printf("%6d.%d: gap in frame number sequence (%d->%d) (ERROR)\n", frame, fieldnum, video.last_frame, framenum);
|
||||
|
||||
// remember the frame number
|
||||
video.last_frame = framenum;
|
||||
|
||||
// if we've seen a white flag before, but it's not here, warn
|
||||
if (video.first_whitefield != -1 && !metadata.white)
|
||||
printf("%6d.%d: detected frame number but no white flag (WARNING)\n", frame, fieldnum);
|
||||
}
|
||||
|
||||
// is the whiteflag set?
|
||||
int field = frame * fields_per_frame + fieldnum;
|
||||
if (metadata.white)
|
||||
{
|
||||
// if this is the first white flag we see, count it
|
||||
if (video.first_whitefield == -1)
|
||||
{
|
||||
video.first_whitefield = field;
|
||||
printf("%6d.%d: first white flag seen\n", frame, fieldnum);
|
||||
}
|
||||
|
||||
// if we've seen frame numbers before, but not here, warn
|
||||
if (video.last_frame != -1 && (metadata.line1718 & VBI_MASK_CAV_PICTURE) != VBI_CODE_CAV_PICTURE)
|
||||
printf("%6d.%d: detected white flag but no frame number (WARNING)\n", frame, fieldnum);
|
||||
}
|
||||
|
||||
// if this is the start of a frame, handle cadence
|
||||
if (metadata.white || (metadata.line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE)
|
||||
{
|
||||
// if we've seen frames, but we're not yet to the lead-out, check the cadence
|
||||
if (video.last_frame != -1 && !video.saw_leadout)
|
||||
{
|
||||
// make sure we have a proper history
|
||||
if (video.prev_whitefield != -1)
|
||||
video.cadence_history = (video.cadence_history << 4) | ((field - video.prev_whitefield) & 0x0f);
|
||||
video.prev_whitefield = field;
|
||||
|
||||
// if we don't know our cadence yet, determine it
|
||||
if (video.cadence == -1 && (video.cadence_history & 0xf00) != 0)
|
||||
{
|
||||
if ((video.cadence_history & 0xfff) == 0x222)
|
||||
{
|
||||
printf("%6d.%d: detected 2:2 cadence\n", frame, fieldnum);
|
||||
video.cadence = 4;
|
||||
}
|
||||
else if ((video.cadence_history & 0xfff) == 0x323)
|
||||
{
|
||||
printf("%6d.%d: detected 3:2 cadence\n", frame, fieldnum);
|
||||
video.cadence = 5;
|
||||
}
|
||||
else if ((video.cadence_history & 0xfff) == 0x232)
|
||||
{
|
||||
printf("%6d.%d: detected 2:3 cadence\n", frame, fieldnum);
|
||||
video.cadence = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%6d.%d: unknown cadence (history %d:%d:%d) (WARNING)\n", frame, fieldnum,
|
||||
(video.cadence_history >> 8) & 15, (video.cadence_history >> 4) & 15, video.cadence_history & 15);
|
||||
}
|
||||
}
|
||||
|
||||
// if we know our cadence, make sure we stick to it
|
||||
if (video.cadence != -1)
|
||||
{
|
||||
if (video.cadence == 4 && (video.cadence_history & 0xfff) != 0x222)
|
||||
{
|
||||
printf("%6d.%d: missed cadence (history %d:%d:%d) (WARNING)\n", frame, fieldnum,
|
||||
(video.cadence_history >> 8) & 15, (video.cadence_history >> 4) & 15, video.cadence_history & 15);
|
||||
video.cadence = -1;
|
||||
video.cadence_history = 0;
|
||||
}
|
||||
else if (video.cadence == 5 && (video.cadence_history & 0xfff) != 0x323 && (video.cadence_history & 0xfff) != 0x232)
|
||||
{
|
||||
printf("%6d.%d: missed cadence (history %d:%d:%d) (WARNING)\n", frame, fieldnum,
|
||||
(video.cadence_history >> 8) & 15, (video.cadence_history >> 4) & 15, video.cadence_history & 15);
|
||||
video.cadence = -1;
|
||||
video.cadence_history = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now examine the active video signal
|
||||
uint32_t yhisto[256] = { 0 };
|
||||
uint32_t crhisto[256] = { 0 };
|
||||
uint32_t cbhisto[256] = { 0 };
|
||||
int pixels = 0;
|
||||
for (int y = 22*2 + fieldnum; y < bitmap.height(); y += 2)
|
||||
{
|
||||
for (int x = 16; x < 720 - 16; x++)
|
||||
{
|
||||
yhisto[bitmap.pix(y, x) >> 8]++;
|
||||
if (x % 2 == 0)
|
||||
cbhisto[bitmap.pix(y, x) & 0xff]++;
|
||||
else
|
||||
crhisto[bitmap.pix(y, x) & 0xff]++;
|
||||
}
|
||||
pixels += 720 - 16 - 16;
|
||||
}
|
||||
|
||||
// remove the top/bottom 0.1% of Y
|
||||
int remaining = pixels / 1000;
|
||||
int yminval;
|
||||
for (yminval = 0; (remaining -= yhisto[yminval]) >= 0; yminval++) ;
|
||||
remaining = pixels / 1000;
|
||||
int ymaxval;
|
||||
for (ymaxval = 255; (remaining -= yhisto[ymaxval]) >= 0; ymaxval--) ;
|
||||
|
||||
// remove the top/bottom 0.1% of Cb
|
||||
remaining = pixels / 500;
|
||||
int cbminval;
|
||||
for (cbminval = 0; (remaining -= cbhisto[cbminval]) >= 0; cbminval++) ;
|
||||
remaining = pixels / 500;
|
||||
int cbmaxval;
|
||||
for (cbmaxval = 255; (remaining -= cbhisto[cbmaxval]) >= 0; cbmaxval--) ;
|
||||
|
||||
// remove the top/bottom 0.1% of Cr
|
||||
remaining = pixels / 500;
|
||||
int crminval;
|
||||
for (crminval = 0; (remaining -= crhisto[crminval]) >= 0; crminval++) ;
|
||||
remaining = pixels / 500;
|
||||
int crmaxval;
|
||||
for (crmaxval = 255; (remaining -= crhisto[crmaxval]) >= 0; crmaxval--) ;
|
||||
|
||||
// track blank frames
|
||||
if (ymaxval - yminval < 10 && cbmaxval - cbminval < 10 && crmaxval - cbmaxval < 10)
|
||||
{
|
||||
if (video.first_blank_frame == -1)
|
||||
{
|
||||
video.first_blank_frame = frame;
|
||||
video.first_blank_field = fieldnum;
|
||||
video.num_blank_fields = 0;
|
||||
}
|
||||
video.num_blank_fields++;
|
||||
}
|
||||
else if (video.num_blank_fields > 0)
|
||||
{
|
||||
if (video.num_blank_fields >= REPORT_BLANKS_THRESHOLD)
|
||||
printf("%6d.%d-%6d.%d: blank frames for %d fields (INFO)\n", video.first_blank_frame, video.first_blank_field, frame, fieldnum, video.num_blank_fields);
|
||||
video.first_blank_frame = video.first_blank_field = video.num_blank_fields = -1;
|
||||
}
|
||||
|
||||
// update the overall min/max
|
||||
video.min_overall = std::min(yminval, video.min_overall);
|
||||
video.max_overall = std::max(ymaxval, video.max_overall);
|
||||
|
||||
// track low fields
|
||||
if (yminval <= 0)
|
||||
{
|
||||
if (video.first_low_frame == -1)
|
||||
{
|
||||
video.first_low_frame = frame;
|
||||
video.first_low_field = fieldnum;
|
||||
video.num_low_fields = 0;
|
||||
}
|
||||
video.num_low_fields++;
|
||||
}
|
||||
else if (video.num_low_fields > 0)
|
||||
{
|
||||
printf("%6d.%d-%6d.%d: active video signal level low for %d fields (WARNING)\n", video.first_low_frame, video.first_low_field, frame, fieldnum, video.num_low_fields);
|
||||
video.first_low_frame = video.first_low_field = video.num_low_fields = -1;
|
||||
}
|
||||
|
||||
// track high fields
|
||||
if (ymaxval >= 255)
|
||||
{
|
||||
if (video.first_high_frame == -1)
|
||||
{
|
||||
video.first_high_frame = frame;
|
||||
video.first_high_field = fieldnum;
|
||||
video.num_high_fields = 0;
|
||||
}
|
||||
video.num_high_fields++;
|
||||
}
|
||||
else if (video.num_high_fields > 0)
|
||||
{
|
||||
printf("%6d.%d-%6d.%d: active video signal level high for %d fields (WARNING)\n", video.first_high_frame, video.first_high_field, frame, fieldnum, video.num_high_fields);
|
||||
video.first_high_frame = video.first_high_field = video.num_high_fields = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// verify_video_final - final verification
|
||||
//-------------------------------------------------
|
||||
|
||||
static void verify_video_final(video_info &video, int frame, bitmap_yuy16 &bitmap)
|
||||
{
|
||||
int fields_per_frame = (bitmap.height() >= 288) ? 2 : 1;
|
||||
int field = frame * fields_per_frame;
|
||||
|
||||
// did we ever see any white flags?
|
||||
if (video.first_whitefield == -1)
|
||||
printf("Track %6d.%d: never saw any white flags (WARNING)\n", field / fields_per_frame, 0);
|
||||
|
||||
// did we ever see any lead-out?
|
||||
if (!video.saw_leadout)
|
||||
printf("Track %6d.%d: never saw any lead-out (WARNING)\n", field / fields_per_frame, 0);
|
||||
|
||||
// any remaining high/low reports?
|
||||
if (video.num_blank_fields >= REPORT_BLANKS_THRESHOLD)
|
||||
printf("%6d.%d-%6d.%d: blank frames for %d fields (INFO)\n", video.first_blank_frame, video.first_blank_field, frame, 0, video.num_blank_fields);
|
||||
if (video.num_low_fields > 0)
|
||||
printf("%6d.%d-%6d.%d: active video signal level low for %d fields (WARNING)\n", video.first_low_frame, video.first_low_field, frame, 0, video.num_low_fields);
|
||||
if (video.num_high_fields > 0)
|
||||
printf("%6d.%d-%6d.%d: active video signal level high for %d fields (WARNING)\n", video.first_high_frame, video.first_high_field, frame, 0, video.num_high_fields);
|
||||
|
||||
// summary info
|
||||
printf("\nVideo summary:\n");
|
||||
printf(" Overall video range: %d-%d (%02X-%02X)\n", video.min_overall, video.max_overall, video.min_overall, video.max_overall);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// init_audio - init audio info structure
|
||||
//-------------------------------------------------
|
||||
|
||||
static void init_audio(audio_info &audio)
|
||||
{
|
||||
audio.min_lsample = 32767;
|
||||
audio.min_rsample = 32767;
|
||||
audio.max_lsample = -32768;
|
||||
audio.max_rsample = -32768;
|
||||
audio.min_lsample_count = 0;
|
||||
audio.min_rsample_count = 0;
|
||||
audio.max_lsample_count = 0;
|
||||
audio.max_rsample_count = 0;
|
||||
audio.sample_count = 0;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// verify_audio - verify audio data
|
||||
//-------------------------------------------------
|
||||
|
||||
static void verify_audio(audio_info &audio, const int16_t *lsound, const int16_t *rsound, int samples)
|
||||
{
|
||||
// count the overall samples
|
||||
audio.sample_count += samples;
|
||||
|
||||
// iterate over samples, tracking min/max
|
||||
for (int sampnum = 0; sampnum < samples; sampnum++)
|
||||
{
|
||||
// did we hit a minimum on the left?
|
||||
if (lsound[sampnum] < audio.min_lsample)
|
||||
{
|
||||
audio.min_lsample = lsound[sampnum];
|
||||
audio.min_lsample_count = 1;
|
||||
}
|
||||
else if (lsound[sampnum] == audio.min_lsample)
|
||||
audio.min_lsample_count++;
|
||||
|
||||
// did we hit a maximum on the left?
|
||||
if (lsound[sampnum] > audio.max_lsample)
|
||||
{
|
||||
audio.max_lsample = lsound[sampnum];
|
||||
audio.max_lsample_count = 1;
|
||||
}
|
||||
else if (lsound[sampnum] == audio.max_lsample)
|
||||
audio.max_lsample_count++;
|
||||
|
||||
// did we hit a minimum on the right?
|
||||
if (rsound[sampnum] < audio.min_rsample)
|
||||
{
|
||||
audio.min_rsample = rsound[sampnum];
|
||||
audio.min_rsample_count = 1;
|
||||
}
|
||||
else if (rsound[sampnum] == audio.min_rsample)
|
||||
audio.min_rsample_count++;
|
||||
|
||||
// did we hit a maximum on the right?
|
||||
if (rsound[sampnum] > audio.max_rsample)
|
||||
{
|
||||
audio.max_rsample = rsound[sampnum];
|
||||
audio.max_rsample_count = 1;
|
||||
}
|
||||
else if (rsound[sampnum] == audio.max_rsample)
|
||||
audio.max_rsample_count++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// verify_audio_final - final verification
|
||||
//-------------------------------------------------
|
||||
|
||||
static void verify_audio_final(audio_info &audio)
|
||||
{
|
||||
printf("\nAudio summary:\n");
|
||||
printf(" Overall channel 0 range: %d-%d (%04X-%04X)\n", audio.min_lsample, audio.max_lsample, uint16_t(audio.min_lsample), uint16_t(audio.max_lsample));
|
||||
printf(" Overall channel 1 range: %d-%d (%04X-%04X)\n", audio.min_rsample, audio.max_rsample, uint16_t(audio.min_rsample), uint16_t(audio.max_rsample));
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// usage - display program usage
|
||||
//-------------------------------------------------
|
||||
|
||||
static int usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: \n");
|
||||
fprintf(stderr, " ldverify <avifile.avi|chdfile.chd>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// main - main entry point
|
||||
//-------------------------------------------------
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// init globals
|
||||
audio_info audio;
|
||||
init_audio(audio);
|
||||
video_info video;
|
||||
init_video(video);
|
||||
|
||||
// verify arguments
|
||||
if (argc < 2)
|
||||
return usage();
|
||||
const char *srcfile = argv[1];
|
||||
|
||||
// check extension of file
|
||||
int srcfilelen = strlen(srcfile);
|
||||
if (srcfilelen < 4)
|
||||
return usage();
|
||||
bool isavi;
|
||||
if (tolower((uint8_t)srcfile[srcfilelen-3]) == 'a' && tolower((uint8_t)srcfile[srcfilelen-2]) == 'v' && tolower((uint8_t)srcfile[srcfilelen-1]) == 'i')
|
||||
isavi = true;
|
||||
else if (tolower((uint8_t)srcfile[srcfilelen-3]) == 'c' && tolower((uint8_t)srcfile[srcfilelen-2]) == 'h' && tolower((uint8_t)srcfile[srcfilelen-1]) == 'd')
|
||||
isavi = false;
|
||||
else
|
||||
return usage();
|
||||
|
||||
// open the file
|
||||
printf("Processing file: %s\n", srcfile);
|
||||
movie_info info = { 0 };
|
||||
void *file = isavi ? open_avi(srcfile, info) : open_chd(srcfile, info);
|
||||
if (file == nullptr)
|
||||
{
|
||||
fprintf(stderr, "Unable to open file '%s'\n", srcfile);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// comment on the video dimensions
|
||||
printf("Video dimensions: %dx%d\n", info.width, info.height);
|
||||
if (info.width != 720)
|
||||
printf("WARNING: Unexpected video width (should be 720)\n");
|
||||
if (info.height != 524)
|
||||
printf("WARNING: Unexpected video height (should be 262 or 524)\n");
|
||||
|
||||
// comment on the video frame rate
|
||||
printf("Video frame rate: %.2fHz\n", info.framerate);
|
||||
if (int(info.framerate * 100.0 + 0.5) != 2997)
|
||||
printf("WARNING: Unexpected frame rate (should be 29.97Hz)\n");
|
||||
|
||||
// comment on the sample rate
|
||||
printf("Sample rate: %dHz\n", info.samplerate);
|
||||
if (info.samplerate != 48000)
|
||||
printf("WARNING: Unexpected sampele rate (should be 48000Hz)\n");
|
||||
|
||||
// allocate a bitmap
|
||||
bitmap_yuy16 bitmap(info.width, info.height);
|
||||
|
||||
// allocate sound buffers
|
||||
std::vector<int16_t> lsound(info.samplerate);
|
||||
std::vector<int16_t> rsound(info.samplerate);
|
||||
|
||||
// loop over frames
|
||||
int frame = 0;
|
||||
int samples = 0;
|
||||
while (isavi ? read_avi(file, frame, bitmap, &lsound[0], &rsound[0], samples) : read_chd(file, frame, bitmap, &lsound[0], &rsound[0], samples))
|
||||
{
|
||||
verify_video(video, frame, bitmap);
|
||||
verify_audio(audio, &lsound[0], &rsound[0], samples);
|
||||
frame++;
|
||||
}
|
||||
|
||||
// close the files
|
||||
isavi ? close_avi(file) : close_chd(file);
|
||||
|
||||
// final output
|
||||
verify_video_final(video, frame, bitmap);
|
||||
verify_audio_final(audio);
|
||||
}
|
||||
catch (std::bad_alloc &)
|
||||
{
|
||||
fprintf(stderr, "Out of memory allocating memory\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,200 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Aaron Giles
|
||||
/***************************************************************************
|
||||
|
||||
pngcmp.c
|
||||
|
||||
PNG comparison (based on regrep.c)
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "corefile.h"
|
||||
#include "png.h"
|
||||
|
||||
#include "osdfile.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
CONSTANTS & DEFINES
|
||||
***************************************************************************/
|
||||
|
||||
#define BITMAP_SPACE 4
|
||||
|
||||
/***************************************************************************
|
||||
PROTOTYPES
|
||||
***************************************************************************/
|
||||
|
||||
static int generate_png_diff(const std::string& imgfile1, const std::string& imgfile2, const std::string& outfilename);
|
||||
|
||||
/***************************************************************************
|
||||
MAIN
|
||||
***************************************************************************/
|
||||
|
||||
/*-------------------------------------------------
|
||||
main - main entry point
|
||||
-------------------------------------------------*/
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
/* first argument is the directory */
|
||||
if (argc < 4)
|
||||
{
|
||||
fprintf(stderr, "Usage:\npngcmp <image1> <image2> <outfile>\n");
|
||||
return 10;
|
||||
}
|
||||
std::string imgfilename1(argv[1]);
|
||||
std::string imgfilename2(argv[2]);
|
||||
std::string outfilename(argv[3]);
|
||||
|
||||
try {
|
||||
return generate_png_diff(imgfilename1, imgfilename2, outfilename);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
printf("Exception occurred");
|
||||
return 1000;
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
generate_png_diff - create a new PNG file
|
||||
that shows multiple differing PNGs side by
|
||||
side with a third set of differences
|
||||
-------------------------------------------------*/
|
||||
|
||||
static int generate_png_diff(const std::string& imgfile1, const std::string& imgfile2, const std::string& outfilename)
|
||||
{
|
||||
bitmap_argb32 bitmap1;
|
||||
bitmap_argb32 bitmap2;
|
||||
bitmap_argb32 finalbitmap;
|
||||
int width, height, maxwidth;
|
||||
util::core_file::ptr file;
|
||||
std::error_condition filerr;
|
||||
int error = 100;
|
||||
|
||||
/* open the source image */
|
||||
filerr = util::core_file::open(imgfile1, OPEN_FLAG_READ, file);
|
||||
if (filerr)
|
||||
{
|
||||
printf("Could not open %s (%s)\n", imgfile1.c_str(), filerr.message().c_str());
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* load the source image */
|
||||
filerr = util::png_read_bitmap(*file, bitmap1);
|
||||
file.reset();
|
||||
if (filerr)
|
||||
{
|
||||
printf("Could not read %s (%s)\n", imgfile1.c_str(), filerr.message().c_str());
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* open the source image */
|
||||
filerr = util::core_file::open(imgfile2, OPEN_FLAG_READ, file);
|
||||
if (filerr)
|
||||
{
|
||||
printf("Could not open %s (%s)\n", imgfile2.c_str(), filerr.message().c_str());
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* load the source image */
|
||||
filerr = util::png_read_bitmap(*file, bitmap2);
|
||||
file.reset();
|
||||
if (filerr)
|
||||
{
|
||||
printf("Could not read %s (%s)\n", imgfile2.c_str(), filerr.message().c_str());
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* if the sizes are different, we differ; otherwise start off assuming we are the same */
|
||||
bool bitmaps_differ;
|
||||
bitmaps_differ = (bitmap2.width() != bitmap1.width() || bitmap2.height() != bitmap1.height());
|
||||
|
||||
/* compare scanline by scanline */
|
||||
for (int y = 0; y < bitmap2.height() && !bitmaps_differ; y++)
|
||||
{
|
||||
uint32_t const *base = &bitmap1.pix(y);
|
||||
uint32_t const *curr = &bitmap2.pix(y);
|
||||
|
||||
/* scan the scanline */
|
||||
int x;
|
||||
for (x = 0; x < bitmap2.width(); x++)
|
||||
if (*base++ != *curr++)
|
||||
break;
|
||||
bitmaps_differ = (x != bitmap2.width());
|
||||
}
|
||||
|
||||
if (bitmaps_differ)
|
||||
{
|
||||
/* determine the size of the final bitmap */
|
||||
height = width = 0;
|
||||
{
|
||||
/* determine the maximal width */
|
||||
maxwidth = std::max(bitmap1.width(), bitmap2.width());
|
||||
width = bitmap1.width() + BITMAP_SPACE + maxwidth + BITMAP_SPACE + maxwidth;
|
||||
|
||||
/* add to the height */
|
||||
height += std::max(bitmap1.height(), bitmap2.height());
|
||||
}
|
||||
|
||||
/* allocate the final bitmap */
|
||||
finalbitmap.allocate(width, height);
|
||||
|
||||
/* now copy and compare each set of bitmaps */
|
||||
int curheight = std::max(bitmap1.height(), bitmap2.height());
|
||||
/* iterate over rows in these bitmaps */
|
||||
for (int y = 0; y < curheight; y++)
|
||||
{
|
||||
uint32_t const *src1 = (y < bitmap1.height()) ? &bitmap1.pix(y) : nullptr;
|
||||
uint32_t const *src2 = (y < bitmap2.height()) ? &bitmap2.pix(y) : nullptr;
|
||||
uint32_t *dst1 = &finalbitmap.pix(y);
|
||||
uint32_t *dst2 = &finalbitmap.pix(y, bitmap1.width() + BITMAP_SPACE);
|
||||
uint32_t *dstdiff = &finalbitmap.pix(y, bitmap1.width() + BITMAP_SPACE + maxwidth + BITMAP_SPACE);
|
||||
|
||||
/* now iterate over columns */
|
||||
for (int x = 0; x < maxwidth; x++)
|
||||
{
|
||||
int pix1 = -1, pix2 = -2;
|
||||
|
||||
if (src1 != nullptr && x < bitmap1.width())
|
||||
pix1 = dst1[x] = src1[x];
|
||||
if (src2 != nullptr && x < bitmap2.width())
|
||||
pix2 = dst2[x] = src2[x];
|
||||
dstdiff[x] = (pix1 != pix2) ? 0xffffffff : 0xff000000;
|
||||
}
|
||||
}
|
||||
|
||||
/* write the final PNG */
|
||||
filerr = util::core_file::open(outfilename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file);
|
||||
if (filerr)
|
||||
{
|
||||
printf("Could not open %s (%s)\n", outfilename.c_str(), filerr.message().c_str());
|
||||
goto error;
|
||||
}
|
||||
filerr = util::png_write_bitmap(*file, nullptr, finalbitmap, 0, nullptr);
|
||||
file.reset();
|
||||
if (filerr)
|
||||
{
|
||||
printf("Could not write %s (%s)\n", outfilename.c_str(), filerr.message().c_str());
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we get here, we are error free */
|
||||
if (bitmaps_differ)
|
||||
error = 1;
|
||||
else
|
||||
error = 0;
|
||||
|
||||
error:
|
||||
if (error == -1)
|
||||
osd_file::remove(outfilename);
|
||||
return error;
|
||||
}
|
1113
src/tools/regrep.cpp
1113
src/tools/regrep.cpp
File diff suppressed because it is too large
Load Diff
@ -1,778 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Aaron Giles,Nicola Salmoria
|
||||
/***************************************************************************
|
||||
|
||||
romcmp.c
|
||||
|
||||
ROM comparison utility program.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "hash.h"
|
||||
#include "path.h"
|
||||
#include "unzip.h"
|
||||
|
||||
#include "osdfile.h"
|
||||
#include "osdcomm.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
|
||||
|
||||
#define MAX_FILES 1000
|
||||
|
||||
|
||||
|
||||
/* compare modes when one file is twice as long as the other */
|
||||
/* A = All file */
|
||||
/* 12 = 1st half */
|
||||
/* 22 = 2nd half */
|
||||
/* E = Even bytes */
|
||||
/* O = Odd bytes */
|
||||
/* E1 = Even bytes 1st half */
|
||||
/* O1 = Odd bytes 1st half */
|
||||
/* E2 = Even bytes 2nd half */
|
||||
/* O2 = Odd bytes 2nd half */
|
||||
enum
|
||||
{
|
||||
MODE_A,
|
||||
MODE_NIB1,MODE_NIB2,
|
||||
MODE_12, MODE_22,
|
||||
MODE_14, MODE_24, MODE_34, MODE_44,
|
||||
MODE_E, MODE_O,
|
||||
MODE_E12, MODE_O12, MODE_E22, MODE_O22,
|
||||
TOTAL_MODES
|
||||
};
|
||||
|
||||
static const char *const modenames[] =
|
||||
{
|
||||
" ",
|
||||
"[bits 0-3]",
|
||||
"[bits 4-7]",
|
||||
"[1/2] ",
|
||||
"[2/2] ",
|
||||
"[1/4] ",
|
||||
"[2/4] ",
|
||||
"[3/4] ",
|
||||
"[4/4] ",
|
||||
"[even] ",
|
||||
"[odd] ",
|
||||
"[even 1/2]",
|
||||
"[odd 1/2] ",
|
||||
"[even 2/2]",
|
||||
"[odd 2/2] ",
|
||||
};
|
||||
|
||||
static void compatiblemodes(int mode,int *start,int *end)
|
||||
{
|
||||
if (mode == MODE_A)
|
||||
{
|
||||
*start = MODE_A;
|
||||
*end = MODE_A;
|
||||
}
|
||||
if (mode >= MODE_NIB1 && mode <= MODE_NIB2)
|
||||
{
|
||||
*start = MODE_NIB1;
|
||||
*end = MODE_NIB2;
|
||||
}
|
||||
if (mode >= MODE_12 && mode <= MODE_22)
|
||||
{
|
||||
*start = MODE_12;
|
||||
*end = MODE_22;
|
||||
}
|
||||
if (mode >= MODE_14 && mode <= MODE_44)
|
||||
{
|
||||
*start = MODE_14;
|
||||
*end = MODE_44;
|
||||
}
|
||||
if (mode >= MODE_E && mode <= MODE_O)
|
||||
{
|
||||
*start = MODE_E;
|
||||
*end = MODE_O;
|
||||
}
|
||||
if (mode >= MODE_E12 && mode <= MODE_O22)
|
||||
{
|
||||
*start = MODE_E12;
|
||||
*end = MODE_O22;
|
||||
}
|
||||
}
|
||||
|
||||
struct fileinfo
|
||||
{
|
||||
std::string name;
|
||||
int size;
|
||||
std::unique_ptr<unsigned char []> buf; // file is read in here
|
||||
int listed;
|
||||
|
||||
|
||||
static constexpr bool is_ascii_char(int ch)
|
||||
{
|
||||
return (ch >= 0x20 && ch < 0x7f) || (ch == '\n') || (ch == '\r') || (ch == '\t');
|
||||
}
|
||||
|
||||
|
||||
void checkintegrity(int side, bool all_hashes) const
|
||||
{
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
if (all_hashes)
|
||||
{
|
||||
util::crc32_creator crc32;
|
||||
util::sha1_creator sha1;
|
||||
util::sum16_creator sum16;
|
||||
crc32.append(buf.get(), size);
|
||||
sha1.append(buf.get(), size);
|
||||
sum16.append(buf.get(), size);
|
||||
printf("%-23s %-23s [0x%x] CRC(%s) SHA1(%s) SUM(%s)\n",
|
||||
(side & 1) ? name.c_str() : "",
|
||||
(side & 2) ? name.c_str() : "",
|
||||
size,
|
||||
crc32.finish().as_string().c_str(),
|
||||
sha1.finish().as_string().c_str(),
|
||||
sum16.finish().as_string().c_str());
|
||||
side = 0;
|
||||
}
|
||||
|
||||
// check for bad data lines
|
||||
unsigned mask0 = 0x0000;
|
||||
unsigned mask1 = 0xffff;
|
||||
|
||||
bool is_ascii = true;
|
||||
for (unsigned i = 0; i < size; i += 2)
|
||||
{
|
||||
is_ascii = is_ascii && is_ascii_char(buf[i]);
|
||||
mask0 |= buf[i] << 8;
|
||||
mask1 &= (buf[i] << 8) | 0x00ff;
|
||||
if (i < size - 1)
|
||||
{
|
||||
is_ascii = is_ascii && is_ascii_char(buf[i+1]);
|
||||
mask0 |= buf[i+1];
|
||||
mask1 &= buf[i+1] | 0xff00;
|
||||
}
|
||||
if (mask0 == 0xffff && mask1 == 0x0000) break;
|
||||
}
|
||||
|
||||
if (is_ascii && mask0 == 0x7f7f && mask1 == 0)
|
||||
{
|
||||
printf("%-23s %-23s ASCII TEXT FILE\n", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mask0 != 0xffff || mask1 != 0x0000)
|
||||
{
|
||||
int fixedmask;
|
||||
int bits;
|
||||
|
||||
fixedmask = (~mask0 | mask1) & 0xffff;
|
||||
|
||||
if (((mask0 >> 8) & 0xff) == (mask0 & 0xff) && ((mask1 >> 8) & 0xff) == (mask1 & 0xff))
|
||||
bits = 8;
|
||||
else bits = 16;
|
||||
|
||||
printf("%-23s %-23s FIXED BITS (", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "");
|
||||
for (int i = 0; i < bits; i++)
|
||||
{
|
||||
if (~mask0 & 0x8000) printf("0");
|
||||
else if (mask1 & 0x8000) printf("1");
|
||||
else printf("x");
|
||||
|
||||
mask0 <<= 1;
|
||||
mask1 <<= 1;
|
||||
}
|
||||
printf(")\n");
|
||||
|
||||
/* if the file contains a fixed value, we don't need to do the other */
|
||||
/* validity checks */
|
||||
if (fixedmask == 0xffff || fixedmask == 0x00ff || fixedmask == 0xff00)
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned addrbit = 1;
|
||||
unsigned addrmirror = 0;
|
||||
while (addrbit <= size/2)
|
||||
{
|
||||
unsigned i = 0;
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
if ((i ^ addrbit) < size && buf[i] != buf[i ^ addrbit]) break;
|
||||
}
|
||||
|
||||
if (i == size)
|
||||
addrmirror |= addrbit;
|
||||
|
||||
addrbit <<= 1;
|
||||
}
|
||||
|
||||
if (addrmirror != 0)
|
||||
{
|
||||
if (addrmirror == size/2)
|
||||
{
|
||||
printf("%-23s %-23s 1ST AND 2ND HALF IDENTICAL\n", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "");
|
||||
util::hash_collection hash;
|
||||
hash.begin();
|
||||
hash.buffer(buf.get(), size / 2);
|
||||
hash.end();
|
||||
printf("%-23s %-23s %s\n", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "", hash.attribute_string().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%-23s %-23s BADADDR", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "");
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
if (size <= (1<<(23-i))) printf(" ");
|
||||
else if (addrmirror & 0x800000) printf("-");
|
||||
else printf("x");
|
||||
addrmirror <<= 1;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned sizemask = 1;
|
||||
while (sizemask < size - 1)
|
||||
sizemask = (sizemask << 1) | 1;
|
||||
|
||||
mask0 = 0x000000;
|
||||
mask1 = sizemask;
|
||||
for (unsigned i = 0; i < size; i++)
|
||||
{
|
||||
if (buf[i] != 0xff)
|
||||
{
|
||||
mask0 |= i;
|
||||
mask1 &= i;
|
||||
if (mask0 == sizemask && mask1 == 0x00) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask0 != sizemask || mask1 != 0x00)
|
||||
{
|
||||
printf("%-23s %-23s ", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "");
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
if (size <= (1<<(23-i))) printf(" ");
|
||||
else if (~mask0 & 0x800000) printf("1");
|
||||
else if (mask1 & 0x800000) printf("0");
|
||||
else printf("x");
|
||||
mask0 <<= 1;
|
||||
mask1 <<= 1;
|
||||
}
|
||||
printf(" = 0xFF\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
mask0 = 0x000000;
|
||||
mask1 = sizemask;
|
||||
for (unsigned i = 0; i < size; i++)
|
||||
{
|
||||
if (buf[i] != 0x00)
|
||||
{
|
||||
mask0 |= i;
|
||||
mask1 &= i;
|
||||
if (mask0 == sizemask && mask1 == 0x00) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask0 != sizemask || mask1 != 0x00)
|
||||
{
|
||||
printf("%-23s %-23s ", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "");
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
if (size <= (1<<(23-i))) printf(" ");
|
||||
else if ((mask0 & 0x800000) == 0) printf("1");
|
||||
else if (mask1 & 0x800000) printf("0");
|
||||
else printf("x");
|
||||
mask0 <<= 1;
|
||||
mask1 <<= 1;
|
||||
}
|
||||
printf(" = 0x00\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
mask0 = 0xff;
|
||||
for (unsigned i = 0; i < size/4 && mask0 != 0x00; i++)
|
||||
{
|
||||
if (buf[ 2*i ] != 0x00) mask0 &= ~0x01;
|
||||
if (buf[ 2*i ] != 0xff) mask0 &= ~0x02;
|
||||
if (buf[ 2*i+1] != 0x00) mask0 &= ~0x04;
|
||||
if (buf[ 2*i+1] != 0xff) mask0 &= ~0x08;
|
||||
if (buf[size/2 + 2*i ] != 0x00) mask0 &= ~0x10;
|
||||
if (buf[size/2 + 2*i ] != 0xff) mask0 &= ~0x20;
|
||||
if (buf[size/2 + 2*i+1] != 0x00) mask0 &= ~0x40;
|
||||
if (buf[size/2 + 2*i+1] != 0xff) mask0 &= ~0x80;
|
||||
}
|
||||
|
||||
if (mask0 & 0x01) printf("%-23s %-23s 1ST HALF = 00xx\n", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "");
|
||||
if (mask0 & 0x02) printf("%-23s %-23s 1ST HALF = FFxx\n", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "");
|
||||
if (mask0 & 0x04) printf("%-23s %-23s 1ST HALF = xx00\n", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "");
|
||||
if (mask0 & 0x08) printf("%-23s %-23s 1ST HALF = xxFF\n", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "");
|
||||
if (mask0 & 0x10) printf("%-23s %-23s 2ND HALF = 00xx\n", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "");
|
||||
if (mask0 & 0x20) printf("%-23s %-23s 2ND HALF = FFxx\n", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "");
|
||||
if (mask0 & 0x40) printf("%-23s %-23s 2ND HALF = xx00\n", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "");
|
||||
if (mask0 & 0x80) printf("%-23s %-23s 2ND HALF = xxFF\n", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "");
|
||||
}
|
||||
|
||||
int usedbytes(int mode) const
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case MODE_A:
|
||||
case MODE_NIB1:
|
||||
case MODE_NIB2:
|
||||
return size;
|
||||
case MODE_12:
|
||||
case MODE_22:
|
||||
case MODE_E:
|
||||
case MODE_O:
|
||||
return size / 2;
|
||||
case MODE_14:
|
||||
case MODE_24:
|
||||
case MODE_34:
|
||||
case MODE_44:
|
||||
case MODE_E12:
|
||||
case MODE_O12:
|
||||
case MODE_E22:
|
||||
case MODE_O22:
|
||||
return size / 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void basemultmask(int mode, int &base, int &mult, int &mask) const
|
||||
{
|
||||
mult = 1;
|
||||
if (mode >= MODE_E)
|
||||
mult = 2;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case MODE_A:
|
||||
case MODE_12:
|
||||
case MODE_14:
|
||||
case MODE_E:
|
||||
case MODE_E12:
|
||||
base = 0; mask = 0xff; break;
|
||||
case MODE_NIB1:
|
||||
base = 0; mask = 0x0f; break;
|
||||
case MODE_NIB2:
|
||||
base = 0; mask = 0xf0; break;
|
||||
case MODE_O:
|
||||
case MODE_O12:
|
||||
base = 1; mask = 0xff; break;
|
||||
case MODE_22:
|
||||
case MODE_E22:
|
||||
base = size / 2; mask = 0xff; break;
|
||||
case MODE_O22:
|
||||
base = 1 + size / 2; mask = 0xff; break;
|
||||
case MODE_24:
|
||||
base = size / 4; mask = 0xff; break;
|
||||
case MODE_34:
|
||||
base = 2*size / 4; mask = 0xff; break;
|
||||
case MODE_44:
|
||||
base = 3*size / 4; mask = 0xff; break;
|
||||
}
|
||||
}
|
||||
|
||||
float compare(const fileinfo &file2, int mode1, int mode2) const
|
||||
{
|
||||
if (!buf || !file2.buf)
|
||||
return 0.0;
|
||||
|
||||
int const size1 = usedbytes(mode1);
|
||||
int const size2 = file2.usedbytes(mode2);
|
||||
|
||||
if (size1 != size2)
|
||||
return 0.0;
|
||||
|
||||
int base1=0, base2=0, mult1=0, mult2=0, mask1=0, mask2=0;
|
||||
basemultmask(mode1, base1, mult1, mask1);
|
||||
file2.basemultmask(mode2, base2, mult2, mask2);
|
||||
|
||||
int match = 0;
|
||||
if (mask1 == mask2)
|
||||
{
|
||||
if (mask1 == 0xff)
|
||||
{
|
||||
// normal compare
|
||||
for (int i = 0; i < size1; i++)
|
||||
if (buf[base1 + mult1 * i] == file2.buf[base2 + mult2 * i]) match++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// nibble compare, abort if other half is not empty
|
||||
for (int i = 0; i < size1; i++)
|
||||
{
|
||||
if (((buf[base1 + mult1 * i] & ~mask1) != (0x00 & ~mask1) &&
|
||||
(buf[base1 + mult1 * i] & ~mask1) != (0xff & ~mask1)) ||
|
||||
((file2.buf[base1 + mult1 * i] & ~mask2) != (0x00 & ~mask2) &&
|
||||
(file2.buf[base1 + mult1 * i] & ~mask2) != (0xff & ~mask2)))
|
||||
{
|
||||
match = 0;
|
||||
break;
|
||||
}
|
||||
if ((buf[base1 + mult1 * i] & mask1) == (file2.buf[base2 + mult2 * i] & mask2)) match++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return float(match) / size1;
|
||||
}
|
||||
|
||||
void readfile(const char *path)
|
||||
{
|
||||
std::string fullname(path ? path : "");
|
||||
util::path_append(fullname, name);
|
||||
|
||||
buf.reset(new (std::nothrow) unsigned char [size]);
|
||||
if (!buf)
|
||||
{
|
||||
printf("%s: out of memory!\n", name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
std::error_condition filerr;
|
||||
osd_file::ptr f;
|
||||
uint64_t filesize;
|
||||
|
||||
filerr = osd_file::open(fullname, OPEN_FLAG_READ, f, filesize);
|
||||
if (filerr)
|
||||
{
|
||||
printf("%s: error %s\n", fullname.c_str(), filerr.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t actual;
|
||||
filerr = f->read(buf.get(), 0, size, actual);
|
||||
if (filerr)
|
||||
{
|
||||
printf("%s: error %s\n", fullname.c_str(), filerr.message().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void free()
|
||||
{
|
||||
buf.reset();
|
||||
}
|
||||
};
|
||||
|
||||
static fileinfo files[2][MAX_FILES];
|
||||
static float matchscore[MAX_FILES][MAX_FILES][TOTAL_MODES][TOTAL_MODES];
|
||||
|
||||
|
||||
static void printname(const fileinfo *file1, const fileinfo *file2, float score, int mode1, int mode2)
|
||||
{
|
||||
printf(
|
||||
"%-12s %s %-12s %s ",
|
||||
file1 ? file1->name.c_str() : "",
|
||||
modenames[mode1],
|
||||
file2 ? file2->name.c_str() : "",
|
||||
modenames[mode2]);
|
||||
if (score == 0.0f) printf("NO MATCH\n");
|
||||
else if (score == 1.0f) printf("IDENTICAL\n");
|
||||
else printf("%3.6f%%\n", double(score*100));
|
||||
}
|
||||
|
||||
|
||||
static int load_files(int i, int *found, const char *path)
|
||||
{
|
||||
/* attempt to open as a directory first */
|
||||
auto dir = osd::directory::open(path);
|
||||
if (dir)
|
||||
{
|
||||
const osd::directory::entry *d;
|
||||
|
||||
/* load all files in directory */
|
||||
while ((d = dir->read()) != nullptr)
|
||||
{
|
||||
const char *d_name = d->name;
|
||||
const std::string buf(util::path_concat(path, d_name)); // FIXME: is this even used for anything?
|
||||
|
||||
if (d->type == osd::directory::entry::entry_type::FILE)
|
||||
{
|
||||
uint64_t size = d->size;
|
||||
while (size && (size & 1) == 0) size >>= 1;
|
||||
//if (size & ~1)
|
||||
// printf("%-23s %-23s ignored (not a ROM)\n", i ? "" : d_name, i ? d_name : "");
|
||||
//else
|
||||
{
|
||||
files[i][found[i]].name = d_name;
|
||||
files[i][found[i]].size = d->size;
|
||||
files[i][found[i]].readfile(path);
|
||||
files[i][found[i]].listed = 0;
|
||||
if (found[i] >= MAX_FILES)
|
||||
{
|
||||
printf("%s: max of %d files exceeded\n", path, MAX_FILES);
|
||||
break;
|
||||
}
|
||||
found[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
dir.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if not, try to open as a ZIP file */
|
||||
util::archive_file::ptr zip;
|
||||
|
||||
/* wasn't a directory, so try to open it as a zip file */
|
||||
if (util::archive_file::open_zip(path, zip) && util::archive_file::open_7z(path, zip))
|
||||
{
|
||||
printf("Error, cannot open zip file '%s' !\n", path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* load all files in zip file */
|
||||
for (int zipent = zip->first_file(); zipent >= 0; zipent = zip->next_file())
|
||||
{
|
||||
if (zip->current_is_directory()) continue;
|
||||
|
||||
int size;
|
||||
|
||||
size = zip->current_uncompressed_length();
|
||||
while (size && (size & 1) == 0) size >>= 1;
|
||||
if (zip->current_uncompressed_length() == 0) // || (size & ~1))
|
||||
{
|
||||
printf("%-23s %-23s ignored (not a ROM)\n",
|
||||
i ? "" : zip->current_name().c_str(), i ? zip->current_name().c_str() : "");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileinfo &file = files[i][found[i]];
|
||||
const char *delim = strrchr(zip->current_name().c_str(), '/');
|
||||
|
||||
if (delim)
|
||||
file.name = delim + 1;
|
||||
else
|
||||
file.name = zip->current_name();
|
||||
file.size = zip->current_uncompressed_length();
|
||||
file.buf.reset(new (std::nothrow) unsigned char [file.size]);
|
||||
if (!file.buf)
|
||||
{
|
||||
printf("%s: out of memory!\n", file.name.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (zip->decompress(file.buf.get(), file.size))
|
||||
file.free();
|
||||
}
|
||||
|
||||
file.listed = 0;
|
||||
if (found[i] >= MAX_FILES)
|
||||
{
|
||||
printf("%s: max of %d files exceeded\n",path,MAX_FILES);
|
||||
break;
|
||||
}
|
||||
found[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int CLIB_DECL main(int argc,char *argv[])
|
||||
{
|
||||
int err;
|
||||
int total_modes = MODE_NIB2; /* by default, use only MODE_A, MODE_NIB1 and MODE_NIB2 */
|
||||
bool all_hashes = false;
|
||||
|
||||
while (argc >= 2)
|
||||
{
|
||||
if (strcmp(argv[1], "-d") == 0)
|
||||
total_modes = TOTAL_MODES;
|
||||
else if (strcmp(argv[1], "-h") == 0)
|
||||
all_hashes = true;
|
||||
else
|
||||
break;
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
printf("usage: romcmp [-d] [-h] [dir1 | zip1] [dir2 | zip2]\n");
|
||||
printf("-d enables a slower, more comprehensive comparison.\n");
|
||||
printf("-h prints hashes and sums for all files.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
{
|
||||
int found[2] = { 0, 0 };
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (argc > i+1)
|
||||
{
|
||||
err = load_files (i, found, argv[i+1]);
|
||||
if (err != 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc >= 3)
|
||||
printf("%d and %d files\n",found[0],found[1]);
|
||||
else
|
||||
printf("%d files\n",found[0]);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
for (int j = 0; j < found[i]; j++)
|
||||
{
|
||||
files[i][j].checkintegrity(1 << i, all_hashes);
|
||||
}
|
||||
}
|
||||
|
||||
if (argc < 3)
|
||||
{
|
||||
// find duplicates in one dir
|
||||
for (int i = 0;i < found[0];i++)
|
||||
{
|
||||
for (int j = i+1;j < found[0];j++)
|
||||
{
|
||||
for (int mode1 = 0;mode1 < total_modes;mode1++)
|
||||
{
|
||||
for (int mode2 = 0;mode2 < total_modes;mode2++)
|
||||
{
|
||||
if (files[0][i].compare(files[0][j],mode1,mode2) == 1.0f)
|
||||
printname(&files[0][i],&files[0][j],1.0,mode1,mode2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// compare two dirs
|
||||
for (int i = 0;i < found[0];i++)
|
||||
{
|
||||
for (int j = 0;j < found[1];j++)
|
||||
{
|
||||
fprintf(stderr,"%2d%%\r",100*(i*found[1]+j)/(found[0]*found[1]));
|
||||
for (int mode1 = 0;mode1 < total_modes;mode1++)
|
||||
{
|
||||
for (int mode2 = 0;mode2 < total_modes;mode2++)
|
||||
{
|
||||
matchscore[i][j][mode1][mode2] = files[0][i].compare(files[1][j],mode1,mode2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(stderr," \r");
|
||||
|
||||
int besti;
|
||||
do
|
||||
{
|
||||
besti = -1;
|
||||
int bestj = -1;
|
||||
float bestscore = 0.0;
|
||||
int bestmode1 = -1, bestmode2 = -1;
|
||||
|
||||
for (int mode1 = 0;mode1 < total_modes;mode1++)
|
||||
{
|
||||
for (int mode2 = 0;mode2 < total_modes;mode2++)
|
||||
{
|
||||
for (int i = 0;i < found[0];i++)
|
||||
{
|
||||
for (int j = 0;j < found[1];j++)
|
||||
{
|
||||
if (matchscore[i][j][mode1][mode2] > bestscore
|
||||
|| (matchscore[i][j][mode1][mode2] == 1.0f && mode2 == 0 && bestmode2 > 0))
|
||||
{
|
||||
bestscore = matchscore[i][j][mode1][mode2];
|
||||
besti = i;
|
||||
bestj = j;
|
||||
bestmode1 = mode1;
|
||||
bestmode2 = mode2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (besti != -1)
|
||||
{
|
||||
int start=0,end=0;
|
||||
|
||||
printname(&files[0][besti],&files[1][bestj],bestscore,bestmode1,bestmode2);
|
||||
files[0][besti].listed = 1;
|
||||
files[1][bestj].listed = 1;
|
||||
|
||||
matchscore[besti][bestj][bestmode1][bestmode2] = 0.0;
|
||||
|
||||
/* remove all matches using the same sections with a worse score */
|
||||
for (int j = 0;j < found[1];j++)
|
||||
{
|
||||
for (int mode2 = 0;mode2 < total_modes;mode2++)
|
||||
{
|
||||
if (matchscore[besti][j][bestmode1][mode2] < bestscore)
|
||||
matchscore[besti][j][bestmode1][mode2] = 0.0;
|
||||
}
|
||||
}
|
||||
for (int i = 0;i < found[0];i++)
|
||||
{
|
||||
for (int mode1 = 0;mode1 < total_modes;mode1++)
|
||||
{
|
||||
if (matchscore[i][bestj][mode1][bestmode2] < bestscore)
|
||||
matchscore[i][bestj][mode1][bestmode2] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove all matches using incompatible sections */
|
||||
compatiblemodes(bestmode1,&start,&end);
|
||||
for (int j = 0;j < found[1];j++)
|
||||
{
|
||||
for (int mode2 = 0;mode2 < total_modes;mode2++)
|
||||
{
|
||||
for (int mode1 = 0;mode1 < start;mode1++)
|
||||
matchscore[besti][j][mode1][mode2] = 0.0;
|
||||
for (int mode1 = end+1;mode1 < total_modes;mode1++)
|
||||
matchscore[besti][j][mode1][mode2] = 0.0;
|
||||
}
|
||||
}
|
||||
compatiblemodes(bestmode2,&start,&end);
|
||||
for (int i = 0;i < found[0];i++)
|
||||
{
|
||||
for (int mode1 = 0;mode1 < total_modes;mode1++)
|
||||
{
|
||||
for (int mode2 = 0;mode2 < start;mode2++)
|
||||
matchscore[i][bestj][mode1][mode2] = 0.0;
|
||||
for (int mode2 = end+1;mode2 < total_modes;mode2++)
|
||||
matchscore[i][bestj][mode1][mode2] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (besti != -1);
|
||||
|
||||
|
||||
for (int i = 0;i < found[0];i++)
|
||||
{
|
||||
if (files[0][i].listed == 0) printname(&files[0][i],nullptr,0.0,0,0);
|
||||
}
|
||||
for (int i = 0;i < found[1];i++)
|
||||
{
|
||||
if (files[1][i].listed == 0) printname(nullptr,&files[1][i],0.0,0,0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0;i < found[0];i++)
|
||||
{
|
||||
files[0][i].free();
|
||||
}
|
||||
for (int i = 0;i < found[1];i++)
|
||||
{
|
||||
files[1][i].free();
|
||||
}
|
||||
}
|
||||
|
||||
util::archive_file::cache_clear();
|
||||
return 0;
|
||||
}
|
@ -1,190 +0,0 @@
|
||||
@rem ----------------------------------------------------
|
||||
@rem MAME Testing script
|
||||
@rem (Windows only at the moment, sorry!)
|
||||
@rem
|
||||
@rem Initial setup of the script:
|
||||
@rem
|
||||
@rem 1. Create a fresh directory mametest/
|
||||
@rem 2. Copy this script into it (mametest/runtest.cmd)
|
||||
@rem 3. Copy a mame.ini with your ROM paths into it
|
||||
@rem (mametest/mame.ini)
|
||||
@rem 4. Copy a transparent crosshair cursor into it
|
||||
@rem (mametest/cross.png)
|
||||
@rem
|
||||
@rem How to run a test:
|
||||
@rem
|
||||
@rem 1. Create a new subdirectory mametest/version/
|
||||
@rem 2. Copy mame.exe into it (mametest/version/mame.exe)
|
||||
@rem 3. Open a command prompt to mametest/version
|
||||
@rem 4. Run "..\runtest"
|
||||
@rem 5. Wait for all the tests to complete
|
||||
@rem (Note one window will be opened for each CPU)
|
||||
@rem
|
||||
@rem How to generate a report:
|
||||
@rem
|
||||
@rem 1. Concatenate the summary files together:
|
||||
@rem copy summary*.log summary-final.log
|
||||
@rem 1. Open a command prompt to mametest.
|
||||
@rem 2. Make sure you have run tests for at least two
|
||||
@rem versions (mametest/ver1 and mametest/ver2)
|
||||
@rem 3. Create an output directory (mametest/report)
|
||||
@rem 4. Run "regrep report ver1\summary-final.log ver2\summary-final.log"
|
||||
@rem 5. Upload the report directory to mamedev.org :)
|
||||
@rem 6. Differing files are printed to stdout; redirect
|
||||
@rem to create a list that can be run again via
|
||||
@rem this script
|
||||
@rem ----------------------------------------------------
|
||||
|
||||
@echo off
|
||||
@setlocal ENABLEDELAYEDEXPANSION
|
||||
|
||||
@rem ----------------------------------------------------
|
||||
@rem We require mame.exe to be present
|
||||
@rem ----------------------------------------------------
|
||||
|
||||
if not exist mame.exe (
|
||||
@echo Missing mame.exe!
|
||||
@goto :eof
|
||||
)
|
||||
|
||||
@rem ----------------------------------------------------
|
||||
@rem By default we generate our own list; however, a list
|
||||
@rem can be specified by an alternate parameter. If a
|
||||
@rem parameter is given, we leave the existing log and
|
||||
@rem snap directories intact; otherwise, we delete them
|
||||
@rem and start fresh.
|
||||
@rem ----------------------------------------------------
|
||||
|
||||
set LIST=gamelist.txt
|
||||
set SUMMARY=summary.log
|
||||
if "%1"=="" (
|
||||
@echo Generating full list
|
||||
mame -ls >%LIST%
|
||||
@echo Deleting old data
|
||||
if exist log rmdir /s/q log
|
||||
if exist snap rmdir /s/q snap
|
||||
if exist %SUMMARY% del %SUMMARY%
|
||||
) else (
|
||||
set LIST=%1
|
||||
@echo Re-testing %1
|
||||
)
|
||||
|
||||
@rem ----------------------------------------------------
|
||||
@rem If we have a %2 parameter, then this is a sublaunch
|
||||
@rem and we should go right to the execution.
|
||||
@rem ----------------------------------------------------
|
||||
|
||||
if not "%2"=="" (
|
||||
set SUMMARY=summary%2.log
|
||||
goto :sublaunch
|
||||
)
|
||||
|
||||
@rem ----------------------------------------------------
|
||||
@rem Always delete all cfg, nvram, and diff files.
|
||||
@rem ----------------------------------------------------
|
||||
|
||||
if exist cfg rmdir /s/q cfg
|
||||
if exist nvram rmdir /s/q nvram
|
||||
if exist diff rmdir /s/q diff
|
||||
|
||||
@rem ----------------------------------------------------
|
||||
@rem Make sure we use transparent crosshairs.
|
||||
@rem ----------------------------------------------------
|
||||
|
||||
if not exist artwork mkdir artwork
|
||||
copy /y ..\cross.png artwork\cross0.png
|
||||
copy /y ..\cross.png artwork\cross1.png
|
||||
copy /y ..\cross.png artwork\cross2.png
|
||||
copy /y ..\cross.png artwork\cross3.png
|
||||
|
||||
@rem ----------------------------------------------------
|
||||
@rem If we don't yet have a summary.log, create a new one.
|
||||
@rem ----------------------------------------------------
|
||||
|
||||
if not exist %SUMMARY% (
|
||||
mame -help >%SUMMARY%
|
||||
echo @@@@@dir=%CD%>>%SUMMARY%
|
||||
)
|
||||
|
||||
@rem ----------------------------------------------------
|
||||
@rem Create the log directory and a starting timestamp.
|
||||
@rem ----------------------------------------------------
|
||||
|
||||
if not exist log mkdir log
|
||||
echo @@@@@start=%TIME%>>%SUMMARY%
|
||||
|
||||
@rem ----------------------------------------------------
|
||||
@rem Iterate over processors and sublaunch an entry for
|
||||
@rem each one.
|
||||
@rem ----------------------------------------------------
|
||||
|
||||
for /l %%c in (1,1,%NUMBER_OF_PROCESSORS%) do (
|
||||
set /a CPU="%%c - 1"
|
||||
@echo call %0 %LIST% !CPU!
|
||||
start %0 %LIST% !CPU!
|
||||
)
|
||||
goto :eof
|
||||
|
||||
|
||||
|
||||
@rem ----------------------------------------------------
|
||||
@rem Iterate over drivers in the log, extracting the
|
||||
@rem source filename as well, and passing both to runone.
|
||||
@rem ----------------------------------------------------
|
||||
|
||||
:sublaunch
|
||||
set CPU=0
|
||||
for /f "tokens=1-5 delims=/ " %%i in (%LIST%) do (
|
||||
set /a CPU="(!CPU! + 1) %% %NUMBER_OF_PROCESSORS%"
|
||||
if not "!CPU!"=="%2" (
|
||||
@rem do nothing
|
||||
) else if not "%%m"=="" (
|
||||
call :runone %%i %%m
|
||||
) else if not "%%l"=="" (
|
||||
call :runone %%i %%l
|
||||
) else if not "%%k"=="" (
|
||||
call :runone %%i %%k
|
||||
) else (
|
||||
call :runone %%i %%j
|
||||
)
|
||||
)
|
||||
|
||||
@rem ----------------------------------------------------
|
||||
@rem Add a final timestamp and we're done.
|
||||
@rem ----------------------------------------------------
|
||||
|
||||
echo @@@@@stop=%TIME%>>%SUMMARY%
|
||||
goto :eof
|
||||
|
||||
|
||||
|
||||
@rem ----------------------------------------------------
|
||||
@rem runone: Execute a single game for 30 seconds and
|
||||
@rem output the results to the summary.log.
|
||||
@rem ----------------------------------------------------
|
||||
|
||||
:runone
|
||||
@echo Testing %1 (%2)...
|
||||
echo.>>%SUMMARY%
|
||||
mame %1 -str 30 -watchdog 300 -nodebug -nothrottle -inipath .. -video none -sound none 1>log\%1.txt 2>log\%1.err
|
||||
if %errorlevel% equ 100 (
|
||||
echo @@@@@driver=%1: Exception>>%SUMMARY%
|
||||
type log\%1.err >>%SUMMARY%
|
||||
) else if %errorlevel% equ 5 (
|
||||
@rem Do nothing -- game does not exist in this build
|
||||
) else if %errorlevel% equ 3 (
|
||||
echo @@@@@driver=%1: Fatal error>>%SUMMARY%
|
||||
type log\%1.err >>%SUMMARY%
|
||||
) else if %errorlevel% equ 2 (
|
||||
echo @@@@@driver=%1: Missing files>>%SUMMARY%
|
||||
type log\%1.err >>%SUMMARY%
|
||||
) else if %errorlevel% equ 1 (
|
||||
echo @@@@@driver=%1: Failed validity check>>%SUMMARY%
|
||||
type log\%1.err >>%SUMMARY%
|
||||
) else if %errorlevel% equ 0 (
|
||||
echo @@@@@driver=%1: Success>>%SUMMARY%
|
||||
) else (
|
||||
echo @@@@@driver=%1: Unknown error %errorlevel%>>%SUMMARY%
|
||||
)
|
||||
echo @@@@@source=%2>>%SUMMARY%
|
||||
goto :eof
|
@ -1,428 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Aaron Giles
|
||||
/***************************************************************************
|
||||
|
||||
split.c
|
||||
|
||||
Simple file splitter/joiner with hashes.
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "corefile.h"
|
||||
#include "corestr.h"
|
||||
#include "hashing.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <cassert>
|
||||
|
||||
#define DEFAULT_SPLIT_SIZE 100
|
||||
#define MAX_PARTS 1000
|
||||
#define SHA1_DIGEST_SIZE 20
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
CORE IMPLEMENTATION
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
compute_hash_as_string - compute an SHA1
|
||||
hash over a buffer and return a string
|
||||
-------------------------------------------------*/
|
||||
|
||||
static void compute_hash_as_string(std::string &buffer, void *data, uint32_t length)
|
||||
{
|
||||
// compute the SHA1
|
||||
util::sha1_creator sha1;
|
||||
sha1.append(data, length);
|
||||
const util::sha1_t sha1digest = sha1.finish();
|
||||
|
||||
// expand the digest to a string
|
||||
char expanded[sizeof(sha1digest.m_raw) * 2];
|
||||
for (int ch = 0; ch < sizeof(sha1digest.m_raw); ch++)
|
||||
{
|
||||
expanded[ch * 2 + 0] = "0123456789ABCDEF"[sha1digest.m_raw[ch] >> 4];
|
||||
expanded[ch * 2 + 1] = "0123456789ABCDEF"[sha1digest.m_raw[ch] & 15];
|
||||
}
|
||||
|
||||
// copy it to the buffer
|
||||
buffer.assign(expanded, sizeof(expanded));
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
split_file - split a file into multiple parts
|
||||
-------------------------------------------------*/
|
||||
|
||||
static int split_file(const char *filename, const char *basename, uint32_t splitsize)
|
||||
{
|
||||
std::string outfilename, basefilename, splitfilename;
|
||||
util::core_file::ptr outfile, infile, splitfile;
|
||||
std::string computedhash;
|
||||
void *splitbuffer = nullptr;
|
||||
int index, partnum;
|
||||
uint64_t totallength;
|
||||
std::error_condition filerr;
|
||||
int error = 1;
|
||||
|
||||
// convert split size to MB
|
||||
if (splitsize > 500)
|
||||
{
|
||||
fprintf(stderr, "Fatal error: maximum split size is 500MB (even that is way huge!)\n");
|
||||
goto cleanup;
|
||||
}
|
||||
splitsize *= 1024 * 1024;
|
||||
|
||||
// open the file for read
|
||||
filerr = util::core_file::open(filename, OPEN_FLAG_READ, infile);
|
||||
if (filerr)
|
||||
{
|
||||
fprintf(stderr, "Fatal error: unable to open file '%s'\n", filename);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// get the total length
|
||||
if (infile->length(totallength))
|
||||
{
|
||||
fprintf(stderr, "Fatal error: unable to get length of file\n");
|
||||
goto cleanup;
|
||||
}
|
||||
if (totallength < splitsize)
|
||||
{
|
||||
fprintf(stderr, "Fatal error: file is smaller than the split size\n");
|
||||
goto cleanup;
|
||||
}
|
||||
if ((uint64_t(splitsize) * MAX_PARTS) < totallength)
|
||||
{
|
||||
fprintf(stderr, "Fatal error: too many splits (maximum is %d)\n", MAX_PARTS);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// allocate a buffer for reading
|
||||
splitbuffer = malloc(splitsize);
|
||||
if (splitbuffer == nullptr)
|
||||
{
|
||||
fprintf(stderr, "Fatal error: unable to allocate memory for the split\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// find the base name of the file
|
||||
basefilename.assign(basename);
|
||||
index = basefilename.find_last_of(PATH_SEPARATOR[0]);
|
||||
if (index != -1)
|
||||
basefilename.erase(0, index + 1);
|
||||
|
||||
// compute the split filename
|
||||
splitfilename.assign(basename).append(".split");
|
||||
|
||||
// create the split file
|
||||
filerr = util::core_file::open(splitfilename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_NO_BOM, splitfile);
|
||||
if (filerr)
|
||||
{
|
||||
fprintf(stderr, "Fatal error: unable to create split file '%s'\n", splitfilename.c_str());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// write the basics out
|
||||
splitfile->printf("splitfile=%s\n", basefilename.c_str());
|
||||
splitfile->printf("splitsize=%d\n", splitsize);
|
||||
|
||||
printf("Split file is '%s'\n", splitfilename.c_str());
|
||||
printf("Splitting file %s into chunks of %dMB...\n", basefilename.c_str(), splitsize / (1024 * 1024));
|
||||
|
||||
// now iterate until done
|
||||
for (partnum = 0; partnum < 1000; partnum++)
|
||||
{
|
||||
printf("Reading part %d...", partnum);
|
||||
|
||||
// read as much as we can from the file
|
||||
size_t length;
|
||||
infile->read(splitbuffer, splitsize, length); // FIXME check error return
|
||||
if (length == 0)
|
||||
break;
|
||||
|
||||
// hash what we have
|
||||
compute_hash_as_string(computedhash, splitbuffer, length);
|
||||
|
||||
// write that info to the split file
|
||||
splitfile->printf("hash=%s file=%s.%03d\n", computedhash.c_str(), basefilename.c_str(), partnum);
|
||||
|
||||
// compute the full filename for this guy
|
||||
outfilename = util::string_format("%s.%03d", basename, partnum);
|
||||
|
||||
// create it
|
||||
filerr = util::core_file::open(outfilename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, outfile);
|
||||
if (filerr)
|
||||
{
|
||||
printf("\n");
|
||||
fprintf(stderr, "Fatal error: unable to create output file '%s'\n", outfilename.c_str());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
printf(" writing %s.%03d...", basefilename.c_str(), partnum);
|
||||
|
||||
// write the data
|
||||
size_t actual;
|
||||
filerr = outfile->write(splitbuffer, length, actual);
|
||||
if (filerr || (actual != length) || outfile->flush())
|
||||
{
|
||||
printf("\n");
|
||||
fprintf(stderr, "Fatal error: Error writing output file (out of space?)\n");
|
||||
goto cleanup;
|
||||
}
|
||||
outfile.reset();
|
||||
|
||||
printf(" done\n");
|
||||
|
||||
// stop if this is the end
|
||||
if (length < splitsize)
|
||||
break;
|
||||
}
|
||||
printf("File split successfully\n");
|
||||
|
||||
// if we get here, clear the errors
|
||||
error = 0;
|
||||
|
||||
cleanup:
|
||||
if (splitfile)
|
||||
{
|
||||
splitfile.reset();
|
||||
if (error != 0)
|
||||
remove(splitfilename.c_str());
|
||||
}
|
||||
if (infile)
|
||||
infile.reset();
|
||||
if (outfile)
|
||||
{
|
||||
outfile.reset();
|
||||
if (error != 0)
|
||||
remove(outfilename.c_str());
|
||||
}
|
||||
if (splitbuffer != nullptr)
|
||||
free(splitbuffer);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
join_file - rejoin a file from its split
|
||||
parts
|
||||
-------------------------------------------------*/
|
||||
|
||||
static int join_file(const char *filename, const char *outname, int write_output)
|
||||
{
|
||||
std::string expectedhash, computedhash;
|
||||
std::string outfilename, infilename;
|
||||
std::string basepath;
|
||||
util::core_file::ptr outfile, infile, splitfile;
|
||||
void *splitbuffer = nullptr;
|
||||
std::error_condition filerr;
|
||||
uint32_t splitsize;
|
||||
char buffer[256];
|
||||
int error = 1;
|
||||
int index;
|
||||
|
||||
// open the file for read
|
||||
filerr = util::core_file::open(filename, OPEN_FLAG_READ, splitfile);
|
||||
if (filerr)
|
||||
{
|
||||
fprintf(stderr, "Fatal error: unable to open file '%s'\n", filename);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// read the first line and verify this is a split file
|
||||
if (!splitfile->gets(buffer, sizeof(buffer)) || strncmp(buffer, "splitfile=", 10) != 0)
|
||||
{
|
||||
fprintf(stderr, "Fatal error: corrupt or incomplete split file at line:\n%s\n", buffer);
|
||||
goto cleanup;
|
||||
}
|
||||
outfilename.assign(strtrimspace(buffer + 10));
|
||||
|
||||
// compute the base path
|
||||
basepath.assign(filename);
|
||||
index = basepath.find_last_of(PATH_SEPARATOR[0]);
|
||||
if (index != -1)
|
||||
basepath.erase(index + 1);
|
||||
else
|
||||
basepath.clear();
|
||||
|
||||
// override the output filename if specified, otherwise prepend the path
|
||||
if (outname != nullptr)
|
||||
outfilename.assign(outname);
|
||||
else
|
||||
outfilename.insert(0, basepath);
|
||||
|
||||
// read the split size
|
||||
if (!splitfile->gets(buffer, sizeof(buffer)) || sscanf(buffer, "splitsize=%d", &splitsize) != 1)
|
||||
{
|
||||
fprintf(stderr, "Fatal error: corrupt or incomplete split file at line:\n%s\n", buffer);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// attempt to open the new file
|
||||
if (write_output)
|
||||
{
|
||||
// don't overwrite the original!
|
||||
filerr = util::core_file::open(outfilename, OPEN_FLAG_READ, outfile);
|
||||
if (!filerr)
|
||||
{
|
||||
outfile.reset();
|
||||
fprintf(stderr, "Fatal error: output file '%s' already exists\n", outfilename.c_str());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// open the output for write
|
||||
filerr = util::core_file::open(outfilename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, outfile);
|
||||
if (filerr)
|
||||
{
|
||||
fprintf(stderr, "Fatal error: unable to create file '%s'\n", outfilename.c_str());
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%s file '%s'...\n", write_output ? "Joining" : "Verifying", outfilename.c_str());
|
||||
|
||||
// now iterate through each file
|
||||
while (splitfile->gets(buffer, sizeof(buffer)))
|
||||
{
|
||||
// make sure the hash and filename are in the right place
|
||||
if (strncmp(buffer, "hash=", 5) != 0 || strncmp(buffer + 5 + SHA1_DIGEST_SIZE * 2, " file=", 6) != 0)
|
||||
{
|
||||
fprintf(stderr, "Fatal error: corrupt or incomplete split file at line:\n%s\n", buffer);
|
||||
goto cleanup;
|
||||
}
|
||||
expectedhash.assign(buffer + 5, SHA1_DIGEST_SIZE * 2);
|
||||
infilename.assign(strtrimspace(buffer + 5 + SHA1_DIGEST_SIZE * 2 + 6));
|
||||
|
||||
printf(" Reading file '%s'...", infilename.c_str());
|
||||
|
||||
// read the file's contents
|
||||
infilename.insert(0, basepath);
|
||||
uint32_t length;
|
||||
filerr = util::core_file::load(infilename.c_str(), &splitbuffer, length);
|
||||
if (filerr)
|
||||
{
|
||||
printf("\n");
|
||||
fprintf(stderr, "Fatal error: unable to load file '%s'\n", infilename.c_str());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// hash the contents
|
||||
compute_hash_as_string(computedhash, splitbuffer, length);
|
||||
|
||||
// compare
|
||||
if (computedhash.compare(expectedhash)!=0)
|
||||
{
|
||||
printf("\n");
|
||||
fprintf(stderr, "Fatal error: file '%s' has incorrect hash\n Expected: %s\n Computed: %s\n", infilename.c_str(), expectedhash.c_str(), computedhash.c_str());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// append to the file
|
||||
if (write_output)
|
||||
{
|
||||
printf(" writing...");
|
||||
|
||||
size_t actual;
|
||||
filerr = outfile->write(splitbuffer, length, actual);
|
||||
if (filerr || (actual != length) || outfile->flush())
|
||||
{
|
||||
printf("\n");
|
||||
fprintf(stderr, "Fatal error: Error writing output file (out of space?)\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
printf(" done\n");
|
||||
}
|
||||
else
|
||||
printf(" verified\n");
|
||||
|
||||
// release allocated memory
|
||||
free(splitbuffer);
|
||||
splitbuffer = nullptr;
|
||||
}
|
||||
if (write_output)
|
||||
printf("File re-created successfully\n");
|
||||
else
|
||||
printf("File verified successfully\n");
|
||||
|
||||
// if we get here, clear the errors
|
||||
error = 0;
|
||||
|
||||
cleanup:
|
||||
if (splitfile)
|
||||
splitfile.reset();
|
||||
if (infile)
|
||||
infile.reset();
|
||||
if (outfile)
|
||||
{
|
||||
outfile.reset();
|
||||
if (error != 0)
|
||||
remove(outfilename.c_str());
|
||||
}
|
||||
if (splitbuffer != nullptr)
|
||||
free(splitbuffer);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
main - primary entry point
|
||||
-------------------------------------------------*/
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int result;
|
||||
|
||||
/* skip if no command */
|
||||
if (argc < 2)
|
||||
goto usage;
|
||||
|
||||
/* split command */
|
||||
if (core_stricmp(argv[1], "-split") == 0)
|
||||
{
|
||||
if (argc != 4 && argc != 5)
|
||||
goto usage;
|
||||
result = split_file(argv[2], argv[3], (argc < 5) ? DEFAULT_SPLIT_SIZE : atoi(argv[4]));
|
||||
}
|
||||
|
||||
/* join command */
|
||||
else if (core_stricmp(argv[1], "-join") == 0)
|
||||
{
|
||||
if (argc != 3 && argc != 4)
|
||||
goto usage;
|
||||
result = join_file(argv[2], (argc >= 4) ? argv[3] : nullptr, true);
|
||||
}
|
||||
|
||||
/* verify command */
|
||||
else if (core_stricmp(argv[1], "-verify") == 0)
|
||||
{
|
||||
if (argc != 3)
|
||||
goto usage;
|
||||
result = join_file(argv[2], nullptr, false);
|
||||
}
|
||||
else
|
||||
goto usage;
|
||||
|
||||
return result;
|
||||
|
||||
usage:
|
||||
fprintf(stderr,
|
||||
"Usage:\n"
|
||||
" split -split <bigfile> <basename> [<size>] -- split file into parts\n"
|
||||
" split -join <splitfile> [<outputfile>] -- join file parts into original file\n"
|
||||
" split -verify <splitfile> -- verify a split file\n"
|
||||
"\n"
|
||||
"Where:\n"
|
||||
" <bigfile> is the large file you wish to split\n"
|
||||
" <basename> is the base path and name to assign to the split files\n"
|
||||
" <size> is the optional split size, in MB (100MB is the default)\n"
|
||||
" <splitfile> is the name of the <basename>.split generated with -split\n"
|
||||
" <outputfile> is the name of the file to output (defaults to original name)\n"
|
||||
);
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,346 +0,0 @@
|
||||
//============================================================
|
||||
//
|
||||
// testkeys.cpp - A small utility to analyze SDL keycodes
|
||||
//
|
||||
// Copyright (c) 1996-2021, Nicola Salmoria and the MAME Team.
|
||||
// Visit https://mamedev.org for licensing and usage restrictions.
|
||||
//
|
||||
// SDLMAME by Olivier Galibert and R. Belmont
|
||||
// testkeys by couriersud
|
||||
//
|
||||
//============================================================
|
||||
|
||||
#include "osdcore.h"
|
||||
|
||||
#include "SDL2/SDL.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
//#include "unicode.h"
|
||||
|
||||
|
||||
struct key_lookup_table { int code; const char *name; };
|
||||
|
||||
#define KE(x) { SDL_SCANCODE_##x, "SDL_SCANCODE_" #x },
|
||||
|
||||
static constexpr key_lookup_table sdl_lookup[] =
|
||||
{
|
||||
KE(UNKNOWN)
|
||||
|
||||
KE(A)
|
||||
KE(B)
|
||||
KE(C)
|
||||
KE(D)
|
||||
KE(E)
|
||||
KE(F)
|
||||
KE(G)
|
||||
KE(H)
|
||||
KE(I)
|
||||
KE(J)
|
||||
KE(K)
|
||||
KE(L)
|
||||
KE(M)
|
||||
KE(N)
|
||||
KE(O)
|
||||
KE(P)
|
||||
KE(Q)
|
||||
KE(R)
|
||||
KE(S)
|
||||
KE(T)
|
||||
KE(U)
|
||||
KE(V)
|
||||
KE(W)
|
||||
KE(X)
|
||||
KE(Y)
|
||||
KE(Z)
|
||||
|
||||
KE(1)
|
||||
KE(2)
|
||||
KE(3)
|
||||
KE(4)
|
||||
KE(5)
|
||||
KE(6)
|
||||
KE(7)
|
||||
KE(8)
|
||||
KE(9)
|
||||
KE(0)
|
||||
|
||||
KE(RETURN)
|
||||
KE(ESCAPE)
|
||||
KE(BACKSPACE)
|
||||
KE(TAB)
|
||||
KE(SPACE)
|
||||
|
||||
KE(MINUS)
|
||||
KE(EQUALS)
|
||||
KE(LEFTBRACKET)
|
||||
KE(RIGHTBRACKET)
|
||||
KE(BACKSLASH)
|
||||
KE(NONUSHASH)
|
||||
KE(SEMICOLON)
|
||||
KE(APOSTROPHE)
|
||||
KE(GRAVE)
|
||||
KE(COMMA)
|
||||
KE(PERIOD)
|
||||
KE(SLASH)
|
||||
|
||||
KE(CAPSLOCK)
|
||||
|
||||
KE(F1)
|
||||
KE(F2)
|
||||
KE(F3)
|
||||
KE(F4)
|
||||
KE(F5)
|
||||
KE(F6)
|
||||
KE(F7)
|
||||
KE(F8)
|
||||
KE(F9)
|
||||
KE(F10)
|
||||
KE(F11)
|
||||
KE(F12)
|
||||
|
||||
KE(PRINTSCREEN)
|
||||
KE(SCROLLLOCK)
|
||||
KE(PAUSE)
|
||||
KE(INSERT)
|
||||
KE(HOME)
|
||||
KE(PAGEUP)
|
||||
KE(DELETE)
|
||||
KE(END)
|
||||
KE(PAGEDOWN)
|
||||
KE(RIGHT)
|
||||
KE(LEFT)
|
||||
KE(DOWN)
|
||||
KE(UP)
|
||||
|
||||
KE(NUMLOCKCLEAR)
|
||||
KE(KP_DIVIDE)
|
||||
KE(KP_MULTIPLY)
|
||||
KE(KP_MINUS)
|
||||
KE(KP_PLUS)
|
||||
KE(KP_ENTER)
|
||||
KE(KP_1)
|
||||
KE(KP_2)
|
||||
KE(KP_3)
|
||||
KE(KP_4)
|
||||
KE(KP_5)
|
||||
KE(KP_6)
|
||||
KE(KP_7)
|
||||
KE(KP_8)
|
||||
KE(KP_9)
|
||||
KE(KP_0)
|
||||
KE(KP_PERIOD)
|
||||
|
||||
KE(NONUSBACKSLASH)
|
||||
KE(APPLICATION)
|
||||
KE(POWER)
|
||||
KE(KP_EQUALS)
|
||||
KE(F13)
|
||||
KE(F14)
|
||||
KE(F15)
|
||||
KE(F16)
|
||||
KE(F17)
|
||||
KE(F18)
|
||||
KE(F19)
|
||||
KE(F20)
|
||||
KE(F21)
|
||||
KE(F22)
|
||||
KE(F23)
|
||||
KE(F24)
|
||||
KE(EXECUTE)
|
||||
KE(HELP)
|
||||
KE(MENU)
|
||||
KE(SELECT)
|
||||
KE(STOP)
|
||||
KE(AGAIN)
|
||||
KE(UNDO)
|
||||
KE(CUT)
|
||||
KE(COPY)
|
||||
KE(PASTE)
|
||||
KE(FIND)
|
||||
KE(MUTE)
|
||||
KE(VOLUMEUP)
|
||||
KE(VOLUMEDOWN)
|
||||
KE(KP_COMMA)
|
||||
KE(KP_EQUALSAS400)
|
||||
|
||||
KE(INTERNATIONAL1)
|
||||
KE(INTERNATIONAL2)
|
||||
KE(INTERNATIONAL3)
|
||||
KE(INTERNATIONAL4)
|
||||
KE(INTERNATIONAL5)
|
||||
KE(INTERNATIONAL6)
|
||||
KE(INTERNATIONAL7)
|
||||
KE(INTERNATIONAL8)
|
||||
KE(INTERNATIONAL9)
|
||||
KE(LANG1)
|
||||
KE(LANG2)
|
||||
KE(LANG3)
|
||||
KE(LANG4)
|
||||
KE(LANG5)
|
||||
KE(LANG6)
|
||||
KE(LANG7)
|
||||
KE(LANG8)
|
||||
KE(LANG9)
|
||||
|
||||
KE(ALTERASE)
|
||||
KE(SYSREQ)
|
||||
KE(CANCEL)
|
||||
KE(CLEAR)
|
||||
KE(PRIOR)
|
||||
KE(RETURN2)
|
||||
KE(SEPARATOR)
|
||||
KE(OUT)
|
||||
KE(OPER)
|
||||
KE(CLEARAGAIN)
|
||||
KE(CRSEL)
|
||||
KE(EXSEL)
|
||||
|
||||
KE(KP_00)
|
||||
KE(KP_000)
|
||||
KE(THOUSANDSSEPARATOR)
|
||||
KE(DECIMALSEPARATOR)
|
||||
KE(CURRENCYUNIT)
|
||||
KE(CURRENCYSUBUNIT)
|
||||
KE(KP_LEFTPAREN)
|
||||
KE(KP_RIGHTPAREN)
|
||||
KE(KP_LEFTBRACE)
|
||||
KE(KP_RIGHTBRACE)
|
||||
KE(KP_TAB)
|
||||
KE(KP_BACKSPACE)
|
||||
KE(KP_A)
|
||||
KE(KP_B)
|
||||
KE(KP_C)
|
||||
KE(KP_D)
|
||||
KE(KP_E)
|
||||
KE(KP_F)
|
||||
KE(KP_XOR)
|
||||
KE(KP_POWER)
|
||||
KE(KP_PERCENT)
|
||||
KE(KP_LESS)
|
||||
KE(KP_GREATER)
|
||||
KE(KP_AMPERSAND)
|
||||
KE(KP_DBLAMPERSAND)
|
||||
KE(KP_VERTICALBAR)
|
||||
KE(KP_DBLVERTICALBAR)
|
||||
KE(KP_COLON)
|
||||
KE(KP_HASH)
|
||||
KE(KP_SPACE)
|
||||
KE(KP_AT)
|
||||
KE(KP_EXCLAM)
|
||||
KE(KP_MEMSTORE)
|
||||
KE(KP_MEMRECALL)
|
||||
KE(KP_MEMCLEAR)
|
||||
KE(KP_MEMADD)
|
||||
KE(KP_MEMSUBTRACT)
|
||||
KE(KP_MEMMULTIPLY)
|
||||
KE(KP_MEMDIVIDE)
|
||||
KE(KP_PLUSMINUS)
|
||||
KE(KP_CLEAR)
|
||||
KE(KP_CLEARENTRY)
|
||||
KE(KP_BINARY)
|
||||
KE(KP_OCTAL)
|
||||
KE(KP_DECIMAL)
|
||||
KE(KP_HEXADECIMAL)
|
||||
|
||||
KE(LCTRL)
|
||||
KE(LSHIFT)
|
||||
KE(LALT)
|
||||
KE(LGUI)
|
||||
KE(RCTRL)
|
||||
KE(RSHIFT)
|
||||
KE(RALT)
|
||||
KE(RGUI)
|
||||
|
||||
KE(MODE)
|
||||
KE(AUDIONEXT)
|
||||
KE(AUDIOPREV)
|
||||
KE(AUDIOSTOP)
|
||||
KE(AUDIOPLAY)
|
||||
KE(AUDIOMUTE)
|
||||
KE(MEDIASELECT)
|
||||
KE(WWW)
|
||||
KE(MAIL)
|
||||
KE(CALCULATOR)
|
||||
KE(COMPUTER)
|
||||
KE(AC_SEARCH)
|
||||
KE(AC_HOME)
|
||||
KE(AC_BACK)
|
||||
KE(AC_FORWARD)
|
||||
KE(AC_STOP)
|
||||
KE(AC_REFRESH)
|
||||
KE(AC_BOOKMARKS)
|
||||
|
||||
KE(BRIGHTNESSDOWN)
|
||||
KE(BRIGHTNESSUP)
|
||||
KE(DISPLAYSWITCH)
|
||||
KE(KBDILLUMTOGGLE)
|
||||
KE(KBDILLUMDOWN)
|
||||
KE(KBDILLUMUP)
|
||||
KE(EJECT)
|
||||
KE(SLEEP)
|
||||
|
||||
KE(APP1)
|
||||
KE(APP2)
|
||||
};
|
||||
|
||||
static char const *lookup_key_name(int kc)
|
||||
{
|
||||
for (key_lookup_table const &k : sdl_lookup)
|
||||
{
|
||||
if (k.code == kc)
|
||||
return k.name;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
SDL_CreateWindow("Input Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 100, 100, 0);
|
||||
|
||||
SDL_Event event;
|
||||
bool quit = false;
|
||||
std::string lasttext;
|
||||
while (SDL_PollEvent(&event) || !quit) {
|
||||
switch(event.type) {
|
||||
case SDL_QUIT:
|
||||
quit = true;
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
if (event.key.keysym.sym == SDLK_ESCAPE) {
|
||||
quit = true;
|
||||
} else {
|
||||
std::cout
|
||||
<< "ITEM_ID_XY "
|
||||
<< lookup_key_name(event.key.keysym.scancode)
|
||||
<< ' '
|
||||
<< std::endl;
|
||||
lasttext.clear();
|
||||
}
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
std::cout
|
||||
<< "ITEM_ID_XY "
|
||||
<< lookup_key_name(event.key.keysym.scancode)
|
||||
<< ' '
|
||||
<< lasttext
|
||||
<< std::endl;
|
||||
break;
|
||||
case SDL_TEXTINPUT:
|
||||
lasttext = event.text.text;
|
||||
break;
|
||||
}
|
||||
event.type = 0;
|
||||
|
||||
#ifdef SDLMAME_OS2
|
||||
SDL_Delay(10);
|
||||
#endif
|
||||
}
|
||||
SDL_Quit();
|
||||
return(0);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,8 @@
|
||||
#!/bin/bash
|
||||
mkdir 3rdparty
|
||||
rsync -r --delete ../mame/3rdparty/{expat,libflac,lzma,nanosvg,utf8proc,zlib} 3rdparty/
|
||||
rsync -r --delete ../mame/src/{emu,frontend,lib,tools} src/
|
||||
rsync -r --delete ../mame/src/{emu,frontend,lib} src/
|
||||
rsync -r --delete --exclude="modules/lib/osd_getenv.cpp" ../mame/src/osd src/
|
||||
mkdir src/tools/
|
||||
rsync ../mame/src/tools/chdman.cpp src/tools/
|
||||
|
Loading…
Reference in New Issue
Block a user