cdrom: add disc dump option

This commit is contained in:
Brad Parker 2019-07-08 15:32:12 -04:00
parent 2ef16de941
commit 15d97dbaf3
13 changed files with 447 additions and 34 deletions

View File

@ -8939,3 +8939,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_XMB_ANIMATION_OPENING_MAIN_MENU,
"Animation Main Menu Opens/Closes")
MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_GPU_INDEX,
"GPU Index")
MSG_HASH(MSG_DUMPING_DISC,
"Dumping disc...")
MSG_HASH(MSG_DRIVE_NUMBER,
"Drive %d")

View File

@ -835,6 +835,7 @@ static int cdrom_read_track_info(libretro_vfs_implementation_file *stream, unsig
unsigned lba = 0;
unsigned track_size = 0;
int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
ssize_t pregap_lba_len;
if (rv)
return 1;
@ -848,6 +849,10 @@ static int cdrom_read_track_info(libretro_vfs_implementation_file *stream, unsig
/* lba_start may be earlier than the MSF start times seen in read_subq */
toc->track[track - 1].lba_start = lba;
toc->track[track - 1].track_size = track_size;
pregap_lba_len = (toc->track[track - 1].audio ? 0 : (toc->track[track - 1].lba - toc->track[track - 1].lba_start));
toc->track[track - 1].track_bytes = (track_size - pregap_lba_len) * 2352;
toc->track[track - 1].mode = buf[6] & 0xF;
#ifdef CDROM_DEBUG
@ -1239,7 +1244,6 @@ struct string_list* cdrom_get_available_drives(void)
struct string_list *list = string_list_new();
#if defined(__linux__) && !defined(ANDROID)
struct string_list *dir_list = dir_list_new("/dev", NULL, false, false, false, false);
int drive_index = 0;
int i;
if (!dir_list)
@ -1250,7 +1254,7 @@ struct string_list* cdrom_get_available_drives(void)
if (strstr(dir_list->elems[i].data, "/dev/sg"))
{
char drive_model[32] = {0};
char drive_string[64] = {0};
char drive_string[32] = {0};
union string_list_elem_attr attr = {0};
int dev_index = 0;
RFILE *file = filestream_open(dir_list->elems[i].data, RETRO_VFS_FILE_ACCESS_READ, 0);
@ -1272,15 +1276,12 @@ struct string_list* cdrom_get_available_drives(void)
dev_index = '0' + dev_index;
attr.i = dev_index;
snprintf(drive_string, sizeof(drive_string), "Drive %d: ", drive_index + 1);
if (!string_is_empty(drive_model))
strlcat(drive_string, drive_model, sizeof(drive_string));
else
strlcat(drive_string, "Unknown Drive", sizeof(drive_string));
string_list_append(list, drive_string, attr);
drive_index++;
}
}
@ -1326,15 +1327,12 @@ struct string_list* cdrom_get_available_drives(void)
attr.i = path[0];
snprintf(drive_string, sizeof(drive_string), "Drive %d: ", drive_index + 1);
if (!string_is_empty(drive_model))
strlcat(drive_string, drive_model, sizeof(drive_string));
else
strlcat(drive_string, "Unknown Drive", sizeof(drive_string));
string_list_append(list, drive_string, attr);
drive_index++;
}
}
#endif
@ -1507,3 +1505,52 @@ bool cdrom_has_atip(const libretro_vfs_implementation_file *stream)
return true;
}
void cdrom_device_fillpath(char *path, size_t len, char drive, unsigned char track, bool is_cue)
{
size_t pos = 0;
if (!path || len == 0)
return;
if (is_cue)
{
#ifdef _WIN32
pos = strlcpy(path, "cdrom://", len);
if (len > pos)
path[pos++] = drive;
pos = strlcat(path, ":/drive.cue", len);
#else
#ifdef __linux__
pos = strlcpy(path, "cdrom://drive", len);
if (len > pos)
path[pos++] = drive;
pos = strlcat(path, ".cue", len);
#endif
#endif
}
else
{
#ifdef _WIN32
pos = strlcpy(path, "cdrom://", len);
if (len > pos)
path[pos++] = drive;
pos += snprintf(path + pos, len - pos, ":/drive-track%02d.bin", track);
#else
#ifdef __linux__
pos = strlcpy(path, "cdrom://drive", len);
if (len > pos)
path[pos++] = drive;
pos += snprintf(path + pos, len - pos, "-track%02d.bin", track);
#endif
#endif
}
}

View File

@ -629,11 +629,20 @@ void fill_str_dated_filename(char *out_filename,
const struct tm* tm_ = localtime(&cur_time);
format[0] = '\0';
strftime(format, sizeof(format), "-%y%m%d-%H%M%S.", tm_);
fill_pathname_join_concat_noext(out_filename,
in_str, format, ext,
size);
if (string_is_empty(ext))
{
strftime(format, sizeof(format), "-%y%m%d-%H%M%S", tm_);
fill_pathname_noext(out_filename, in_str, format, size);
}
else
{
strftime(format, sizeof(format), "-%y%m%d-%H%M%S.", tm_);
fill_pathname_join_concat_noext(out_filename,
in_str, format, ext,
size);
}
}
/**
@ -885,8 +894,7 @@ void fill_pathname_join_special_ext(char *out_path,
strlcat(out_path, ext, size);
}
void fill_pathname_join_concat_noext(
char *out_path,
void fill_pathname_join_concat_noext(char *out_path,
const char *dir, const char *path,
const char *concat,
size_t size)

View File

@ -50,7 +50,8 @@ typedef struct
{
unsigned lba_start; /* start of pregap */
unsigned lba; /* start of data */
unsigned track_size;
unsigned track_size; /* in LBAs */
unsigned track_bytes;
unsigned char track_num;
unsigned char min; /* start of data */
unsigned char sec;
@ -117,6 +118,8 @@ bool cdrom_get_timeouts(libretro_vfs_implementation_file *stream, cdrom_group_ti
bool cdrom_has_atip(const libretro_vfs_implementation_file *stream);
void cdrom_device_fillpath(char *path, size_t len, char drive, unsigned char track, bool is_cue);
RETRO_END_DECLS
#endif

View File

@ -376,8 +376,7 @@ void fill_pathname_join_special_ext(char *out_path,
const char *last, const char *ext,
size_t size);
void fill_pathname_join_concat_noext(
char *out_path,
void fill_pathname_join_concat_noext(char *out_path,
const char *dir, const char *path,
const char *concat,
size_t size);

View File

@ -369,6 +369,12 @@ int64_t retro_vfs_file_read_cdrom(libretro_vfs_implementation_file *stream,
unsigned char rsec = 0;
unsigned char rframe = 0;
if (stream->cdrom.byte_pos >= vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].track_bytes)
return 0;
if (stream->cdrom.byte_pos + len > vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].track_bytes)
len -= (stream->cdrom.byte_pos + len) - vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].track_bytes;
cdrom_lba_to_msf(stream->cdrom.cur_lba, &min, &sec, &frame);
cdrom_lba_to_msf(stream->cdrom.cur_lba - vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].lba, &rmin, &rsec, &rframe);

View File

@ -2141,7 +2141,11 @@ static int action_ok_load_cdrom(const char *path,
static int action_ok_dump_cdrom(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
{
/* TODO/FIXME - implement */
if (string_is_empty(label))
return -1;
#ifdef HAVE_CDROM
task_push_cdrom_dump(label);
#endif
return 0;
}

View File

@ -168,7 +168,7 @@ static int menu_displaylist_parse_core_info(menu_displaylist_info_t *info)
if (!info_list[i].name)
continue;
fill_pathname_join_concat_noext(tmp,
fill_pathname_join_concat(tmp,
msg_hash_to_str(info_list[i].msg),
": ",
info_list[i].name,
@ -345,7 +345,7 @@ static unsigned menu_displaylist_parse_system_info(menu_displaylist_info_t *info
(void)tmp_string;
#ifdef HAVE_GIT_VERSION
fill_pathname_join_concat_noext(
fill_pathname_join_concat(
tmp,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_GIT_VERSION),
": ",
@ -493,7 +493,7 @@ static unsigned menu_displaylist_parse_system_info(menu_displaylist_info_t *info
tmp2[0] = '\0';
fill_pathname_join_concat_noext(
fill_pathname_join_concat(
tmp,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_FRONTEND_IDENTIFIER),
": ",
@ -509,7 +509,7 @@ static unsigned menu_displaylist_parse_system_info(menu_displaylist_info_t *info
{
frontend->get_lakka_version(tmp2, sizeof(tmp2));
fill_pathname_join_concat_noext(tmp,
fill_pathname_join_concat(tmp,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_LAKKA_VERSION),
": ",
tmp2,
@ -525,7 +525,7 @@ static unsigned menu_displaylist_parse_system_info(menu_displaylist_info_t *info
{
frontend->get_name(tmp2, sizeof(tmp2));
fill_pathname_join_concat_noext(tmp,
fill_pathname_join_concat(tmp,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_FRONTEND_NAME),
": ",
tmp2,
@ -661,7 +661,7 @@ static unsigned menu_displaylist_parse_system_info(menu_displaylist_info_t *info
break;
}
fill_pathname_join_concat_noext(tmp,
fill_pathname_join_concat(tmp,
msg_hash_to_str(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_POWER_SOURCE),
": ",
@ -678,7 +678,7 @@ static unsigned menu_displaylist_parse_system_info(menu_displaylist_info_t *info
video_context_driver_get_ident(&ident_info);
tmp_string = ident_info.ident;
fill_pathname_join_concat_noext(tmp,
fill_pathname_join_concat(tmp,
msg_hash_to_str(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_VIDEO_CONTEXT_DRIVER),
": ",
@ -809,7 +809,7 @@ static unsigned menu_displaylist_parse_system_info(menu_displaylist_info_t *info
for (i = 0; i < ARRAY_SIZE(info_list); i++)
{
fill_pathname_join_concat_noext(feat_str,
fill_pathname_join_concat(feat_str,
msg_hash_to_str(
info_list[i].msg),
": ",
@ -987,7 +987,7 @@ static int create_string_list_rdb_entry_string(
string_list_join_concat(output_label, str_len, str_list, "|");
string_list_free(str_list);
fill_pathname_join_concat_noext(tmp, desc, ": ",
fill_pathname_join_concat(tmp, desc, ": ",
actual_string, path_size);
menu_entries_append_enum(list, tmp, output_label,
enum_idx,
@ -1181,7 +1181,7 @@ static int menu_displaylist_parse_database_entry(menu_handle_t *menu,
if (db_info_entry->name)
{
fill_pathname_join_concat_noext(tmp,
fill_pathname_join_concat(tmp,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_NAME),
": ",
db_info_entry->name,
@ -1193,7 +1193,7 @@ static int menu_displaylist_parse_database_entry(menu_handle_t *menu,
}
if (db_info_entry->description)
{
fill_pathname_join_concat_noext(tmp,
fill_pathname_join_concat(tmp,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_DESCRIPTION),
": ",
db_info_entry->description,
@ -1205,7 +1205,7 @@ static int menu_displaylist_parse_database_entry(menu_handle_t *menu,
}
if (db_info_entry->genre)
{
fill_pathname_join_concat_noext(tmp,
fill_pathname_join_concat(tmp,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_GENRE),
": ",
db_info_entry->genre,
@ -4906,9 +4906,18 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
for (i = 0; list && i < list->size; i++)
{
char drive_string[32] = {0};
char drive[2] = {0};
size_t pos = 0;
drive[0] = list->elems[i].attr.i;
pos += snprintf(drive_string + pos, sizeof(drive_string) - pos, msg_hash_to_str(MSG_DRIVE_NUMBER), i + 1);
pos += snprintf(drive_string + pos, sizeof(drive_string) - pos, ": %s", list->elems[i].data);
menu_entries_append_enum(info->list,
list->elems[i].data,
"",
drive_string,
drive,
0,
MENU_SET_CDROM_LIST,
0, i);

View File

@ -2435,6 +2435,9 @@ enum msg_hash_enums
MENU_LABEL(ENABLE_DEVICE_VIBRATION),
MENU_LABEL(VIDEO_GPU_INDEX),
MSG_DUMPING_DISC,
MSG_DRIVE_NUMBER,
MSG_LAST
};

View File

@ -10328,7 +10328,7 @@ void video_driver_set_title_buf(void)
struct retro_system_info info;
core_get_system_info(&info);
fill_pathname_join_concat_noext(
fill_pathname_join_concat(
video_driver_title_buf,
msg_hash_to_str(MSG_PROGRAM),
" ",

View File

@ -66,6 +66,9 @@
#ifdef HAVE_MENU
#include "../menu/menu_driver.h"
#ifdef HAVE_MENU_WIDGETS
#include "../../menu/widgets/menu_widgets.h"
#endif
#endif
#include "../menu/menu_shader.h"
@ -108,6 +111,34 @@ extern bool discord_is_inited;
typedef struct content_stream content_stream_t;
typedef struct content_information_ctx content_information_ctx_t;
#ifdef HAVE_CDROM
enum cdrom_dump_state
{
DUMP_STATE_TOC_PENDING,
DUMP_STATE_WRITE_CUE,
DUMP_STATE_NEXT_TRACK,
DUMP_STATE_READ_TRACK
};
typedef struct
{
RFILE *file;
RFILE *output_file;
const libretro_vfs_implementation_file *stream;
const cdrom_toc_t *toc;
char cdrom_path[64];
char drive_letter[2];
char title[512];
unsigned char cur_track;
int64_t cur_track_bytes;
int64_t track_written_bytes;
int64_t disc_total_bytes;
int64_t disc_read_bytes;
bool next;
enum cdrom_dump_state state;
} task_cdrom_dump_state_t;
#endif
struct content_stream
{
uint32_t a;
@ -166,6 +197,301 @@ static char pending_subsystem_extensions[PATH_MAX_LENGTH];
#endif
static char *pending_subsystem_roms[RARCH_MAX_SUBSYSTEM_ROMS];
#ifdef HAVE_CDROM
static void task_cdrom_dump_handler(retro_task_t *task)
{
task_cdrom_dump_state_t *state = (task_cdrom_dump_state_t*)task->state;
if (task_get_progress(task) == 100)
{
if (state->file)
{
filestream_close(state->file);
state->file = NULL;
}
if (state->output_file)
{
filestream_close(state->output_file);
state->file = NULL;
}
task_set_finished(task, true);
RARCH_LOG("[CDROM] Dump finished.\n");
return;
}
switch (state->state)
{
case DUMP_STATE_TOC_PENDING:
{
/* open cuesheet file from drive */
char cue_path[PATH_MAX_LENGTH];
cue_path[0] = '\0';
cdrom_device_fillpath(cue_path, sizeof(cue_path), state->drive_letter[0], 0, true);
state->file = filestream_open(cue_path, RETRO_VFS_FILE_ACCESS_READ, 0);
if (!state->file)
{
RARCH_ERR("[CDROM]: Error opening file for reading: %s\n", cue_path);
task_set_progress(task, 100);
task_free_title(task);
task_set_title(task, strdup(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_READ_OPEN_FAILED)));
return;
}
state->state = DUMP_STATE_WRITE_CUE;
break;
}
case DUMP_STATE_WRITE_CUE:
{
/* write cuesheet to a file */
int64_t cue_size = filestream_get_size(state->file);
char *cue_data = (char*)calloc(1, cue_size);
settings_t *settings = config_get_ptr();
char output_file[PATH_MAX_LENGTH];
char cue_filename[PATH_MAX_LENGTH];
output_file[0] = cue_filename[0] = '\0';
filestream_read(state->file, cue_data, cue_size);
state->stream = filestream_get_vfs_handle(state->file);
state->toc = retro_vfs_file_get_cdrom_toc();
if (cdrom_has_atip(state->stream))
RARCH_LOG("[CDROM] This disc is not genuine.\n");
filestream_close(state->file);
output_file[0] = cue_filename[0] = '\0';
snprintf(cue_filename, sizeof(cue_filename), "%s.cue", state->title);
fill_pathname_join(output_file, settings->paths.directory_core_assets,
cue_filename, sizeof(output_file));
{
RFILE *file = filestream_open(output_file, RETRO_VFS_FILE_ACCESS_WRITE, 0);
unsigned char point = 0;
if (!file)
{
RARCH_ERR("[CDROM]: Error opening file for writing: %s\n", output_file);
task_set_progress(task, 100);
task_free_title(task);
task_set_title(task, strdup(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_WRITE_OPEN_FAILED)));
return;
}
for (point = 1; point <= state->toc->num_tracks; point++)
{
const char *track_type = "MODE1/2352";
char track_filename[PATH_MAX_LENGTH];
state->disc_total_bytes += state->toc->track[point - 1].track_bytes;
track_filename[0] = '\0';
if (state->toc->track[point - 1].audio)
track_type = "AUDIO";
else if (state->toc->track[point - 1].mode == 1)
track_type = "MODE1/2352";
else if (state->toc->track[point - 1].mode == 2)
track_type = "MODE2/2352";
snprintf(track_filename, sizeof(track_filename), "%s (Track %02d).bin", state->title, point);
filestream_printf(file, "FILE \"%s\" BINARY\n", track_filename);
filestream_printf(file, " TRACK %02d %s\n", point, track_type);
{
unsigned pregap_lba_len = state->toc->track[point - 1].lba - state->toc->track[point - 1].lba_start;
if (state->toc->track[point - 1].audio && pregap_lba_len > 0)
{
unsigned char min = 0;
unsigned char sec = 0;
unsigned char frame = 0;
cdrom_lba_to_msf(pregap_lba_len, &min, &sec, &frame);
filestream_printf(file, " INDEX 00 00:00:00\n");
filestream_printf(file, " INDEX 01 %02u:%02u:%02u\n", (unsigned)min, (unsigned)sec, (unsigned)frame);
}
else
filestream_printf(file, " INDEX 01 00:00:00\n");
}
}
filestream_close(file);
}
state->file = NULL;
state->state = DUMP_STATE_NEXT_TRACK;
free(cue_data);
return;
}
case DUMP_STATE_NEXT_TRACK:
{
/* no file is open as we either just started or just finished a track, need to start dumping the next track */
state->cur_track++;
/* no more tracks to dump, we're done */
if (state->toc && state->cur_track > state->toc->num_tracks)
{
task_set_progress(task, 100);
return;
}
RARCH_LOG("[CDROM] Dumping track %d...\n", state->cur_track);
memset(state->cdrom_path, 0, sizeof(state->cdrom_path));
cdrom_device_fillpath(state->cdrom_path, sizeof(state->cdrom_path), state->drive_letter[0], state->cur_track, false);
state->track_written_bytes = 0;
state->file = filestream_open(state->cdrom_path, RETRO_VFS_FILE_ACCESS_READ, 0);
/* open a new file for writing for this next track */
if (state->file)
{
settings_t *settings = config_get_ptr();
char output_path[PATH_MAX_LENGTH];
char track_filename[PATH_MAX_LENGTH];
output_path[0] = track_filename[0] = '\0';
snprintf(track_filename, sizeof(track_filename), "%s (Track %02d).bin", state->title, state->cur_track);
state->cur_track_bytes = filestream_get_size(state->file);
fill_pathname_join(output_path, settings->paths.directory_core_assets,
track_filename, sizeof(output_path));
state->output_file = filestream_open(output_path, RETRO_VFS_FILE_ACCESS_WRITE, 0);
if (!state->output_file)
{
RARCH_ERR("[CDROM]: Error opening file for writing: %s\n", output_path);
task_set_progress(task, 100);
task_free_title(task);
task_set_title(task, strdup(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_WRITE_OPEN_FAILED)));
return;
}
}
else
{
RARCH_ERR("[CDROM]: Error opening file for writing: %s\n", state->cdrom_path);
task_set_progress(task, 100);
task_free_title(task);
task_set_title(task, strdup(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_WRITE_OPEN_FAILED)));
return;
}
state->state = DUMP_STATE_READ_TRACK;
return;
}
case DUMP_STATE_READ_TRACK:
{
/* read data from track and write it to a file in chunks */
if (state->cur_track_bytes > state->track_written_bytes)
{
char data[2352 * 2] = {0};
int64_t read_bytes = filestream_read(state->file, data, sizeof(data));
int progress = 0;
if (read_bytes <= 0)
{
task_set_progress(task, 100);
task_free_title(task);
task_set_title(task, strdup("Failed to read from drive. Dump aborted."));
return;
}
state->track_written_bytes += read_bytes;
state->disc_read_bytes += read_bytes;
progress = (state->disc_read_bytes / (double)state->disc_total_bytes) * 100.0;
#ifdef CDROM_DEBUG
RARCH_LOG("[CDROM] Read %" PRId64 " bytes, totalling %" PRId64 " of %" PRId64 " bytes. Progress: %d%%\n", read_bytes, state->track_written_bytes, state->cur_track_bytes, progress);
#endif
if (filestream_write(state->output_file, data, read_bytes) <= 0)
{
task_set_progress(task, 100);
task_free_title(task);
task_set_title(task, strdup("Failed to write to disk. Dump aborted."));
return;
}
task_set_progress(task, progress);
return;
}
else if (state->cur_track_bytes == state->track_written_bytes)
{
/* TODO: FIXME: this stops after only the first track */
if (state->file)
{
filestream_close(state->file);
state->file = NULL;
}
if (state->output_file)
{
filestream_close(state->output_file);
state->file = NULL;
}
state->state = DUMP_STATE_NEXT_TRACK;
return;
}
break;
}
}
}
static void task_cdrom_dump_callback(retro_task_t *task,
void *task_data,
void *user_data, const char *error)
{
task_cdrom_dump_state_t *state = (task_cdrom_dump_state_t*)task->state;
if (state)
free(state);
}
void task_push_cdrom_dump(const char *drive)
{
retro_task_t *task = task_init();
task_cdrom_dump_state_t *state = (task_cdrom_dump_state_t*)calloc(1, sizeof(*state));
state->drive_letter[0] = drive[0];
state->next = true;
state->cur_track = 0;
state->state = DUMP_STATE_TOC_PENDING;
fill_str_dated_filename(state->title, "cdrom", NULL, sizeof(state->title));
task->state = state;
task->handler = task_cdrom_dump_handler;
task->callback = task_cdrom_dump_callback;
task->title = strdup(msg_hash_to_str(MSG_DUMPING_DISC));
RARCH_LOG("[CDROM] Starting disc dump...\n");
task_queue_push(task);
}
#endif
static int64_t content_file_read(const char *path, void **buf, int64_t *length)
{
#ifdef HAVE_COMPRESSION

View File

@ -310,7 +310,7 @@ static bool screenshot_dump(
if (use_thread)
{
retro_task_t *task= task_init();
retro_task_t *task = task_init();
task->type = TASK_TYPE_BLOCKING;
task->state = state;

View File

@ -150,6 +150,10 @@ void input_autoconfigure_joypad_reindex_devices(void);
void set_save_state_in_background(bool state);
#ifdef HAVE_CDROM
void task_push_cdrom_dump(const char *drive);
#endif
extern const char* const input_builtin_autoconfs[];
RETRO_END_DECLS