// license:BSD-3-Clause
// copyright-holders:Nathan Woods
/***************************************************************************
imgtool.cpp
Core code for Imgtool
***************************************************************************/
#include <string.h>
#include <ctype.h>
#include <iostream>
#include "imgtool.h"
#include "formats/imageutl.h"
#include "library.h"
#include "modules.h"
#include "pool.h"
/***************************************************************************
GLOBALS
***************************************************************************/
static std::unique_ptr<imgtool::library> global_imgtool_library;
static int global_omit_untested;
static void (*global_warn)(const char *message);
void CLIB_DECL ATTR_PRINTF(1,2) logerror(const char *format, ...)
{
va_list arg;
va_start(arg, format);
vprintf(format, arg);
va_end(arg);
}
/***************************************************************************
Imgtool initialization and basics
***************************************************************************/
//-------------------------------------------------
// rtrim
//-------------------------------------------------
void rtrim(char *buf)
{
size_t buflen;
char *s;
buflen = strlen(buf);
if (buflen)
{
for (s = &buf[buflen-1]; s >= buf && (*s >= '\0') && isspace(*s); s--)
*s = '\0';
}
}
//-------------------------------------------------
// strncpyz
//-------------------------------------------------
char *strncpyz(char *dest, const char *source, size_t len)
{
char *s;
if (len) {
s = strncpy(dest, source, len - 1);
dest[len-1] = '\0';
}
else {
s = dest;
}
return s;
}
//-------------------------------------------------
// extract_padded_string
//-------------------------------------------------
static std::string extract_padded_string(const char *source, size_t len)
{
while ((len > 0) && (source[len - 1] == ' '))
len--;
return std::string(source, len);
}
//-------------------------------------------------
// extract_padded_filename - this is a common
// enough scenario that it is justified to have
// this in common code
//-------------------------------------------------
std::string extract_padded_filename(const char *source, size_t filename_length, size_t extension_length)
{
std::string filename = extract_padded_string(source, filename_length);
std::string extension = extract_padded_string(source + filename_length, extension_length);
return extension.empty() ? filename : filename + "." + extension;
}
//-------------------------------------------------
// markerrorsource - marks where an error source
//-------------------------------------------------
static imgtoolerr_t markerrorsource(imgtoolerr_t err)
{
switch(err)
{
case IMGTOOLERR_OUTOFMEMORY:
case IMGTOOLERR_UNEXPECTED:
case IMGTOOLERR_BUFFERTOOSMALL:
/* Do nothing */
break;
case IMGTOOLERR_FILENOTFOUND:
case IMGTOOLERR_BADFILENAME:
err = imgtoolerr_t(err | IMGTOOLERR_SRC_FILEONIMAGE);
break;
default:
err = imgtoolerr_t(err | IMGTOOLERR_SRC_IMAGEFILE);
break;
}
return err;
}
//-------------------------------------------------
// internal_error - debug function for raising
// internal errors
//-------------------------------------------------
static void internal_error(const imgtool_module *module, const char *message)
{
#ifdef MAME_DEBUG
printf("%s: %s\n", module->name, message);
#endif
}
//-------------------------------------------------
// imgtool_init - initializes the imgtool core
//-------------------------------------------------
void imgtool_init(bool omit_untested, void (*warn)(const char *message))
{
imgtoolerr_t err;
err = imgtool_create_cannonical_library(omit_untested, global_imgtool_library);
assert(err == IMGTOOLERR_SUCCESS);
if (err == IMGTOOLERR_SUCCESS)
{
global_imgtool_library->sort(imgtool::library::sort_type::DESCRIPTION);
}
global_omit_untested = omit_untested;
global_warn = warn;
}
//-------------------------------------------------
// imgtool_exit - closes out the imgtool core
//-------------------------------------------------
void imgtool_exit(void)
{
if (global_imgtool_library)
global_imgtool_library.reset();
global_warn = nullptr;
}
//-------------------------------------------------
// imgtool_find_module - looks up a module
//-------------------------------------------------
const imgtool_module *imgtool_find_module(const std::string &modulename)
{
return global_imgtool_library->findmodule(modulename);
}
//-------------------------------------------------
// imgtool_find_module - looks up a module
//-------------------------------------------------
const imgtool::library::modulelist &imgtool_get_modules()
{
return global_imgtool_library->modules();
}
//-------------------------------------------------
// imgtool_get_module_features - retrieves a
// structure identifying this module's features
// associated with an image
//-------------------------------------------------
imgtool_module_features imgtool_get_module_features(const imgtool_module *module)
{
imgtool_module_features features;
memset(&features, 0, sizeof(features));
if (module->create)
features.supports_create = 1;
if (module->open)
features.supports_open = 1;
if (module->read_sector)
features.supports_readsector = 1;
if (module->write_sector)
features.supports_writesector = 1;
return features;
}
//-------------------------------------------------
// imgtool_warn - issues a warning
//-------------------------------------------------
void imgtool_warn(const char *format, ...)
{
va_list va;
char buffer[2000];
if (global_warn)
{
va_start(va, format);
vsprintf(buffer, format, va);
va_end(va);
global_warn(buffer);
}
}
//-------------------------------------------------
// evaluate_module - evaluates a single file to
// determine what module can best handle a file
//-------------------------------------------------
static imgtoolerr_t evaluate_module(const char *fname, const imgtool_module *module, float &result)
{
imgtoolerr_t err;
imgtool::image::ptr image;
imgtool::partition::ptr partition;
imgtool::directory::ptr imageenum;
imgtool_dirent ent;
float current_result;
result = 0.0;
err = imgtool::image::open(module, fname, OSD_FOPEN_READ, image);
if (err)
goto done;
if (image)
{
current_result = module->open_is_strict ? 0.9 : 0.5;
err = imgtool::partition::open(*image, 0, partition);
if (err)
goto done;
err = imgtool::directory::open(*partition, "", imageenum);
if (err)
goto done;
memset(&ent, 0, sizeof(ent));
do
{
err = imageenum->get_next(ent);
if (err)
goto done;
if (ent.corrupt)
current_result = (current_result * 99 + 1.00f) / 100;
else
current_result = (current_result + 1.00f) / 2;
}
while(!ent.eof);
result = current_result;
}
done:
if (ERRORCODE(err) == IMGTOOLERR_CORRUPTIMAGE)
err = IMGTOOLERR_SUCCESS;
return err;
}
//-------------------------------------------------
// identify_file - attempts to determine the module
// for any given image
//-------------------------------------------------
imgtoolerr_t imgtool::image::identify_file(const char *fname, imgtool_module **modules, size_t count)
{
imgtoolerr_t err = IMGTOOLERR_SUCCESS;
imgtool::library &library = *global_imgtool_library.get();
imgtool_module *insert_module;
imgtool_module *temp_module;
size_t i = 0;
const char *extension;
float val, temp_val;
std::unique_ptr<float[]> values;
if (count <= 0)
return IMGTOOLERR_UNEXPECTED;
for (i = 0; i < count; i++)
modules[i] = nullptr;
if (count > 1)
count--; /* null terminate */
try { values = std::make_unique<float[]>(count); }
catch (std::bad_alloc const &) { return IMGTOOLERR_OUTOFMEMORY; }
for (i = 0; i < count; i++)
values[i] = 0.0;
/* figure out the file extension, if any */
extension = strrchr(fname, '.');
if (extension)
extension++;
/* iterate through all modules */
for (const auto &module : library.modules())
{
if (!extension || image_find_extension(module->extensions, extension))
{
err = evaluate_module(fname, module.get(), val);
if (err)
return err;
insert_module = module.get();
for (i = 0; (val > 0.0f) && (i < count); i++)
{
if (val > values[i])
{
temp_val = values[i];
temp_module = modules[i];
values[i] = val;
modules[i] = insert_module;
val = temp_val;
insert_module = temp_module;
}
}
}
}
if (!modules[0])
return imgtoolerr_t(IMGTOOLERR_MODULENOTFOUND | IMGTOOLERR_SRC_IMAGEFILE);
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// get_geometry - gets the geometry
// of an image; note that this may disagree with
// particular sectors; this is a common copy
// protection scheme
//-------------------------------------------------
imgtoolerr_t imgtool::image::get_geometry(uint32_t *tracks, uint32_t *heads, uint32_t *sectors)
{
uint32_t dummy;
/* some sanitization, to make the callbacks easier to implement */
if (!tracks)
tracks = &dummy;
if (!heads)
heads = &dummy;
if (!sectors)
sectors = &dummy;
*tracks = 0;
*heads = 0;
*sectors = 0;
/* implemented? */
if (!module().get_geometry)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
return module().get_geometry(*this, tracks, heads, sectors);
}
//-------------------------------------------------
// read_sector - reads a sector on an image
//-------------------------------------------------
imgtoolerr_t imgtool::image::read_sector(uint32_t track, uint32_t head,
uint32_t sector, std::vector<uint8_t> &buffer)
{
// implemented?
if (!module().read_sector)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
return module().read_sector(*this, track, head, sector, buffer);
}
//-------------------------------------------------
// write_sector - writes a sector on an image
//-------------------------------------------------
imgtoolerr_t imgtool::image::write_sector(uint32_t track, uint32_t head,
uint32_t sector, const void *buffer, size_t len)
{
// implemented?
if (!module().write_sector)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
return module().write_sector(*this, track, head, sector, buffer, len);
}
//-------------------------------------------------
// get_block_size - gets the size of a standard
// block on an image
//-------------------------------------------------
imgtoolerr_t imgtool::image::get_block_size(uint32_t &length)
{
// implemented?
if (module().block_size == 0)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
length = module().block_size;
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// read_block - reads a standard block on an image
//-------------------------------------------------
imgtoolerr_t imgtool::image::read_block(uint64_t block, void *buffer)
{
// implemented?
if (!module().read_block)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
return module().read_block(*this, buffer, block);
}
//-------------------------------------------------
// write_block - writes a standard block on an image
//-------------------------------------------------
imgtoolerr_t imgtool::image::write_block(uint64_t block, const void *buffer)
{
// implemented?
if (!module().write_block)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
return module().write_block(*this, buffer, block);
}
//-------------------------------------------------
// clear_block - clears a standard block on an image
//-------------------------------------------------
imgtoolerr_t imgtool::image::clear_block(uint64_t block, uint8_t data)
{
imgtoolerr_t err;
uint8_t *block_data = nullptr;
uint32_t length;
err = get_block_size(length);
if (err)
goto done;
block_data = (uint8_t*)malloc(length);
if (!block_data)
{
err = (imgtoolerr_t)IMGTOOLERR_OUTOFMEMORY;
goto done;
}
memset(block_data, data, length);
err = write_block(block, block_data);
if (err)
goto done;
done:
if (block_data)
free(block_data);
return err;
}
//-------------------------------------------------
// list_partitions - lists the partitions on an image
//-------------------------------------------------
imgtoolerr_t imgtool::image::list_partitions(std::vector<imgtool::partition_info> &partitions)
{
imgtoolerr_t err;
// clear out partitions first
partitions.clear();
// implemented?
if (module().list_partitions)
{
// if so, call the module's callback
err = module().list_partitions(*this, partitions);
if (err)
return err;
}
else
{
// default implementation
partitions.emplace_back(module().imgclass, 0, ~0);
}
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// malloc - allocates memory associated with an image
//-------------------------------------------------
void *imgtool::image::malloc(size_t size)
{
return pool_malloc_lib(m_pool, size);
}
/***************************************************************************
Imgtool partition management
***************************************************************************/
//-------------------------------------------------
// imgtool::partition ctor
//-------------------------------------------------
imgtool::partition::partition(imgtool::image &image, const imgtool_class &imgclass, int partition_index, uint64_t base_block, uint64_t block_count)
: m_image(image)
, m_base_block(base_block)
, m_block_count(block_count)
, m_imgclass(imgclass)
{
// does this partition type have extra bytes?
size_t extra_bytes_size = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_PARTITION_EXTRA_BYTES);
if (extra_bytes_size > 0)
{
m_extra_bytes = std::make_unique<uint8_t[]>(extra_bytes_size);
memset(m_extra_bytes.get(), 0, sizeof(m_extra_bytes.get()[0]) * extra_bytes_size);
}
m_directory_extra_bytes = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES);
m_path_separator = (char)imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_PATH_SEPARATOR);
m_alternate_path_separator = (char)imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_ALTERNATE_PATH_SEPARATOR);
m_prefer_ucase = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_PREFER_UCASE) ? 1 : 0;
m_supports_creation_time = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME) ? 1 : 0;
m_supports_lastmodified_time = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME) ? 1 : 0;
m_supports_bootblock = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_SUPPORTS_BOOTBLOCK) ? 1 : 0;
m_begin_enum = (imgtoolerr_t(*)(imgtool::directory &, const char *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_BEGIN_ENUM);
m_next_enum = (imgtoolerr_t(*)(imgtool::directory &, imgtool_dirent &)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_NEXT_ENUM);
m_close_enum = (void(*)(imgtool::directory &)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_CLOSE_ENUM);
m_free_space = (imgtoolerr_t(*)(imgtool::partition &, uint64_t *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_FREE_SPACE);
m_read_file = (imgtoolerr_t(*)(imgtool::partition &, const char *, const char *, imgtool::stream &)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_READ_FILE);
m_write_file = (imgtoolerr_t(*)(imgtool::partition &, const char *, const char *, imgtool::stream &, util::option_resolution *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_WRITE_FILE);
m_delete_file = (imgtoolerr_t(*)(imgtool::partition &, const char *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_DELETE_FILE);
m_list_forks = (imgtoolerr_t(*)(imgtool::partition &, const char *, std::vector<imgtool::fork_entry> &forks)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_LIST_FORKS);
m_create_dir = (imgtoolerr_t(*)(imgtool::partition &, const char *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_CREATE_DIR);
m_delete_dir = (imgtoolerr_t(*)(imgtool::partition &, const char *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_DELETE_DIR);
m_list_attrs = (imgtoolerr_t(*)(imgtool::partition &, const char *, uint32_t *, size_t)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_LIST_ATTRS);
m_get_attrs = (imgtoolerr_t(*)(imgtool::partition &, const char *, const uint32_t *, imgtool_attribute *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_GET_ATTRS);
m_set_attrs = (imgtoolerr_t(*)(imgtool::partition &, const char *, const uint32_t *, const imgtool_attribute *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_SET_ATTRS);
m_attr_name = (imgtoolerr_t(*)(uint32_t, const imgtool_attribute *, char *, size_t)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_ATTR_NAME);
m_get_iconinfo = (imgtoolerr_t(*)(imgtool::partition &, const char *, imgtool_iconinfo *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_GET_ICON_INFO);
m_suggest_transfer = (imgtoolerr_t(*)(imgtool::partition &, const char *, imgtool_transfer_suggestion *, size_t)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_SUGGEST_TRANSFER);
m_get_chain = (imgtoolerr_t(*)(imgtool::partition &, const char *, imgtool_chainent *, size_t)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_GET_CHAIN);
m_writefile_optguide = (const util::option_guide *) imgtool_get_info_ptr(&imgclass, IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE);
const char *writefile_optspec = (const char *)imgtool_get_info_ptr(&imgclass, IMGTOOLINFO_STR_WRITEFILE_OPTSPEC);
if (writefile_optspec)
m_writefile_optspec.assign(writefile_optspec);
// mask out if writing is untested
if (global_omit_untested && imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_WRITING_UNTESTED))
{
m_write_file = nullptr;
m_delete_file = nullptr;
m_create_dir = nullptr;
m_delete_dir = nullptr;
m_writefile_optguide = nullptr;
m_writefile_optspec = nullptr;
}
}
//-------------------------------------------------
// imgtool::partition dtor
//-------------------------------------------------
imgtool::partition::~partition()
{
}
//-------------------------------------------------
// open - opens a partition
//-------------------------------------------------
imgtoolerr_t imgtool::partition::open(imgtool::image &image, int partition_index, imgtool::partition::ptr &partition)
{
imgtoolerr_t err = (imgtoolerr_t)IMGTOOLERR_SUCCESS;
imgtool::partition::ptr p;
std::vector<imgtool::partition_info> partitions;
imgtoolerr_t (*open_partition)(imgtool::partition &partition, uint64_t first_block, uint64_t block_count);
// list the partitions
err = image.list_partitions(partitions);
if (err)
return err;
// is this an invalid index?
if ((partition_index < 0) || (partition_index >= partitions.size()) || (!partitions[partition_index].imgclass().get_info && !partitions[partition_index].imgclass().derived_get_info))
return IMGTOOLERR_INVALIDPARTITION;
// use this partition
const imgtool_class &imgclass(partitions[partition_index].imgclass());
uint64_t base_block = partitions[partition_index].base_block();
uint64_t block_count = partitions[partition_index].block_count();
// allocate the new partition object
try { p = std::make_unique<imgtool::partition>(image, imgclass, partition_index, base_block, block_count); }
catch (std::bad_alloc const &)
{
err = (imgtoolerr_t)IMGTOOLERR_OUTOFMEMORY;
goto done;
}
// call the partition open function, if present
open_partition = (imgtoolerr_t (*)(imgtool::partition &, uint64_t, uint64_t)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_OPEN_PARTITION);
if (open_partition)
{
/* we have an open partition function */
err = (*open_partition)(*p, base_block, block_count);
if (err)
goto done;
}
done:
if (!err)
partition = std::move(p);
else
partition.reset();
return err;
}
/***************************************************************************
Imgtool partition operations
***************************************************************************/
//-------------------------------------------------
// get_attribute_name - retrieves the human readable
// name for an attribute
//-------------------------------------------------
void imgtool::partition::get_attribute_name(uint32_t attribute, const imgtool_attribute *attr_value,
char *buffer, size_t buffer_len)
{
imgtoolerr_t err = (imgtoolerr_t)IMGTOOLERR_UNIMPLEMENTED;
buffer[0] = '\0';
if (attr_value)
{
if (m_attr_name)
err = m_attr_name(attribute, attr_value, buffer, buffer_len);
if (err == (imgtoolerr_t)IMGTOOLERR_UNIMPLEMENTED)
{
switch(attribute & 0xF0000)
{
case IMGTOOLATTR_INT_FIRST:
snprintf(buffer, buffer_len, "%d", (int) attr_value->i);
break;
}
}
}
else
{
switch(attribute)
{
case IMGTOOLATTR_INT_MAC_TYPE:
snprintf(buffer, buffer_len, "File type");
break;
case IMGTOOLATTR_INT_MAC_CREATOR:
snprintf(buffer, buffer_len, "File creator");
break;
case IMGTOOLATTR_INT_MAC_FINDERFLAGS:
snprintf(buffer, buffer_len, "Finder flags");
break;
case IMGTOOLATTR_INT_MAC_COORDX:
snprintf(buffer, buffer_len, "X coordinate");
break;
case IMGTOOLATTR_INT_MAC_COORDY:
snprintf(buffer, buffer_len, "Y coordinate");
break;
case IMGTOOLATTR_INT_MAC_FINDERFOLDER:
snprintf(buffer, buffer_len, "Finder folder");
break;
case IMGTOOLATTR_INT_MAC_ICONID:
snprintf(buffer, buffer_len, "Icon ID");
break;
case IMGTOOLATTR_INT_MAC_SCRIPTCODE:
snprintf(buffer, buffer_len, "Script code");
break;
case IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS:
snprintf(buffer, buffer_len, "Extended flags");
break;
case IMGTOOLATTR_INT_MAC_COMMENTID:
snprintf(buffer, buffer_len, "Comment ID");
break;
case IMGTOOLATTR_INT_MAC_PUTAWAYDIRECTORY:
snprintf(buffer, buffer_len, "Putaway directory");
break;
case IMGTOOLATTR_TIME_CREATED:
snprintf(buffer, buffer_len, "Creation time");
break;
case IMGTOOLATTR_TIME_LASTMODIFIED:
snprintf(buffer, buffer_len, "Last modified time");
break;
}
}
}
//-------------------------------------------------
// test_imgtool_datetime - unit test for imgtool::datetime
//-------------------------------------------------
static bool test_imgtool_datetime(int second, int minute, int hour, int day_of_month, int month, int year)
{
bool error = false;
util::arbitrary_datetime t;
t.second = second;
t.minute = minute;
t.hour = hour;
t.day_of_month = day_of_month;
t.month = month;
t.year = year;
imgtool::datetime dt(imgtool::datetime::datetime_type::GMT, t);
std::tm t2 = dt.gmtime();
if (t2.tm_sec != second)
{
util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_sec to be %d, instead got %d\n", second, t2.tm_sec);
error = true;
}
if (t2.tm_min != minute)
{
util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_min to be %d, instead got %d\n", minute, t2.tm_min);
error = true;
}
if (t2.tm_hour != hour)
{
util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_hour to be %d, instead got %d\n", hour, t2.tm_hour);
error = true;
}
if (t2.tm_mday != day_of_month)
{
util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_mday to be %d, instead got %d\n", day_of_month, t2.tm_mday);
error = true;
}
if (t2.tm_mon != month - 1)
{
util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_mon to be %d, instead got %d\n", month - 1, t2.tm_mon);
error = true;
}
if (t2.tm_year != year - 1900)
{
util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_mon to be %d, instead got %d\n", year - 1900, t2.tm_year);
error = true;
}
return error;
}
//-------------------------------------------------
// test_imgtool_datetime - unit tests for imgtool::datetime
//-------------------------------------------------
static bool test_imgtool_datetime()
{
bool error = false;
// various test cases for imgtool::datetime
if (test_imgtool_datetime(34, 23, 12, 18, 3, 1993)) // March 18th, 1993 12:23:34
error = true;
if (test_imgtool_datetime(0, 20, 16, 25, 12, 1976)) // December 25th, 1976 16:20:00
error = true;
return error;
}
//-------------------------------------------------
// imgtool_validitychecks - checks the validity
// of the imgtool modules
//-------------------------------------------------
bool imgtool_validitychecks(void)
{
bool error = false;
imgtoolerr_t err = (imgtoolerr_t)IMGTOOLERR_SUCCESS;
imgtool_module_features features;
int created_library = false;
// various test cases for imgtool::datetime
if (test_imgtool_datetime())
error = true;
if (!global_imgtool_library)
{
imgtool_init(false, nullptr);
created_library = true;
}
for (const auto &module : global_imgtool_library->modules())
{
features = imgtool_get_module_features(module.get());
if (!module->name)
{
util::stream_format(std::wcerr, L"imgtool module %s has null 'name'\n", wstring_from_utf8(module->name));
error = true;
}
if (!module->description)
{
util::stream_format(std::wcerr, L"imgtool module %s has null 'description'\n", wstring_from_utf8(module->name));
error = true;
}
if (!module->extensions)
{
util::stream_format(std::wcerr, L"imgtool module %s has null 'extensions'\n", wstring_from_utf8(module->extensions));
error = true;
}
#if 0
/* sanity checks on modules that do not support directories */
if (!module->path_separator)
{
if (module->alternate_path_separator)
{
util::stream_format(std::wcerr, L"imgtool module %s specified alternate_path_separator but not path_separator\n", wstring_from_utf8(module->name));
error = true;
}
if (module->initial_path_separator)
{
util::stream_format(std::wcerr, L"imgtool module %s specified initial_path_separator without directory support\n", wstring_from_utf8(module->name));
error = true;
}
if (module->create_dir)
{
util::stream_format(std::wcerr, L"imgtool module %s implements create_dir without directory support\n", wstring_from_utf8(module->name));
error = true;
}
if (module->delete_dir)
{
util::stream_format(std::wcerr, L"imgtool module %s implements delete_dir without directory support\n", wstring_from_utf8(module->name));
error = true;
}
}
#endif
/* sanity checks on creation options */
if (module->createimage_optguide || module->createimage_optspec)
{
if (!module->create)
{
util::stream_format(std::wcerr, L"imgtool module %s has creation options without supporting create\n", wstring_from_utf8(module->name));
error = true;
}
if ((!module->createimage_optguide && module->createimage_optspec)
|| (module->createimage_optguide && !module->createimage_optspec))
{
util::stream_format(std::wcerr, L"imgtool module %s does has partially incomplete creation options\n", wstring_from_utf8(module->name));
error = true;
}
if (module->createimage_optguide && module->createimage_optspec)
{
auto resolution = std::make_unique<util::option_resolution>(*module->createimage_optguide);
resolution->set_specification(module->createimage_optspec);
}
}
}
if (created_library)
imgtool_exit();
if (err)
{
util::stream_format(std::wcerr, L"imgtool: %s\n", wstring_from_utf8(imgtool_error(err)));
error = true;
}
return error;
}
/*-------------------------------------------------
imgtool_temp_str - provides a temporary string
buffer used for string passing
-------------------------------------------------*/
char *imgtool_temp_str(void)
{
static int index;
static char temp_string_pool[32][256];
return temp_string_pool[index++ % ARRAY_LENGTH(temp_string_pool)];
}
/***************************************************************************
Image handling functions
***************************************************************************/
imgtoolerr_t imgtool::image::internal_open(const imgtool_module *module, const std::string &filename,
int read_or_write, util::option_resolution *createopts, imgtool::image::ptr &outimg)
{
imgtoolerr_t err;
imgtool::stream::ptr stream;
imgtool::image::ptr image;
object_pool *pool = nullptr;
void *extra_bytes = nullptr;
outimg.reset();
// is the requested functionality implemented?
if ((read_or_write == OSD_FOPEN_RW_CREATE) ? !module->create : !module->open)
{
err = imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
goto done;
}
// create a memory pool
pool = pool_alloc_lib(nullptr);
if (!pool)
{
err = imgtoolerr_t(IMGTOOLERR_OUTOFMEMORY);
goto done;
}
// open the stream
stream = imgtool::stream::open(filename, read_or_write);
if (!stream)
{
err = imgtoolerr_t(IMGTOOLERR_FILENOTFOUND | IMGTOOLERR_SRC_IMAGEFILE);
goto done;
}
// allocate extra
if (module->image_extra_bytes > 0)
{
extra_bytes = pool_malloc_lib(pool, module->image_extra_bytes);
if (!extra_bytes)
{
err = imgtoolerr_t(IMGTOOLERR_OUTOFMEMORY);
goto done;
}
memset(extra_bytes, 0, module->image_extra_bytes);
}
// setup the image structure
try { image = std::make_unique<imgtool::image>(*module, pool, extra_bytes); }
catch (std::bad_alloc const &)
{
err = imgtoolerr_t(IMGTOOLERR_OUTOFMEMORY);
goto done;
}
// the pool is no longer owned by this function
pool = nullptr;
// actually call create or open
err = (read_or_write == OSD_FOPEN_RW_CREATE)
? module->create(*image, std::move(stream), createopts)
: module->open(*image, std::move(stream));
if (err)
{
err = markerrorsource(err);
goto done;
}
// we've succeeded - set the output image, and record that
// we are "okay to close"
image->m_okay_to_close = true;
outimg = std::move(image);
done:
if (pool)
pool_free_lib(pool);
return err;
}
//-------------------------------------------------
// open - open an image
//-------------------------------------------------
imgtoolerr_t imgtool::image::open(const imgtool_module *module, const std::string &filename, int read_or_write, ptr &outimg)
{
read_or_write = read_or_write ? OSD_FOPEN_RW : OSD_FOPEN_READ;
return internal_open(module, filename, read_or_write, nullptr, outimg);
}
//-------------------------------------------------
// imgtool::image::open_byname - open an image
//-------------------------------------------------
imgtoolerr_t imgtool::image::open(const std::string &modulename, const std::string &filename, int read_or_write, ptr &outimg)
{
const imgtool_module *module;
module = imgtool_find_module(modulename);
if (!module)
return imgtoolerr_t(IMGTOOLERR_MODULENOTFOUND | IMGTOOLERR_SRC_MODULE);
return open(module, filename, read_or_write, outimg);
}
//-------------------------------------------------
// imgtool::image::image
//-------------------------------------------------
imgtool::image::image(const imgtool_module &module, object_pool *pool, void *extra_bytes)
: m_module(module)
, m_pool(pool)
, m_extra_bytes(extra_bytes)
, m_okay_to_close(false)
{
}
//-------------------------------------------------
// imgtool::image::~image
//-------------------------------------------------
imgtool::image::~image()
{
if (m_okay_to_close && module().close)
module().close(*this);
pool_free_lib(m_pool);
}
//-------------------------------------------------
// create - creates an image
//-------------------------------------------------
imgtoolerr_t imgtool::image::create(const imgtool_module *module, const std::string &filename,
util::option_resolution *opts, ptr &image)
{
std::unique_ptr<util::option_resolution> alloc_resolution;
/* allocate dummy options if necessary */
if (!opts && module->createimage_optguide)
{
try { alloc_resolution.reset(new util::option_resolution(*module->createimage_optguide)); }
catch (...) { return (imgtoolerr_t)IMGTOOLERR_OUTOFMEMORY; }
if (module->createimage_optspec)
alloc_resolution->set_specification(module->createimage_optspec);
opts = alloc_resolution.get();
}
return internal_open(module, filename, OSD_FOPEN_RW_CREATE, opts, image);
}
//-------------------------------------------------
// create - creates an image
//-------------------------------------------------
imgtoolerr_t imgtool::image::create(const std::string &modulename, const std::string &filename, util::option_resolution *opts, ptr &image)
{
const imgtool_module *module;
module = imgtool_find_module(modulename);
if (!module)
return imgtoolerr_t(IMGTOOLERR_MODULENOTFOUND | IMGTOOLERR_SRC_MODULE);
return create(module, filename, opts, image);
}
//-------------------------------------------------
// create - creates an image
//-------------------------------------------------
imgtoolerr_t imgtool::image::create(const imgtool_module *module, const std::string &filename,
util::option_resolution *opts)
{
std::unique_ptr<image> image;
return create(module, filename, opts, image);
}
//-------------------------------------------------
// create - creates an image
//-------------------------------------------------
imgtoolerr_t imgtool::image::create(const std::string &modulename, const std::string &filename, util::option_resolution *opts)
{
std::unique_ptr<image> image;
return create(modulename, filename, opts, image);
}
//-------------------------------------------------
// info - returns format specific information about an image
//-------------------------------------------------
std::string imgtool::image::info()
{
std::string string;
if (module().info)
{
std::stringstream stream;
module().info(*this, stream);
string = stream.str();
}
return string;
}
#define PATH_MUSTBEDIR 0x00000001
#define PATH_LEAVENULLALONE 0x00000002
#define PATH_CANBEBOOTBLOCK 0x00000004
//-------------------------------------------------
// partition::cannonicalize_path - normalizes a path string
// into a NUL delimited list
//-------------------------------------------------
imgtoolerr_t imgtool::partition::cannonicalize_path(uint32_t flags, const char *path, std::string &result)
{
imgtoolerr_t err = (imgtoolerr_t)IMGTOOLERR_SUCCESS;
// is this path NULL? if so, is that ignored?
if (!path && (flags & PATH_LEAVENULLALONE))
return IMGTOOLERR_SUCCESS;
// is this the special filename for bootblocks?
if (path == FILENAME_BOOTBLOCK)
{
if (!(flags & PATH_CANBEBOOTBLOCK))
err = (imgtoolerr_t)IMGTOOLERR_UNEXPECTED;
else if (!m_supports_bootblock)
err = (imgtoolerr_t)IMGTOOLERR_FILENOTFOUND;
return err;
}
// normalize the path into the native character set
std::string converted_path;
imgtool::charconverter * const converter = reinterpret_cast<imgtool::charconverter * const>(get_info_ptr(IMGTOOLINFO_PTR_CHARCONVERTER));
if (converter)
{
converted_path = converter->from_utf8(path);
path = converted_path.c_str();
}
if (m_path_separator == '\0')
{
if (flags & PATH_MUSTBEDIR)
{
// do we specify a path when paths are not supported? */
if (path && *path)
return imgtoolerr_t(IMGTOOLERR_CANNOTUSEPATH | IMGTOOLERR_SRC_FUNCTIONALITY);
result = ""; /* normalize empty path */
}
else
{
// simple passthrough
result = path;
}
}
else
{
const char *s = path ? path : "";
// allocate space for a new cannonical path
std::ostringstream stream;
// copy the path
bool in_path_separator = true;
size_t i = 0;
do
{
if ((s[i] != '\0') && (s[i] != m_path_separator) && (s[i] != m_alternate_path_separator))
{
stream << s[i];
in_path_separator = false;
}
else if (!in_path_separator)
{
stream << '\0';
in_path_separator = true;
}
}
while(s[i++] != '\0');
stream << '\0' << '\0';
result = stream.str();
}
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// partition::cannonicalize_fork
//-------------------------------------------------
imgtoolerr_t imgtool::partition::cannonicalize_fork(const char **fork)
{
// does this module support forks?
if (m_list_forks)
{
// this module supports forks; make sure that fork is non-NULL
if (!*fork)
*fork = "";
}
else
{
// this module does not support forks; make sure that fork is NULL
if (*fork)
return IMGTOOLERR_NOFORKS;
}
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// partition::get_directory_entry - retrieves
// the nth directory entry within a partition
//-------------------------------------------------
imgtoolerr_t imgtool::partition::get_directory_entry(const char *path, int index, imgtool_dirent &ent)
{
imgtoolerr_t err;
imgtool::directory::ptr imgenum;
if (index < 0)
{
err = (imgtoolerr_t)IMGTOOLERR_PARAMTOOSMALL;
goto done;
}
err = imgtool::directory::open(*this, path, imgenum);
if (err)
goto done;
do
{
err = imgenum->get_next(ent);
if (err)
goto done;
if (ent.eof)
{
err = (imgtoolerr_t)IMGTOOLERR_FILENOTFOUND;
goto done;
}
}
while(index--);
done:
if (err)
memset(ent.filename, 0, sizeof(ent.filename));
return err;
}
//-------------------------------------------------
// partition::get_file_size - returns free
// space on a partition, in bytes
//-------------------------------------------------
imgtoolerr_t imgtool::partition::get_file_size(const char *fname, uint64_t &filesize)
{
imgtoolerr_t err;
imgtool::directory::ptr imgenum;
imgtool_dirent ent;
const char *path;
path = nullptr; /* TODO: Need to parse off the path */
filesize = ~((uint64_t) 0);
memset(&ent, 0, sizeof(ent));
err = imgtool::directory::open(*this, path, imgenum);
if (err)
goto done;
do
{
err = imgenum->get_next(ent);
if (err)
goto done;
if (!core_stricmp(fname, ent.filename))
{
filesize = ent.filesize;
goto done;
}
}
while(ent.filename[0]);
err = (imgtoolerr_t)IMGTOOLERR_FILENOTFOUND;
done:
return err;
}
//-------------------------------------------------
// partition::list_file_attributes - identifies
// all attributes on a file
//-------------------------------------------------
imgtoolerr_t imgtool::partition::list_file_attributes(const char *path, uint32_t *attrs, size_t len)
{
imgtoolerr_t err;
memset(attrs, 0, sizeof(*attrs) * len);
if (!m_list_attrs)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
// cannonicalize path
std::string cannonical_path;
err = cannonicalize_path(PATH_LEAVENULLALONE, path, cannonical_path);
if (err)
return err;
err = m_list_attrs(*this, cannonical_path.c_str(), attrs, len);
if (err)
return err;
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// partition::get_file_attributes - retrieves
// attributes on a file
//-------------------------------------------------
imgtoolerr_t imgtool::partition::get_file_attributes(const char *path, const uint32_t *attrs, imgtool_attribute *values)
{
imgtoolerr_t err;
if (!m_get_attrs)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
// cannonicalize path
std::string cannonical_path;
err = cannonicalize_path(PATH_LEAVENULLALONE, path, cannonical_path);
if (err)
return err;
err = m_get_attrs(*this, cannonical_path.c_str(), attrs, values);
if (err)
return err;
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// imgtool::partition::put_file_attributes - sets
// attributes on a file
//-------------------------------------------------
imgtoolerr_t imgtool::partition::put_file_attributes(const char *path, const uint32_t *attrs, const imgtool_attribute *values)
{
imgtoolerr_t err;
if (!m_set_attrs)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
// cannonicalize path
std::string cannonical_path;
err = cannonicalize_path(PATH_LEAVENULLALONE, path, cannonical_path);
if (err)
return err;
err = m_set_attrs(*this, cannonical_path.c_str(), attrs, values);
if (err)
return err;
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// partition::get_file_attribute - retrieves
// an attribute on a single file
//-------------------------------------------------
imgtoolerr_t imgtool::partition::get_file_attribute(const char *path, uint32_t attr, imgtool_attribute &value)
{
uint32_t attrs[2];
attrs[0] = attr;
attrs[1] = 0;
return get_file_attributes(path, attrs, &value);
}
//-------------------------------------------------
// partition::put_file_attribute - sets
// attributes on a single file
//-------------------------------------------------
imgtoolerr_t imgtool::partition::put_file_attribute(const char *path, uint32_t attr, const imgtool_attribute &value)
{
uint32_t attrs[2];
attrs[0] = attr;
attrs[1] = 0;
return put_file_attributes(path, attrs, &value);
}
//-------------------------------------------------
// partition::get_icon_info - retrieves the
// icon for a file stored on a partition
//-------------------------------------------------
imgtoolerr_t imgtool::partition::get_icon_info(const char *path, imgtool_iconinfo *iconinfo)
{
imgtoolerr_t err;
if (!m_get_iconinfo)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
// cannonicalize path
std::string cannonical_path;
err = cannonicalize_path(0, path, cannonical_path);
if (err)
return err;
memset(iconinfo, 0, sizeof(*iconinfo));
err = m_get_iconinfo(*this, cannonical_path.c_str(), iconinfo);
if (err)
return err;
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// partition::suggest_file_filters - suggests a
// list of filters appropriate for a file on a
// partition
//-------------------------------------------------
imgtoolerr_t imgtool::partition::suggest_file_filters(const char *path,
imgtool::stream *stream, imgtool_transfer_suggestion *suggestions, size_t suggestions_length)
{
imgtoolerr_t err;
int i, j;
imgtoolerr_t (*check_stream)(imgtool::stream &stream, imgtool_suggestion_viability_t *viability);
size_t position;
// clear out buffer
memset(suggestions, 0, sizeof(*suggestions) * suggestions_length);
if (!m_suggest_transfer)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
// cannonicalize path
std::string cannonical_path;
err = cannonicalize_path(PATH_LEAVENULLALONE, path, cannonical_path);
if (err)
return err;
// invoke the module's suggest call
err = m_suggest_transfer(*this, cannonical_path.c_str(), suggestions, suggestions_length);
if (err)
return err;
// loop on resulting suggestions, and do the following:
// 1. Call check_stream if present, and remove disqualified streams
// 2. Fill in missing descriptions
i = j = 0;
while(suggestions[i].viability)
{
if (stream && suggestions[i].filter)
{
check_stream = (imgtoolerr_t (*)(imgtool::stream &, imgtool_suggestion_viability_t *)) filter_get_info_fct(suggestions[i].filter, FILTINFO_PTR_CHECKSTREAM);
if (check_stream)
{
position = stream->tell();
err = check_stream(*stream, &suggestions[i].viability);
stream->seek(position, SEEK_SET);
if (err)
return err;
}
}
/* the check_stream proc can remove the option by clearing out the viability */
if (suggestions[i].viability)
{
/* we may have to move this suggestion, if one was removed */
if (i != j)
memcpy(&suggestions[j], &suggestions[i], sizeof(*suggestions));
/* if the description is missing, fill it in */
if (!suggestions[j].description)
{
if (suggestions[j].filter)
suggestions[j].description = filter_get_info_string(suggestions[i].filter, FILTINFO_STR_HUMANNAME);
else
suggestions[j].description = "Raw";
}
j++;
}
i++;
}
suggestions[j].viability = (imgtool_suggestion_viability_t)0;
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// partition::get_chain - retrieves the block
// chain for a file or directory on a partition
//-------------------------------------------------
imgtoolerr_t imgtool::partition::get_chain(const char *path, imgtool_chainent *chain, size_t chain_size)
{
size_t i;
assert(chain_size > 0);
if (!m_get_chain)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
// initialize the chain array, so the module's get_chain function can be lazy
for (i = 0; i < chain_size; i++)
{
chain[i].level = 0;
chain[i].block = ~0;
}
return m_get_chain(*this, path, chain, chain_size - 1);
}
//-------------------------------------------------
// partition::get_chain_string - retrieves
// the block chain for a file or directory on a
// partition
//-------------------------------------------------
imgtoolerr_t imgtool::partition::get_chain_string(const char *path, char *buffer, size_t buffer_len)
{
imgtoolerr_t err;
imgtool_chainent chain[512];
uint64_t last_block;
uint8_t cur_level = 0;
int len, i;
int comma_needed = false;
// determine the last block identifier
chain[0].block = ~0;
last_block = chain[0].block;
err = get_chain(path, chain, ARRAY_LENGTH(chain));
if (err)
return err;
len = snprintf(buffer, buffer_len, "[");
buffer += len;
buffer_len -= len;
for (i = 0; chain[i].block != last_block; i++)
{
while(cur_level < chain[i].level)
{
len = snprintf(buffer, buffer_len, " [");
buffer += len;
buffer_len -= len;
cur_level++;
comma_needed = false;
}
while(cur_level > chain[i].level)
{
len = snprintf(buffer, buffer_len, "]");
buffer += len;
buffer_len -= len;
cur_level--;
}
if (comma_needed)
{
len = snprintf(buffer, buffer_len, ", ");
buffer += len;
buffer_len -= len;
}
len = snprintf(buffer, buffer_len, "%u", (unsigned) chain[i].block);
buffer += len;
buffer_len -= len;
comma_needed = true;
}
do
{
len = snprintf(buffer, buffer_len, "]");
buffer += len;
buffer_len -= len;
}
while(cur_level-- > 0);
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// partition::get_free_space - returns the
// amount of free space on a partition
//-------------------------------------------------
imgtoolerr_t imgtool::partition::get_free_space(uint64_t &sz)
{
imgtoolerr_t err;
if (!m_free_space)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
err = m_free_space(*this, &sz);
if (err)
return (imgtoolerr_t)(err | IMGTOOLERR_SRC_IMAGEFILE);
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// partition::read_file - starts reading
// from a file on a partition with a stream
//-------------------------------------------------
imgtoolerr_t imgtool::partition::read_file(const char *filename, const char *fork, imgtool::stream &destf, filter_getinfoproc filter)
{
imgtoolerr_t err;
union filterinfo u;
if (!m_read_file)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
if (filter)
{
// use a filter
filter(FILTINFO_PTR_READFILE, &u);
if (!u.read_file)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
err = u.read_file(*this, filename, fork, destf);
if (err)
return markerrorsource(err);
}
else
{
// cannonicalize path
std::string cannonical_path;
err = cannonicalize_path(PATH_CANBEBOOTBLOCK, filename, cannonical_path);
if (err)
return err;
err = cannonicalize_fork(&fork);
if (err)
return err;
// invoke the actual module
err = m_read_file(*this, cannonical_path.c_str(), fork, destf);
if (err)
return markerrorsource(err);
}
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// partition::write_file - starts writing
// to a new file on an image with a stream
//-------------------------------------------------
imgtoolerr_t imgtool::partition::write_file(const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts, filter_getinfoproc filter)
{
imgtoolerr_t err;
std::unique_ptr<util::option_resolution> alloc_resolution;
uint64_t free_space;
uint64_t file_size;
union filterinfo u;
if (!m_write_file)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
if (filter)
{
// use a filter
filter(FILTINFO_PTR_WRITEFILE, &u);
if (!u.write_file)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
err = u.write_file(*this, filename, fork, sourcef, opts);
if (err)
return markerrorsource(err);
}
else
{
// does this partition prefer upper case file names?
std::string ucase_str;
if (m_prefer_ucase)
{
std::ostringstream s;
size_t i = 0;
while (filename[i])
{
char32_t ch;
int count = uchar_from_utf8(&ch, &filename[i], UTF8_CHAR_MAX);
if (count > 0)
{
char32_t upper_ch = uchar_toupper(ch);
s << utf8_from_uchar(upper_ch);
}
i += count > 0 ? count : 1;
}
ucase_str = s.str();
filename = ucase_str.c_str();
}
// cannonicalize path
std::string cannonical_path;
err = cannonicalize_path(PATH_CANBEBOOTBLOCK, filename, cannonical_path);
if (err)
return err;
err = cannonicalize_fork(&fork);
if (err)
return err;
// allocate dummy options if necessary
if (!opts && m_writefile_optguide)
{
try { alloc_resolution.reset(new util::option_resolution(*m_writefile_optguide)); }
catch (...)
{
return IMGTOOLERR_OUTOFMEMORY;
}
if (!m_writefile_optspec.empty())
alloc_resolution->set_specification(m_writefile_optspec);
opts = alloc_resolution.get();
}
// if free_space is implemented; do a quick check to see if space is available
if (m_free_space)
{
err = m_free_space(*this, &free_space);
if (err)
return markerrorsource(err);
file_size = sourcef.size();
if (file_size > free_space)
return markerrorsource(IMGTOOLERR_NOSPACE);
}
// actually invoke the write file handler
err = m_write_file(*this, cannonical_path.c_str(), fork, sourcef, opts);
if (err)
return markerrorsource(err);
}
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// partition::get_file - read a file from
// an image, storing it into a native file
//-------------------------------------------------
imgtoolerr_t imgtool::partition::get_file(const char *filename, const char *fork,
const char *dest, filter_getinfoproc filter)
{
imgtoolerr_t err;
imgtool::stream::ptr f;
char *alloc_dest = nullptr;
const char *filter_extension = nullptr;
if (!dest)
{
// determine the filter extension, if appropriate
if (filter != nullptr)
filter_extension = filter_get_info_string(filter, FILTINFO_STR_EXTENSION);
if (filter_extension != nullptr)
{
alloc_dest = (char*)malloc(strlen(filename) + 1 + strlen(filter_extension) + 1);
if (!alloc_dest)
return IMGTOOLERR_OUTOFMEMORY;
sprintf(alloc_dest, "%s.%s", filename, filter_extension);
dest = alloc_dest;
}
else
{
if (filename == FILENAME_BOOTBLOCK)
dest = "boot.bin";
else
dest = filename;
}
}
f = imgtool::stream::open(dest, OSD_FOPEN_WRITE);
if (!f)
{
err = imgtoolerr_t(IMGTOOLERR_FILENOTFOUND | IMGTOOLERR_SRC_NATIVEFILE);
goto done;
}
err = read_file(filename, fork, *f, filter);
done:
if (alloc_dest != nullptr)
free(alloc_dest);
return err;
}
//-------------------------------------------------
// partition::put_file - read a native file
// and store it on a partition
//-------------------------------------------------
imgtoolerr_t imgtool::partition::put_file(const char *newfname, const char *fork,
const char *source, util::option_resolution *opts, filter_getinfoproc filter)
{
imgtoolerr_t err;
imgtool::stream::ptr f;
std::string alloc_newfname;
std::string basename;
if (!newfname)
{
basename = core_filename_extract_base(source);
newfname = basename.c_str();
}
imgtool::charconverter *charconverter = (imgtool::charconverter *) get_info_ptr(IMGTOOLINFO_PTR_CHARCONVERTER);
if (charconverter)
{
// convert to native format
try
{
alloc_newfname = charconverter->from_utf8(newfname);
}
catch (charconverter_exception)
{
return imgtoolerr_t(IMGTOOLERR_BADFILENAME | IMGTOOLERR_SRC_NATIVEFILE);
}
newfname = alloc_newfname.c_str();
}
f = imgtool::stream::open(source, OSD_FOPEN_READ);
if (f)
err = write_file(newfname, fork, *f, opts, filter);
else
err = imgtoolerr_t(IMGTOOLERR_FILENOTFOUND | IMGTOOLERR_SRC_NATIVEFILE);
return err;
}
/*-------------------------------------------------
imgtool::partition::delete_file - delete a file
on a partition
-------------------------------------------------*/
imgtoolerr_t imgtool::partition::delete_file(const char *fname)
{
imgtoolerr_t err;
if (!m_delete_file)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
// cannonicalize path
std::string cannonical_path;
err = cannonicalize_path(0, fname, cannonical_path);
if (err)
return err;
err = m_delete_file(*this, cannonical_path.c_str());
if (err)
return markerrorsource(err);
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// partition::list_file_forks - lists all
// forks on an image
//-------------------------------------------------
imgtoolerr_t imgtool::partition::list_file_forks(const char *path, std::vector<imgtool::fork_entry> &forks)
{
imgtoolerr_t err;
if (!m_list_forks)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
// cannonicalize path
std::string cannonical_path;
err = cannonicalize_path(0, path, cannonical_path);
if (err)
return err;
// call the callback
forks.clear();
err = m_list_forks(*this, cannonical_path.c_str(), forks);
if (err)
return err;
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// partition::create_directory - creates a
// directory on a partition
//-------------------------------------------------
imgtoolerr_t imgtool::partition::create_directory(const char *path)
{
imgtoolerr_t err;
// implemented?
if (!m_create_dir)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
// cannonicalize path
std::string cannonical_path;
err = cannonicalize_path(PATH_MUSTBEDIR, path, cannonical_path);
if (err)
return err;
err = m_create_dir(*this, path);
if (err)
return err;
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// partition::delete_directory - deletes a
// directory on a partition
//-------------------------------------------------
imgtoolerr_t imgtool::partition::delete_directory(const char *path)
{
imgtoolerr_t err;
// implemented?
if (!m_delete_dir)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
// cannonicalize path
std::string cannonical_path;
err = cannonicalize_path(PATH_MUSTBEDIR, path, cannonical_path);
if (err)
return err;
err = m_delete_dir(*this, path);
if (err)
return err;
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// partition::get_block_size - gets the
// size of a standard block on a partition
//-------------------------------------------------
imgtoolerr_t imgtool::partition::get_block_size(uint32_t &length)
{
return m_image.get_block_size(length);
}
//-------------------------------------------------
// partition::is_block_in_range
//-------------------------------------------------
imgtoolerr_t imgtool::partition::map_block_to_image_block(uint64_t partition_block, uint64_t &image_block) const
{
if (partition_block >= m_block_count)
return IMGTOOLERR_SEEKERROR;
image_block = m_base_block + partition_block;
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// partition::read_block - reads a standard
// block on a partition
//-------------------------------------------------
imgtoolerr_t imgtool::partition::read_block(uint64_t block, void *buffer)
{
uint64_t image_block;
imgtoolerr_t err = map_block_to_image_block(block, image_block);
if (err)
return err;
return m_image.read_block(image_block, buffer);
}
//-------------------------------------------------
// partition::write_block - writes a
// standard block on a partition
//-------------------------------------------------
imgtoolerr_t imgtool::partition::write_block(uint64_t block, const void *buffer)
{
uint64_t image_block;
imgtoolerr_t err = map_block_to_image_block(block, image_block);
if (err)
return err;
return m_image.write_block(image_block, buffer);
}
//-------------------------------------------------
// partition::get_features - retrieves a
// structure identifying this partition's features
// associated with an image
//-------------------------------------------------
imgtool_partition_features imgtool::partition::get_features() const
{
imgtool_partition_features features;
memset(&features, 0, sizeof(features));
if (m_read_file)
features.supports_reading = 1;
if (m_write_file)
features.supports_writing = 1;
if (m_delete_file)
features.supports_deletefile = 1;
if (m_path_separator)
features.supports_directories = 1;
if (m_free_space)
features.supports_freespace = 1;
if (m_create_dir)
features.supports_createdir = 1;
if (m_delete_dir)
features.supports_deletedir = 1;
if (m_supports_creation_time)
features.supports_creation_time = 1;
if (m_supports_lastmodified_time)
features.supports_lastmodified_time = 1;
if (m_get_iconinfo)
features.supports_geticoninfo = 1;
if (!features.supports_writing && !features.supports_createdir && !features.supports_deletefile && !features.supports_deletedir)
features.is_read_only = 1;
return features;
}
//-------------------------------------------------
// partition::get_info_ptr - retrieves a
// pointer associated with a partition's format
//-------------------------------------------------
void *imgtool::partition::get_info_ptr(uint32_t state)
{
return imgtool_get_info_ptr(&m_imgclass, state);
}
//-------------------------------------------------
// partition::get_info_string - retrieves a
// string associated with a partition's format
//-------------------------------------------------
const char *imgtool::partition::get_info_string(uint32_t state)
{
return imgtool_get_info_string(&m_imgclass, state);
}
//-------------------------------------------------
// partition::get_info_int - retrieves a
// pointer associated with a partition's format
//-------------------------------------------------
uint64_t imgtool::partition::get_info_int(uint32_t state)
{
return imgtool_get_info_int(&m_imgclass, state);
}
//-------------------------------------------------
// partition::extra_bytes - returns extra
// bytes on a partition
//-------------------------------------------------
void *imgtool::partition::extra_bytes()
{
if (!m_extra_bytes)
throw false;
return m_extra_bytes.get();
}
/***************************************************************************
Path handling functions
***************************************************************************/
//-------------------------------------------------
// partition::get_root_path - retrieves
// the path root of this partition
//-------------------------------------------------
const char *imgtool::partition::get_root_path()
{
int initial_path_separator;
char path_separator;
char *buf;
int pos = 0;
initial_path_separator = get_info_int(IMGTOOLINFO_INT_INITIAL_PATH_SEPARATOR) ? 1 : 0;
path_separator = (char) get_info_int(IMGTOOLINFO_INT_PATH_SEPARATOR);
buf = imgtool_temp_str();
if (initial_path_separator)
buf[pos++] = path_separator;
buf[pos] = '\0';
return buf;
}
//-------------------------------------------------
// partition::path_concatenate - retrieves
// a pointer associated with a partition's format
//-------------------------------------------------
const char *imgtool::partition::path_concatenate(const char *path1, const char *path2)
{
char path_separator;
size_t len;
char *buffer;
size_t buffer_len;
path_separator = (char) get_info_int(IMGTOOLINFO_INT_PATH_SEPARATOR);
len = strlen(path1);
/* prepare buffer */
buffer = imgtool_temp_str();
buffer_len = 256;
if (!strcmp(path2, "."))
{
/* keep the same directory */
snprintf(buffer, buffer_len, "%s", path1);
}
else if (!strcmp(path2, ".."))
{
/* up one directory */
while((len > 0) && (path1[len - 1] == path_separator))
len--;
while((len > 0) && (path1[len - 1] != path_separator))
len--;
while((len > 1) && (path1[len - 1] == path_separator))
len--;
snprintf(buffer, buffer_len, "%s", path1);
buffer[len] = '\0';
}
else
{
/* append a path */
if ((*path1 != 0) && (path1[strlen(path1) - 1] != path_separator))
snprintf(buffer, buffer_len, "%s%c%s", path1, path_separator, path2);
else
snprintf(buffer, buffer_len, "%s%s", path1, path2);
}
return buffer;
}
//-------------------------------------------------
// partition::get_base_name - retrieves
// a base name for a partition specific path
//-------------------------------------------------
const char *imgtool::partition::get_base_name(const char *path)
{
char path_separator;
const char *new_path = path;
int i;
path_separator = (char) get_info_int(IMGTOOLINFO_INT_PATH_SEPARATOR);
for (i = 0; path[i]; i++)
{
if (path[i] == path_separator)
new_path = &path[i + 1];
}
return new_path;
}
/***************************************************************************
Directory handling functions
***************************************************************************/
//-------------------------------------------------
// directory ctor
//-------------------------------------------------
imgtool::directory::directory(imgtool::partition &partition)
: m_partition(partition)
, m_okay_to_close(false)
{
if (partition.m_directory_extra_bytes > 0)
{
m_extra_bytes = std::make_unique<uint8_t[]>(partition.m_directory_extra_bytes);
memset(m_extra_bytes.get(), 0, sizeof(m_extra_bytes.get()[0] * partition.m_directory_extra_bytes));
}
}
//-------------------------------------------------
// directory::open - begins
// enumerating files on a partition
//-------------------------------------------------
imgtoolerr_t imgtool::directory::open(imgtool::partition &partition, const std::string &path, imgtool::directory::ptr &outenum)
{
imgtoolerr_t err = imgtoolerr_t(IMGTOOLERR_SUCCESS);
imgtool::directory::ptr enumeration;
outenum.reset();
if (!partition.m_next_enum)
return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY);
std::string cannonical_path;
err = partition.cannonicalize_path(PATH_MUSTBEDIR, path.c_str(), cannonical_path);
if (err)
return err;
try { enumeration = std::make_unique<directory>(partition); }
catch (std::bad_alloc const &)
{
return imgtoolerr_t(IMGTOOLERR_OUTOFMEMORY);
}
if (partition.m_begin_enum)
{
err = partition.m_begin_enum(*enumeration, cannonical_path.c_str());
if (err)
return markerrorsource(err);
}
enumeration->m_okay_to_close = true;
outenum = std::move(enumeration);
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// directory dtor
//-------------------------------------------------
imgtool::directory::~directory()
{
if (m_okay_to_close && m_partition.m_close_enum)
m_partition.m_close_enum(*this);
}
//-------------------------------------------------
// directory::get_next - continues
// enumerating files within a partition
//-------------------------------------------------
imgtoolerr_t imgtool::directory::get_next(imgtool_dirent &ent)
{
imgtoolerr_t err;
// This makes it so that drivers don't have to take care of clearing
// the attributes if they don't apply
memset(&ent, 0, sizeof(ent));
err = m_partition.m_next_enum(*this, ent);
if (err)
return markerrorsource(err);
imgtool::charconverter * const converter = reinterpret_cast<imgtool::charconverter * const>(m_partition.get_info_ptr(IMGTOOLINFO_PTR_CHARCONVERTER));
if (converter)
{
std::string new_fname;
try
{
new_fname = converter->to_utf8(ent.filename);
}
catch (charconverter_exception)
{
return imgtoolerr_t(IMGTOOLERR_BADFILENAME);
}
snprintf(ent.filename, ARRAY_LENGTH(ent.filename), "%s", new_fname.c_str());
}
// don't trust the module!
if (!m_partition.m_supports_creation_time && (ent.creation_time.type() != imgtool::datetime::datetime_type::NONE))
{
internal_error(nullptr, "next_enum() specified creation_time, which is marked as unsupported by this module");
return IMGTOOLERR_UNEXPECTED;
}
if (!m_partition.m_supports_lastmodified_time && (ent.lastmodified_time.type() != imgtool::datetime::datetime_type::NONE))
{
internal_error(nullptr, "next_enum() specified lastmodified_time, which is marked as unsupported by this module");
return IMGTOOLERR_UNEXPECTED;
}
if (!m_partition.m_path_separator && ent.directory)
{
internal_error(nullptr, "next_enum() returned a directory, which is marked as unsupported by this module");
return IMGTOOLERR_UNEXPECTED;
}
return IMGTOOLERR_SUCCESS;
}
//-------------------------------------------------
// unknown_partition_get_info - represents an
// unknown partition
//-------------------------------------------------
void unknown_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(), "unknown"); break;
case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Unknown partition type"); break;
}
}