Copy only chdman.cpp from tools.

This commit is contained in:
CharlesThobe 2022-01-04 05:45:25 +02:00
parent 0ee0c559b3
commit 64883c012c
74 changed files with 5 additions and 65836 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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)

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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, &sector_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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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 */

View File

@ -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];
}

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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, &sector, &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, &sector, &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, &sector);
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, &sector);
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, &sector);
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, &sector_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;
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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;
}
}

View File

@ -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';
}

View File

@ -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

View File

@ -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, &sectors);
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, &sector);
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, &sector);
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;
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 &sector)
{
imgtoolerr_t err;
rt11_diskinfo *di = get_rt11_info(image);
uint32_t tracks, heads, sectors;
err = image.get_geometry(&tracks, &heads, &sectors);
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, &sector_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

View File

@ -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, &sector);
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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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/