mirror of
https://github.com/libretro/RetroArch
synced 2025-01-29 18:32:44 +00:00
cdrom: add Disc Information details
This commit is contained in:
parent
ad5773a031
commit
30c3b1ea9d
@ -188,6 +188,7 @@ OBJ += frontend/frontend.o \
|
||||
$(LIBRETRO_COMM_DIR)/streams/interface_stream.o \
|
||||
$(LIBRETRO_COMM_DIR)/streams/memory_stream.o \
|
||||
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.o \
|
||||
$(LIBRETRO_COMM_DIR)/media/media_detect_cd.o \
|
||||
$(LIBRETRO_COMM_DIR)/lists/string_list.o \
|
||||
$(LIBRETRO_COMM_DIR)/string/stdstring.o \
|
||||
$(LIBRETRO_COMM_DIR)/memmap/memalign.o \
|
||||
|
56
libretro-common/include/media/media_detect_cd.h
Normal file
56
libretro-common/include/media/media_detect_cd.h
Normal file
@ -0,0 +1,56 @@
|
||||
/* Copyright (C) 2010-2019 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (media_detect_cd.h).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __LIBRETRO_SDK_MEDIA_DETECT_CD_H
|
||||
#define __LIBRETRO_SDK_MEDIA_DETECT_CD_H
|
||||
|
||||
#include <retro_common_api.h>
|
||||
#include <boolean.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
enum media_detect_cd_system
|
||||
{
|
||||
MEDIA_CD_SYSTEM_MEGA_CD,
|
||||
MEDIA_CD_SYSTEM_SATURN,
|
||||
MEDIA_CD_SYSTEM_DREAMCAST,
|
||||
MEDIA_CD_SYSTEM_PSX,
|
||||
MEDIA_CD_SYSTEM_3DO
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char title[256];
|
||||
char system[128];
|
||||
char region[128];
|
||||
char serial[64];
|
||||
char maker[64];
|
||||
char version[32];
|
||||
char release_date[32];
|
||||
enum media_detect_cd_system system_id;
|
||||
} media_detect_cd_info_t;
|
||||
|
||||
bool media_detect_cd_info(const char *path, media_detect_cd_info_t *info);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
197
libretro-common/media/media_detect_cd.c
Normal file
197
libretro-common/media/media_detect_cd.c
Normal file
@ -0,0 +1,197 @@
|
||||
/* Copyright (C) 2010-2019 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (media_detect_cd.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <media/media_detect_cd.h>
|
||||
#include <streams/file_stream.h>
|
||||
#include <string/stdstring.h>
|
||||
|
||||
static bool media_skip_spaces(const char **buf, size_t len)
|
||||
{
|
||||
bool found = false;
|
||||
int i;
|
||||
|
||||
if (!buf || !*buf)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if ((*buf)[i] == ' ')
|
||||
continue;
|
||||
|
||||
*buf += i;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool media_detect_cd_info(const char *path, media_detect_cd_info_t *info)
|
||||
{
|
||||
RFILE *file;
|
||||
|
||||
if (string_is_empty(path) || !info)
|
||||
return false;
|
||||
|
||||
file = filestream_open(path, RETRO_VFS_FILE_ACCESS_READ, 0);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
printf("[MEDIA] Could not open path for reading: %s\n", path);
|
||||
fflush(stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
unsigned offset = 0;
|
||||
unsigned sector_size = 0;
|
||||
unsigned buf_size = 17 * 2352;
|
||||
char *buf = (char*)calloc(1, buf_size);
|
||||
|
||||
if (!buf)
|
||||
return false;
|
||||
|
||||
int64_t read_bytes = filestream_read(file, buf, buf_size);
|
||||
|
||||
if (read_bytes != buf_size)
|
||||
{
|
||||
printf("[MEDIA] Could not read from media.\n");
|
||||
fflush(stdout);
|
||||
filestream_close(file);
|
||||
free(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 12-byte sync field at the start of every sector, common to both mode1 and mode2 data tracks
|
||||
* (when at least sync data is requested). This is a CD-ROM standard feature and not specific to any game devices,
|
||||
* and as such should not be part of any system-specific detection or "magic" bytes.
|
||||
* Depending on what parts of a sector were requested from the disc, the user data might start at
|
||||
* byte offset 0, 4, 8, 12, 16 or 24. Cue sheets only specify the total number of bytes requested from the sectors
|
||||
* of a track (like 2048 or 2352) and it is then assumed based on the size/mode as to what fields are present. */
|
||||
if (!memcmp(buf, "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", 12))
|
||||
{
|
||||
/* Assume track data contains all fields. */
|
||||
sector_size = 2352;
|
||||
|
||||
if (buf[15] == 2)
|
||||
{
|
||||
/* assume Mode 2 formed (formless is rarely used) */
|
||||
offset = 24;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* assume Mode 1 */
|
||||
offset = 16;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Assume sectors only contain user data instead. */
|
||||
offset = 0;
|
||||
sector_size = 2048;
|
||||
}
|
||||
|
||||
if (!memcmp(buf + offset, "SEGADISCSYSTEM", strlen("SEGADISCSYSTEM")))
|
||||
{
|
||||
const char *title_pos;
|
||||
const char *serial_pos;
|
||||
bool title_found = false;
|
||||
|
||||
/* All discs currently in Redump for MCD start with SEGADISCSYSTEM. There are other strings mentioned elsewhere online,
|
||||
* but I have not seen any real examples of them. */
|
||||
info->system_id = MEDIA_CD_SYSTEM_MEGA_CD;
|
||||
|
||||
strlcpy(info->system, "Sega CD / Mega CD", sizeof(info->system));
|
||||
|
||||
title_pos = buf + offset + 0x150;
|
||||
|
||||
if (media_skip_spaces(&title_pos, 48))
|
||||
memcpy(info->title, title_pos, 48 - (title_pos - (buf + offset + 0x150)));
|
||||
else
|
||||
strlcpy(info->title, "N/A", sizeof(info->title));
|
||||
|
||||
serial_pos = buf + offset + 0x183;
|
||||
|
||||
if (media_skip_spaces(&serial_pos, 8))
|
||||
memcpy(info->serial, serial_pos, 8 - (serial_pos - (buf + offset + 0x183)));
|
||||
else
|
||||
strlcpy(info->serial, "N/A", sizeof(info->title));
|
||||
}
|
||||
else if (!memcmp(buf + offset, "SEGA SEGASATURN", strlen("SEGA SEGASATURN")))
|
||||
{
|
||||
const char *title_pos;
|
||||
const char *serial_pos;
|
||||
bool title_found = false;
|
||||
|
||||
info->system_id = MEDIA_CD_SYSTEM_SATURN;
|
||||
|
||||
strlcpy(info->system, "Sega Saturn", sizeof(info->system));
|
||||
|
||||
title_pos = buf + offset + 0x60;
|
||||
|
||||
if (media_skip_spaces(&title_pos, 112))
|
||||
memcpy(info->title, title_pos, 112 - (title_pos - (buf + offset + 0x60)));
|
||||
else
|
||||
strlcpy(info->title, "N/A", sizeof(info->title));
|
||||
|
||||
serial_pos = buf + offset + 0x20;
|
||||
|
||||
if (media_skip_spaces(&serial_pos, 10))
|
||||
memcpy(info->serial, serial_pos, 10 - (serial_pos - (buf + offset + 0x20)));
|
||||
else
|
||||
strlcpy(info->serial, "N/A", sizeof(info->title));
|
||||
}
|
||||
/* Primary Volume Descriptor fields of ISO9660 */
|
||||
else if (!memcmp(buf + offset + (16 * sector_size), "\1CD001\1\0PLAYSTATION", 19))
|
||||
{
|
||||
const char *title_pos;
|
||||
const char *serial_pos;
|
||||
bool title_found = false;
|
||||
|
||||
info->system_id = MEDIA_CD_SYSTEM_PSX;
|
||||
|
||||
strlcpy(info->system, "Sony PlayStation", sizeof(info->system));
|
||||
|
||||
title_pos = buf + offset + (16 * sector_size) + 40;
|
||||
|
||||
if (media_skip_spaces(&title_pos, 32))
|
||||
memcpy(info->title, title_pos, 32 - (title_pos - (buf + offset + (16 * sector_size) + 40)));
|
||||
else
|
||||
strlcpy(info->title, "N/A", sizeof(info->title));
|
||||
}
|
||||
else if (!memcmp(buf + offset, "\x01\x5a\x5a\x5a\x5a\x5a\x01\x00\x00\x00\x00\x00", 12))
|
||||
{
|
||||
info->system_id = MEDIA_CD_SYSTEM_3DO;
|
||||
|
||||
strlcpy(info->system, "3DO", sizeof(info->system));
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
filestream_close(file);
|
||||
|
||||
return true;
|
||||
}
|
@ -61,7 +61,8 @@
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CDROM
|
||||
#include <cdrom/cdrom.h>
|
||||
#include <vfs/vfs_implementation_cdrom.h>
|
||||
#include <media/media_detect_cd.h>
|
||||
#endif
|
||||
|
||||
#include "menu_cbs.h"
|
||||
@ -4951,8 +4952,188 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
{
|
||||
#ifdef HAVE_CDROM
|
||||
case DISPLAYLIST_CDROM_DETAIL_INFO:
|
||||
{
|
||||
media_detect_cd_info_t cd_info = {0};
|
||||
char file_path[PATH_MAX_LENGTH];
|
||||
RFILE *file;
|
||||
char drive = info->path[0];
|
||||
bool atip = false;
|
||||
|
||||
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
|
||||
count = 0;
|
||||
file_path[0] = '\0';
|
||||
|
||||
if (cdrom_drive_has_media(drive))
|
||||
{
|
||||
cdrom_device_fillpath(file_path, sizeof(file_path), drive, 0, true);
|
||||
|
||||
/* opening the cue triggers storing of TOC info internally */
|
||||
file = filestream_open(file_path, RETRO_VFS_FILE_ACCESS_READ, 0);
|
||||
|
||||
if (file)
|
||||
{
|
||||
const cdrom_toc_t *toc = retro_vfs_file_get_cdrom_toc();
|
||||
|
||||
atip = cdrom_has_atip(filestream_get_vfs_handle(file));
|
||||
|
||||
filestream_close(file);
|
||||
|
||||
/* open first track */
|
||||
cdrom_device_fillpath(file_path, sizeof(file_path), drive, 1, false);
|
||||
|
||||
if (media_detect_cd_info(file_path, &cd_info))
|
||||
{
|
||||
if (!string_is_empty(cd_info.title))
|
||||
{
|
||||
char title[256];
|
||||
|
||||
count++;
|
||||
|
||||
title[0] = '\0';
|
||||
|
||||
strlcpy(title, "Title: ", sizeof(title));
|
||||
strlcat(title, cd_info.title, sizeof(title));
|
||||
|
||||
menu_entries_append_enum(info->list,
|
||||
title,
|
||||
"",
|
||||
0,
|
||||
FILE_TYPE_NONE, 0, 0);
|
||||
}
|
||||
|
||||
if (!string_is_empty(cd_info.system))
|
||||
{
|
||||
char system[256];
|
||||
|
||||
count++;
|
||||
|
||||
system[0] = '\0';
|
||||
|
||||
strlcpy(system, "System: ", sizeof(system));
|
||||
strlcat(system, cd_info.system, sizeof(system));
|
||||
|
||||
menu_entries_append_enum(info->list,
|
||||
system,
|
||||
"",
|
||||
0,
|
||||
FILE_TYPE_NONE, 0, 0);
|
||||
}
|
||||
|
||||
if (!string_is_empty(cd_info.serial))
|
||||
{
|
||||
char serial[256];
|
||||
|
||||
count++;
|
||||
|
||||
serial[0] = '\0';
|
||||
|
||||
strlcpy(serial, "Serial#: ", sizeof(serial));
|
||||
strlcat(serial, cd_info.serial, sizeof(serial));
|
||||
|
||||
menu_entries_append_enum(info->list,
|
||||
serial,
|
||||
"",
|
||||
0,
|
||||
FILE_TYPE_NONE, 0, 0);
|
||||
}
|
||||
|
||||
{
|
||||
char atip_string[16] = {"Genuine CD: "};
|
||||
|
||||
if (atip)
|
||||
strlcat(atip_string, "No", sizeof(atip_string));
|
||||
else
|
||||
strlcat(atip_string, "Yes", sizeof(atip_string));
|
||||
|
||||
menu_entries_append_enum(info->list,
|
||||
atip_string,
|
||||
"",
|
||||
0,
|
||||
FILE_TYPE_NONE, 0, 0);
|
||||
}
|
||||
|
||||
{
|
||||
char tracks_string[32] = {"Number of tracks: "};
|
||||
|
||||
snprintf(tracks_string + strlen(tracks_string), sizeof(tracks_string) - strlen(tracks_string), "%d", toc->num_tracks);
|
||||
|
||||
menu_entries_append_enum(info->list,
|
||||
tracks_string,
|
||||
"",
|
||||
0,
|
||||
FILE_TYPE_NONE, 0, 0);
|
||||
}
|
||||
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < toc->num_tracks; i++)
|
||||
{
|
||||
char track_string[16] = {"Track "};
|
||||
char mode_string[16] = {" - Mode: "};
|
||||
char size_string[32] = {" - Size: "};
|
||||
char length_string[32] = {" - Length: "};
|
||||
|
||||
snprintf(track_string + strlen(track_string), sizeof(track_string) - strlen(track_string), "%d:", i + 1);
|
||||
|
||||
menu_entries_append_enum(info->list,
|
||||
track_string,
|
||||
"",
|
||||
0,
|
||||
FILE_TYPE_NONE, 0, 0);
|
||||
|
||||
if (toc->track[i].audio)
|
||||
snprintf(mode_string + strlen(mode_string), sizeof(mode_string) - strlen(mode_string), "Audio");
|
||||
else
|
||||
snprintf(mode_string + strlen(mode_string), sizeof(mode_string) - strlen(mode_string), "Mode %d", toc->track[i].mode);
|
||||
|
||||
menu_entries_append_enum(info->list,
|
||||
mode_string,
|
||||
"",
|
||||
0,
|
||||
FILE_TYPE_NONE, 0, 0);
|
||||
|
||||
snprintf(size_string + strlen(size_string), sizeof(size_string) - strlen(size_string), "%.1f MB", toc->track[i].track_bytes / 1000.0 / 1000.0);
|
||||
|
||||
menu_entries_append_enum(info->list,
|
||||
size_string,
|
||||
"",
|
||||
0,
|
||||
FILE_TYPE_NONE, 0, 0);
|
||||
|
||||
{
|
||||
unsigned char min = 0;
|
||||
unsigned char sec = 0;
|
||||
unsigned char frame = 0;
|
||||
|
||||
cdrom_lba_to_msf(toc->track[i].track_size, &min, &sec, &frame);
|
||||
|
||||
snprintf(length_string + strlen(length_string), sizeof(length_string) - strlen(length_string), "%02d:%02d:%02d", min, sec, frame);
|
||||
|
||||
menu_entries_append_enum(info->list,
|
||||
length_string,
|
||||
"",
|
||||
0,
|
||||
FILE_TYPE_NONE, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
RARCH_ERR("[CDROM]: Could not detect any disc info.\n");
|
||||
}
|
||||
else
|
||||
RARCH_ERR("[CDROM]: Error opening file for reading: %s\n", file_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
RARCH_LOG("[CDROM]: No media is inserted or drive is not ready.\n");
|
||||
|
||||
runloop_msg_queue_push(
|
||||
msg_hash_to_str(MSG_NO_DISC_INSERTED),
|
||||
1, 100, true,
|
||||
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
menu_entries_append_enum(info->list,
|
||||
@ -4965,6 +5146,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
info->need_refresh = true;
|
||||
info->need_clear = true;
|
||||
break;
|
||||
}
|
||||
case DISPLAYLIST_DISC_INFO:
|
||||
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
|
||||
count = menu_displaylist_parse_disc_info(info,
|
||||
|
Loading…
x
Reference in New Issue
Block a user