nkeynes@138: /** nkeynes@561: * $Id$ nkeynes@138: * nkeynes@138: * GD-Rom access functions. nkeynes@138: * nkeynes@138: * Copyright (c) 2005 Nathan Keynes. nkeynes@138: * nkeynes@138: * This program is free software; you can redistribute it and/or modify nkeynes@138: * it under the terms of the GNU General Public License as published by nkeynes@138: * the Free Software Foundation; either version 2 of the License, or nkeynes@138: * (at your option) any later version. nkeynes@138: * nkeynes@138: * This program is distributed in the hope that it will be useful, nkeynes@138: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@138: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@138: * GNU General Public License for more details. nkeynes@138: */ nkeynes@138: nkeynes@138: #include nkeynes@1097: #include nkeynes@1097: #include nkeynes@237: #include nkeynes@168: #include nkeynes@1023: #include nkeynes@678: #include nkeynes@1074: #include nkeynes@138: #include "gdrom/ide.h" nkeynes@138: #include "gdrom/gdrom.h" nkeynes@143: #include "gdrom/packet.h" nkeynes@1023: #include "bootstrap.h" nkeynes@1097: #include "drivers/cdrom/cdrom.h" nkeynes@138: nkeynes@1097: #define GDROM_LBA_OFFSET 150 nkeynes@138: nkeynes@678: DEFINE_HOOK( gdrom_disc_change_hook, gdrom_disc_change_hook_t ) nkeynes@678: nkeynes@1097: static void gdrom_fire_disc_changed( cdrom_disc_t disc ) nkeynes@678: { nkeynes@678: CALL_HOOKS( gdrom_disc_change_hook, disc, disc == NULL ? NULL : disc->name ); nkeynes@678: } nkeynes@678: nkeynes@1097: gboolean gdrom_disc_read_title( cdrom_disc_t disc, char *title, size_t titlelen ); nkeynes@138: nkeynes@1097: struct gdrom_drive { nkeynes@1097: cdrom_disc_t disc; nkeynes@1097: int boot_track; nkeynes@1097: char title[129]; nkeynes@1097: } gdrom_drive; nkeynes@1023: nkeynes@1097: void gdrom_mount_disc( cdrom_disc_t disc ) nkeynes@138: { nkeynes@1097: if( disc != gdrom_drive.disc ) { nkeynes@1097: cdrom_disc_unref(gdrom_drive.disc); nkeynes@1097: gdrom_drive.disc = disc; nkeynes@1097: cdrom_disc_ref(disc); nkeynes@1097: gdrom_disc_read_title( disc, gdrom_drive.title, sizeof(gdrom_drive.title) ); nkeynes@678: gdrom_fire_disc_changed( disc ); nkeynes@678: } nkeynes@138: } nkeynes@138: nkeynes@433: gboolean gdrom_mount_image( const gchar *filename ) nkeynes@138: { nkeynes@1097: cdrom_disc_t disc = cdrom_disc_open(filename, NULL); nkeynes@695: if( disc != NULL ) { nkeynes@678: gdrom_mount_disc( disc ); nkeynes@678: return TRUE; nkeynes@433: } nkeynes@433: return FALSE; nkeynes@138: } nkeynes@138: nkeynes@138: void gdrom_unmount_disc( ) nkeynes@138: { nkeynes@1097: if( gdrom_drive.disc != NULL ) { nkeynes@1097: cdrom_disc_unref(gdrom_drive.disc); nkeynes@678: gdrom_fire_disc_changed(NULL); nkeynes@1097: gdrom_drive.disc = NULL; nkeynes@138: } nkeynes@138: } nkeynes@138: nkeynes@1097: cdrom_disc_t gdrom_get_current_disc() nkeynes@464: { nkeynes@1097: return gdrom_drive.disc; nkeynes@464: } nkeynes@613: nkeynes@678: const gchar *gdrom_get_current_disc_name() nkeynes@678: { nkeynes@1097: if( gdrom_drive.disc == NULL ) { nkeynes@678: return NULL; nkeynes@678: } else { nkeynes@1097: return gdrom_drive.disc->name; nkeynes@678: } nkeynes@678: } nkeynes@678: nkeynes@837: const gchar *gdrom_get_current_disc_title() nkeynes@837: { nkeynes@1097: if( gdrom_drive.disc == NULL || gdrom_drive.title[0] == '\0' ) { nkeynes@837: return NULL; nkeynes@837: } else { nkeynes@1097: return gdrom_drive.title; nkeynes@837: } nkeynes@837: } nkeynes@837: nkeynes@1097: #define CHECK_DISC() do { \ nkeynes@1097: if( gdrom_drive.disc == NULL || gdrom_drive.disc->disc_type == CDROM_DISC_NONE ) { return CDROM_ERROR_NODISC; } \ nkeynes@1097: } while(0) nkeynes@1097: nkeynes@1097: cdrom_error_t gdrom_check_media( ) nkeynes@613: { nkeynes@1097: CHECK_DISC(); nkeynes@1097: return CDROM_ERROR_OK; nkeynes@613: } nkeynes@837: nkeynes@1097: cdrom_error_t gdrom_read_toc( unsigned char *buf ) nkeynes@1023: { nkeynes@1023: struct gdrom_toc *toc = (struct gdrom_toc *)buf; nkeynes@1023: int i; nkeynes@1023: nkeynes@1097: CHECK_DISC(); nkeynes@1097: cdrom_disc_t disc = gdrom_drive.disc; nkeynes@1023: nkeynes@1023: for( i=0; itrack_count; i++ ) { nkeynes@1097: toc->track[i] = htonl( disc->track[i].lba+GDROM_LBA_OFFSET ) | disc->track[i].flags; nkeynes@1023: } nkeynes@1023: toc->first = 0x0100 | disc->track[0].flags; nkeynes@1023: toc->last = (disc->track_count<<8) | disc->track[i-1].flags; nkeynes@1097: toc->leadout = htonl(disc->leadout+GDROM_LBA_OFFSET) | nkeynes@1023: disc->track[i-1].flags; nkeynes@1023: for( ;i<99; i++ ) nkeynes@1023: toc->track[i] = 0xFFFFFFFF; nkeynes@1023: return PKT_ERR_OK; nkeynes@1023: } nkeynes@1023: nkeynes@1097: cdrom_error_t gdrom_read_session( int session, unsigned char *buf ) nkeynes@1023: { nkeynes@1097: cdrom_lba_t lba; nkeynes@1097: CHECK_DISC(); nkeynes@1023: nkeynes@1023: buf[0] = 0x01; /* Disc status? */ nkeynes@1023: buf[1] = 0; nkeynes@1023: nkeynes@1097: nkeynes@1023: if( session == 0 ) { nkeynes@1097: buf[2] = gdrom_drive.disc->session_count; nkeynes@1097: lba = gdrom_drive.disc->leadout + GDROM_LBA_OFFSET; nkeynes@1023: } else { nkeynes@1097: cdrom_track_t track = cdrom_disc_get_session( gdrom_drive.disc, session ); nkeynes@1097: if( track == NULL ) nkeynes@1097: return CDROM_ERROR_BADFIELD; nkeynes@1097: nkeynes@1097: buf[2] = track->trackno; nkeynes@1097: lba = track->lba + GDROM_LBA_OFFSET; nkeynes@1023: } nkeynes@1097: buf[3] = (lba >> 16) & 0xFF; nkeynes@1097: buf[4] = (lba >> 8) & 0xFF; nkeynes@1097: buf[5] = lba & 0xFF; nkeynes@1097: return CDROM_ERROR_OK; nkeynes@1023: } nkeynes@1023: nkeynes@1097: cdrom_error_t gdrom_read_short_status( cdrom_lba_t lba, unsigned char *buf ) nkeynes@1023: { nkeynes@1097: cdrom_lba_t real_lba = lba - GDROM_LBA_OFFSET; nkeynes@1097: CHECK_DISC(); nkeynes@1023: nkeynes@1097: cdrom_track_t track = cdrom_disc_get_track_by_lba( gdrom_drive.disc, real_lba ); nkeynes@1097: if( track == NULL ) { nkeynes@1097: track = cdrom_disc_get_track( gdrom_drive.disc, 1 ); nkeynes@1097: if( track == NULL ) nkeynes@1097: return CDROM_ERROR_NODISC; nkeynes@1023: lba = 150; nkeynes@1023: } nkeynes@1097: uint32_t offset = real_lba - track->lba; nkeynes@1023: buf[0] = 0x00; nkeynes@1023: buf[1] = 0x15; /* audio status ? */ nkeynes@1023: buf[2] = 0x00; nkeynes@1023: buf[3] = 0x0E; nkeynes@1023: buf[4] = track->flags; nkeynes@1097: buf[5] = track->trackno; nkeynes@1023: buf[6] = 0x01; /* ?? */ nkeynes@1023: buf[7] = (offset >> 16) & 0xFF; nkeynes@1023: buf[8] = (offset >> 8) & 0xFF; nkeynes@1023: buf[9] = offset & 0xFF; nkeynes@1023: buf[10] = 0; nkeynes@1023: buf[11] = (lba >> 16) & 0xFF; nkeynes@1023: buf[12] = (lba >> 8) & 0xFF; nkeynes@1023: buf[13] = lba & 0xFF; nkeynes@1023: return PKT_ERR_OK; nkeynes@1023: } nkeynes@1023: nkeynes@1097: int gdrom_get_drive_status( ) nkeynes@1023: { nkeynes@1097: if( gdrom_drive.disc == NULL ) { nkeynes@1097: return CDROM_DISC_NONE; nkeynes@1023: } nkeynes@1023: nkeynes@1097: if( cdrom_disc_check_media(gdrom_drive.disc) == CDROM_DISC_NONE ) { nkeynes@1097: return CDROM_DISC_NONE; nkeynes@1023: } else { nkeynes@1097: return gdrom_drive.disc->disc_type | IDE_DISC_READY; nkeynes@1023: } nkeynes@1023: } nkeynes@1023: nkeynes@1097: cdrom_error_t gdrom_play_audio( cdrom_lba_t lba, cdrom_count_t count ) nkeynes@1097: { nkeynes@1097: CHECK_DISC(); nkeynes@1097: if( gdrom_drive.disc->play_audio ) { nkeynes@1097: return gdrom_drive.disc->play_audio( gdrom_drive.disc, lba - GDROM_LBA_OFFSET, count ); nkeynes@1097: } nkeynes@1097: return CDROM_ERROR_BADFIELD; nkeynes@1097: } nkeynes@1097: nkeynes@1097: /* Parse CD read */ nkeynes@1097: #define READ_CD_MODE(x) ((x)&0x0E) nkeynes@1097: #define READ_CD_MODE_ANY 0x00 nkeynes@1097: #define READ_CD_MODE_CDDA 0x02 nkeynes@1097: #define READ_CD_MODE_1 0x04 nkeynes@1097: #define READ_CD_MODE_2 0x06 nkeynes@1097: #define READ_CD_MODE_2_FORM_1 0x08 nkeynes@1097: #define READ_CD_MODE_2_FORM_2 0x0A nkeynes@1097: nkeynes@1097: #define READ_CD_CHANNELS(x) ((x)&0xF0) nkeynes@1097: #define READ_CD_HEADER(x) ((x)&0x80) nkeynes@1097: #define READ_CD_SUBHEAD(x) ((x)&0x40) nkeynes@1097: #define READ_CD_DATA(x) ((x)&0x20) nkeynes@1097: #define READ_CD_RAW(x) ((x)&0x10) nkeynes@1097: nkeynes@1097: nkeynes@1097: cdrom_error_t gdrom_read_cd( cdrom_lba_t lba, cdrom_count_t count, nkeynes@1097: unsigned mode, unsigned char *buf, size_t *length ) nkeynes@1097: { nkeynes@1097: cdrom_lba_t real_lba = lba - 150; nkeynes@1097: cdrom_read_mode_t real_mode = 0; nkeynes@1097: nkeynes@1097: CHECK_DISC(); nkeynes@1097: nkeynes@1097: /* Translate GDROM read mode into standard MMC read mode */ nkeynes@1097: if( READ_CD_RAW(mode) ) { nkeynes@1097: real_mode = CDROM_READ_RAW; nkeynes@1097: } else { nkeynes@1097: if( READ_CD_HEADER(mode) ) { nkeynes@1097: real_mode = CDROM_READ_HEADER|CDROM_READ_SYNC; nkeynes@1097: } nkeynes@1097: if( READ_CD_SUBHEAD(mode) ) { nkeynes@1097: real_mode |= CDROM_READ_SUBHEADER; nkeynes@1097: } nkeynes@1097: if( READ_CD_DATA(mode) ) { nkeynes@1097: real_mode |= CDROM_READ_DATA; nkeynes@1097: } nkeynes@1097: } nkeynes@1097: nkeynes@1097: if( READ_CD_MODE(mode) == 0x0C ) nkeynes@1097: real_mode |= CDROM_READ_MODE2; nkeynes@1097: else nkeynes@1097: real_mode |= (READ_CD_MODE(mode)<<1); nkeynes@1097: nkeynes@1097: return cdrom_disc_read_sectors( gdrom_drive.disc, real_lba, count, real_mode, buf, length ); nkeynes@1097: } nkeynes@1097: nkeynes@1097: void gdrom_run_slice( uint32_t nanosecs ) nkeynes@1097: { nkeynes@1097: nkeynes@1097: } nkeynes@1097: nkeynes@1097: nkeynes@1097: cdrom_track_t gdrom_disc_get_boot_track( cdrom_disc_t disc ) { nkeynes@1097: int i, boot_track = -1; nkeynes@1097: if( disc != NULL && disc->track_count > 0 ) { nkeynes@1097: int last_session = disc->track[disc->track_count-1].sessionno; nkeynes@1097: if( last_session == 1 ) nkeynes@1097: return NULL; nkeynes@1097: for( i=disc->track_count-1; i>=0 && disc->track[i].sessionno == last_session; i-- ) { nkeynes@1097: if( disc->track[i].flags & TRACK_FLAG_DATA ) { nkeynes@1097: boot_track = i; nkeynes@1097: } nkeynes@1097: } nkeynes@1097: } nkeynes@1097: return &disc->track[boot_track]; nkeynes@1097: } nkeynes@1097: nkeynes@1023: /** nkeynes@1023: * Check the disc for a useable DC bootstrap, and update the disc nkeynes@1097: * with the title accordingly. Otherwise set the title to the empty string. nkeynes@1023: * @return TRUE if we found a bootstrap, otherwise FALSE. nkeynes@1023: */ nkeynes@1097: gboolean gdrom_disc_read_title( cdrom_disc_t disc, char *title, size_t titlelen ) { nkeynes@1097: cdrom_track_t boot_track = gdrom_disc_get_boot_track(disc); nkeynes@1097: int i; nkeynes@1097: if( boot_track != NULL ) { nkeynes@1097: unsigned char boot_sector[CDROM_MAX_SECTOR_SIZE]; nkeynes@1097: size_t length = sizeof(boot_sector); nkeynes@1097: if( cdrom_disc_read_sectors( disc, boot_track->lba, 1, CDROM_READ_DATA|CDROM_READ_MODE2_FORM1, nkeynes@1097: boot_sector, &length ) == CDROM_ERROR_OK ) { nkeynes@1097: if( memcmp( boot_sector, "SEGA SEGAKATANA SEGA ENTERPRISES", 32) == 0 ) { nkeynes@1097: /* Got magic */ nkeynes@1097: dc_bootstrap_head_t bootstrap = (dc_bootstrap_head_t)boot_sector; nkeynes@1097: for( i=128; i>0; i-- ) { nkeynes@1097: if( !isspace(bootstrap->product_name[i-1]) ) nkeynes@1097: break; nkeynes@1097: } nkeynes@1097: if( i >= titlelen ) nkeynes@1097: i = (titlelen-1); nkeynes@1097: memcpy( title, bootstrap->product_name, i ); nkeynes@1097: title[i] = '\0'; nkeynes@1023: } nkeynes@1097: bootstrap_dump(boot_sector, FALSE); nkeynes@1097: return TRUE; nkeynes@1023: } nkeynes@1023: } nkeynes@1097: title[0] = '\0'; nkeynes@1023: return FALSE; nkeynes@1023: } nkeynes@1097: