Files
Main_MiSTer/support/3do/3docdd.cpp
Sergiy Dvodnenko 8dcef7213b 3DO: auto-loading of kanji.rom, save support, send the volume header to FPGA. (#1142)
* 3DO: auto-loading of kanji.rom, save support.

* 3DO: send the volume header to FPGA.
2026-03-30 18:43:17 +08:00

737 lines
17 KiB
C++

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <time.h>
#include "3do.h"
#include "../chd/mister_chd.h"
p3docdd_t p3docdd;
p3docdd_t::p3docdd_t() {
loaded = 0;
state = P3DO_Open;
lid_open = false;
stop_pend = false;
read_pend = false;
read_toc = false;
track = 0;
lba = 0;
speed = 0;
chd_hunkbuf = NULL;
chd_hunknum = -1;
SendData = NULL;
}
static int sgets(char *out, int sz, char **in)
{
*out = 0;
do
{
char *instr = *in;
int cnt = 0;
while (*instr && *instr != 10)
{
if (*instr == 13)
{
instr++;
continue;
}
if (cnt < sz - 1)
{
out[cnt++] = *instr;
out[cnt] = 0;
}
instr++;
}
if (*instr == 10) instr++;
*in = instr;
} while (!*out && **in);
return *out;
}
int p3docdd_t::LoadCUE(const char* filename) {
static char fname[1024 + 10];
static char line[128];
char *ptr, *lptr;
static char cue[100 * 1024];
int new_file = 0;
int file_size = 0;
strcpy(fname, filename);
memset(cue, 0, sizeof(cue));
if (!FileLoad(fname, cue, sizeof(cue) - 1)) return 1;
#ifdef P3DO_DEBUG
printf("\x1b[32m3DO: Open CUE: %s\n\x1b[0m", fname);
#endif // P3DO_DEBUG
this->toc.last = -1;
int idx, mm, ss, bb, pregap = 0;
char *buf = cue;
while (sgets(line, sizeof(line), &buf))
{
lptr = line;
while (*lptr == 0x20) lptr++;
/* decode FILE commands */
if (!(memcmp(lptr, "FILE", 4)))
{
if (this->toc.last == 99) break;
ptr = fname + strlen(fname) - 1;
while ((ptr - fname) && (*ptr != '/') && (*ptr != '\\')) ptr--;
if (ptr - fname) ptr++;
lptr += 4;
while (*lptr == 0x20) lptr++;
if (*lptr == '\"')
{
lptr++;
while ((*lptr != '\"') && (lptr <= (line + 128)) && (ptr < (fname + 1023)))
*ptr++ = *lptr++;
}
else
{
while ((*lptr != 0x20) && (lptr <= (line + 128)) && (ptr < (fname + 1023)))
*ptr++ = *lptr++;
}
*ptr = 0;
if (!FileOpen(&this->toc.tracks[this->toc.last + 1].f, fname)) return -1;
FileSeek(&this->toc.tracks[this->toc.last + 1].f, 0, SEEK_SET);
file_size = this->toc.tracks[this->toc.last + 1].f.size;
#ifdef P3DO_DEBUG
printf("\x1b[32m3DO: Open track file: %s\n\x1b[0m", fname);
#endif // P3DO_DEBUG
pregap = 0;
this->toc.tracks[this->toc.last + 1].offset = 0;
if (!strstr(lptr, "BINARY") && !strstr(lptr, "MOTOROLA") && !strstr(lptr, "WAVE"))
{
FileClose(&this->toc.tracks[this->toc.last + 1].f);
#ifdef P3DO_DEBUG
printf("\x1b[32m3DO: unsupported file: %s\n\x1b[0m", fname);
#endif // P3DO_DEBUG
return -1;
}
}
/* decode TRACK commands */
else if ((sscanf(lptr, "TRACK %02d %*s", &bb)) || (sscanf(lptr, "TRACK %d %*s", &bb)))
{
if (this->toc.last == 99) break;
this->toc.last++;
if (bb != (this->toc.last + 1))
{
FileClose(&this->toc.tracks[this->toc.last].f);
#ifdef P3DO_DEBUG
printf("\x1b[32m3DO: missing tracks: %s\n\x1b[0m", fname);
#endif // P3DO_DEBUG
break;
}
if (strstr(lptr, "MODE1/2048"))
{
this->sectorSize = 2048;
this->toc.tracks[this->toc.last].type = TT_MODE1;
}
else if (strstr(lptr, "MODE1/2352"))
{
this->sectorSize = 2352;
this->toc.tracks[this->toc.last].type = TT_MODE1;
//FileSeek(&this->toc.tracks[0].f, 0x10, SEEK_SET);
}
else if (strstr(lptr, "MODE2/2352"))
{
this->sectorSize = 2352;
this->toc.tracks[this->toc.last].type = TT_MODE2;
//FileSeek(&this->toc.tracks[0].f, 0x10, SEEK_SET);
}
if (!this->toc.last)
{
/*if (strstr(lptr, "MODE1/2048"))
{
this->sectorSize = 2048;
this->toc.tracks[0].type = 1;
}
else if (strstr(lptr, "MODE1/2352") || strstr(lptr, "MODE2/2352"))
{
this->sectorSize = 2352;
this->toc.tracks[0].type = 1;
FileSeek(&this->toc.tracks[0].f, 0x10, SEEK_SET);
}
if (this->sectorSize)
{
this->toc.tracks[0].type = 1;
FileReadAdv(&this->toc.tracks[0].f, header, 0x210);
FileSeek(&this->toc.tracks[0].f, 0, SEEK_SET);
}*/
}
else
{
if (!this->toc.tracks[this->toc.last].f.opened())
{
this->toc.tracks[this->toc.last - 1].end = 0;
}
}
new_file = 1;
if (!this->toc.tracks[this->toc.last].f.opened())
{
FileOpen(&this->toc.tracks[this->toc.last].f, fname);
new_file = 0;
}
#ifdef P3DO_DEBUG
printf("\x1b[32m3DO: track = %u, type = %u\n\x1b[0m", this->toc.last + 1, (int)this->toc.tracks[this->toc.last].type);
#endif // P3DO_DEBUG
}
/* decode PREGAP commands */
else if (sscanf(lptr, "PREGAP %02d:%02d:%02d", &mm, &ss, &bb) == 3)
{
this->toc.tracks[this->toc.last].pregap = bb + ss * 75 + mm * 60 * 75;
pregap += this->toc.tracks[this->toc.last].pregap;
}
/* decode INDEX commands */
else if ((sscanf(lptr, "INDEX %02d %02d:%02d:%02d", &idx, &mm, &ss, &bb) == 4) ||
(sscanf(lptr, "INDEX %01d %02d:%02d:%02d", &idx, &mm, &ss, &bb) == 4))
{
int idx_pos = bb + ss * 75 + mm * 60 * 75;
if (idx == 0) {
if (this->toc.last && !this->toc.tracks[this->toc.last - 1].end)
{
this->toc.tracks[this->toc.last - 1].end = pregap;
}
}
else if (idx == 1) {
this->toc.tracks[this->toc.last].offset += pregap * 2352;
if (!new_file)
{
this->toc.tracks[this->toc.last].start = idx_pos + pregap;
if (this->toc.last && !this->toc.tracks[this->toc.last - 1].end)
{
this->toc.tracks[this->toc.last - 1].end = this->toc.tracks[this->toc.last].start - this->toc.tracks[this->toc.last].pregap;
#ifdef P3DO_DEBUG
printf("\x1b[32m3DO: track = %u, start = %u, end = %u\n\x1b[0m", this->toc.last - 1 + 1, this->toc.tracks[this->toc.last - 1].start, this->toc.tracks[this->toc.last - 1].end);
#endif // P3DO_DEBUG
}
}
else
{
this->toc.tracks[this->toc.last].start = this->toc.end + pregap;
this->toc.tracks[this->toc.last].offset += this->toc.end * 2352;
int sectorSize = 2352;
if (this->toc.tracks[this->toc.last].type != TT_CDDA) sectorSize = this->sectorSize;
this->toc.tracks[this->toc.last].end = this->toc.tracks[this->toc.last].start + ((file_size + sectorSize - 1) / sectorSize);
this->toc.end = this->toc.tracks[this->toc.last].end;
#ifdef P3DO_DEBUG
printf("\x1b[32m3DO: track = %u, start = %u, end = %u\n\x1b[0m", this->toc.last + 1, this->toc.tracks[this->toc.last].start, this->toc.tracks[this->toc.last].end);
#endif // P3DO_DEBUG
}
#ifdef P3DO_DEBUG
printf("\x1b[32m3DO: track = %u, offset = %u\n\x1b[0m", this->toc.last + 1, this->toc.tracks[this->toc.last].offset);
#endif // P3DO_DEBUG
}
if (idx == 0) {
this->toc.tracks[this->toc.last].indexes[idx] = 0;
}
else {
if (!new_file)
{
this->toc.tracks[this->toc.last].indexes[idx] = idx_pos - this->toc.tracks[this->toc.last].start;
}
else
{
this->toc.tracks[this->toc.last].indexes[idx] = idx_pos;
}
}
this->toc.tracks[this->toc.last].index_num = idx + 1;
#ifdef P3DO_DEBUG
//printf("\x1b[32mSaturn: index = %u, pos = %u\n\x1b[0m", idx, this->toc.tracks[this->toc.last].indexes[idx]);
#endif // P3DO_DEBUG
}
}
this->toc.last++;
if (this->toc.last && !this->toc.tracks[this->toc.last - 1].end)
{
this->toc.end += pregap;
this->toc.tracks[this->toc.last - 1].end = this->toc.end;
}
return 0;
}
int p3docdd_t::LoadISO(const char* filename) {
static char fname[1024 + 10];
strcpy(fname, filename);
if (!FileOpen(&this->toc.tracks[0].f, filename)) return -1;
FileSeek(&this->toc.tracks[0].f, 0, SEEK_SET);
int file_size = this->toc.tracks[0].f.size;
this->toc.tracks[0].start = 0;
this->toc.tracks[0].offset = 0;
this->toc.end = this->toc.tracks[0].end = file_size / 2048;
this->toc.sectorSize = this->toc.tracks[0].sector_size = 2048;
this->toc.tracks[0].type = TT_MODE1;
this->toc.last = 1;
#ifdef P3DO_DEBUG
printf("\x1b[32m3DO: track = %u, start = %u, end = %u\n\x1b[0m", this->toc.last, this->toc.tracks[this->toc.last - 1].start, this->toc.tracks[this->toc.last - 1].end);
#endif // P3DO_DEBUG
return 0;
}
int p3docdd_t::Load(const char *filename)
{
Unload();
const char *ext = filename + strlen(filename) - 4;
if (!strncasecmp(".cue", ext, 4))
{
if (LoadCUE(filename)) {
return (-1);
}
}
else if (!strncasecmp(".iso", ext, 4)) {
if (LoadISO(filename)) {
return (-1);
}
if (this->toc.tracks[0].sector_size)
{
this->sectorSize = this->toc.tracks[0].sector_size;
}
}
else if (!strncasecmp(".chd", ext, 4)) {
chd_error err = mister_load_chd(filename, &this->toc);
if (err != CHDERR_NONE)
{
printf("ERROR %s\n", chd_error_string(err));
return -1;
}
if (this->chd_hunkbuf)
{
free(this->chd_hunkbuf);
}
this->chd_hunkbuf = (uint8_t *)malloc(this->toc.chd_hunksize);
this->chd_hunknum = -1;
if (this->toc.tracks[0].sector_size)
{
this->sectorSize = this->toc.tracks[0].sector_size;
}
}
else {
return (-1);
}
/*if (this->toc.chd_f)
{
mister_chd_read_sector(this->toc.chd_f, 0, 0, 0, 0x10, (uint8_t *)header, this->chd_hunkbuf, &this->chd_hunknum);
}
else {
fd_img = &this->toc.tracks[0].f;
FileSeek(fd_img, 0, SEEK_SET);
FileReadAdv(fd_img, header, 0x10);
}*/
#ifdef P3DO_DEBUG
printf("\x1b[32m3DO: Sector size = %u, Track 1 end = %u\n\x1b[0m", this->sectorSize, this->toc.tracks[0].end);
#endif // P3DO_DEBUG
if (this->toc.last)
{
this->toc.tracks[this->toc.last].start = this->toc.end;
this->loaded = 1;
this->lid_open = false;
this->stop_pend = true;
#ifdef P3DO_DEBUG
printf("\x1b[32m3DO: CD mounted, last track = %u\n\x1b[0m", this->toc.last);
#endif // P3DO_DEBUG
return 1;
}
return 0;
}
void p3docdd_t::Unload()
{
if (this->loaded)
{
if (this->toc.chd_f)
{
chd_close(this->toc.chd_f);
}
if (this->chd_hunkbuf)
{
free(this->chd_hunkbuf);
this->chd_hunkbuf = NULL;
}
for (int i = 0; i < this->toc.last; i++)
{
if (this->toc.tracks[i].f.opened())
{
FileClose(&this->toc.tracks[i].f);
}
}
this->loaded = 0;
}
memset(&this->toc, 0x00, sizeof(this->toc));
this->sectorSize = 0;
#ifdef P3DO_DEBUG
printf("\x1b[32m3DO: ");
printf("Unload");
printf("\n\x1b[0m");
#endif // P3DO_DEBUG
}
int p3docdd_t::GetDiscInfo(uint8_t *buf) {
if (this->toc.last < 0) return -1;
memset(buf, 0x00, 2048);
msf_t msf = { 0,2,0 };
LBAToMSF(this->toc.end + 150, &msf);
buf[1] = P3DO_DISC_DA_OR_CDROM; //disc id
buf[2] = 0x01; //first track
buf[3] = this->toc.last; //last track
buf[4] = msf.m; //mm
buf[5] = msf.s; //ss
buf[6] = msf.f; //ff
for (int i = 0, offs = 0x10; i <= this->toc.last; i++, offs+=0x10)
{
LBAToMSF(this->toc.tracks[i].start + 150, &msf);
buf[offs + 1] = 0;
buf[offs + 2] = this->toc.tracks[i].type == TT_CDDA ? 0x00 : 0x04;
buf[offs + 3] = i + 1;
buf[offs + 4] = 0;
buf[offs + 5] = msf.m; //min
buf[offs + 6] = msf.s; //sec
buf[offs + 7] = msf.f; //frames
buf[offs + 8] = 0;
}
this->lba = 0;
ReadData(cd_buf);
memcpy(buf+1024, cd_buf, 128);
return 1;
}
void p3docdd_t::Reset() {
state = P3DO_Open;
lid_open = false;
stop_pend = false;
read_pend = false;
read_toc = false;
track = 0;
lba = 0;
speed = 0;
p3docdd.SendData = 0;
#ifdef P3DO_DEBUG
printf("\x1b[32m3DO: ");
printf("Reset");
printf("\n\x1b[0m");
#endif // P3DO_DEBUG
}
void p3docdd_t::CommandExec() {
int cmd_lba = GetLBAFromCommand(comm);
int cmd_blocks = GetBlocksFromCommand(comm);
switch (comm[0]) {
case P3DO_COMM_NOP:
#ifdef P3DO_DEBUG
//printf("\x1b[32m3DO: ");
//printf("Command Nop");
//printf(" (%u)\n\x1b[0m", p3do_frame_cnt);
#endif // P3DO_DEBUG
break;
case P3DO_COMM_READ:
this->lba = cmd_lba - 150;
this->track = this->toc.GetTrackByLBA(this->lba);
this->read_pend = true;
this->block_reads = cmd_blocks;
this->block_count = 0;
this->speed = comm[7] == 1 ? 1 : 2;
#ifdef P3DO_DEBUG
//printf("\x1b[32m3DO: ");
//printf("Command = %02X %02X %02X %02X %02X %02X %02X %02X", comm[0], comm[1], comm[2], comm[3], comm[4], comm[5], comm[6], comm[7]);
//printf("\n\x1b[0m");
printf("\x1b[32m3DO: ");
printf("Command Read Data: cmd_lba = %u, cmd_blocks = %u, track = %u, track_start = %u, speed = %u", cmd_lba, cmd_blocks, this->track + 1, this->toc.tracks[this->track].start, this->speed);
printf(" (%u)\n\x1b[0m", p3do_frame_cnt);
#endif // P3DO_DEBUG
break;
case P3DO_COMM_NEXT:
if (this->block_reads > 0) this->read_pend = true;
#ifdef P3DO_DEBUG
//printf("\x1b[32m3DO: ");
//printf("Command Next Data");
//printf(" (%u)\n\x1b[0m", p3do_frame_cnt);
#endif // P3DO_DEBUG
break;
default:
this->read_pend = false;
#ifdef P3DO_DEBUG
printf("\x1b[32m3DO: ");
printf("Command undefined, command = %02X %02X %02X %02X %02X %02X %02X %02X", comm[0], comm[1], comm[2], comm[3], comm[4], comm[5], comm[6], comm[7]);
printf(" (%u)\n\x1b[0m", p3do_frame_cnt);
#endif // P3DO_DEBUG
break;
}
}
void p3docdd_t::Process(uint8_t* time_mode) {
if (this->lid_open) {
this->state = P3DO_Open;
*time_mode = 1;
#ifdef P3DO_DEBUG
printf("\x1b[32m3DO: ");
printf("Lid open");
printf(" (%u)\n\x1b[0m", p3do_frame_cnt);
#endif // P3DO_DEBUG
}
else if (this->read_pend) {
this->state = P3DO_Read;
*time_mode = this->speed;
/*if (this->block_count >= 16)*/ this->read_pend = false;
#ifdef P3DO_DEBUG
//printf("\x1b[32m3DO: ");
//printf("Process read data, track = %i, lba = %i, amsf = %02X:%02X:%02X, msf = %02X:%02X:%02X", this->track + 1, this->lba, BCD(amsf.m), BCD(amsf.s), BCD(amsf.f), BCD(msf.m), BCD(msf.s), BCD(msf.f));
//printf(" (%u)\n\x1b[0m", p3do_frame_cnt);
#endif // P3DO_DEBUG
}
else {
this->state = P3DO_Idle;
*time_mode = 1;
}
}
void p3docdd_t::Update() {
msf_t msf = { 0,2,0 };
switch (this->state)
{
case P3DO_Idle:
this->track = this->toc.GetTrackByLBA(this->lba);
break;
case P3DO_Open:
break;
case P3DO_Read:
LBAToMSF(this->lba + 150, &msf);
if (this->toc.tracks[this->track].type == TT_MODE1)
{
// CD-ROM Data (Mode 1/2)
uint8_t header[16];
#ifdef P3DO_DEBUG
//printf("\x1b[32m3DO: ");
//printf("Update read data, track = %i, lba = %i, msf = %02X:%02X:%02X, mode = %u", this->track + 1, this->lba + 150, BCD(msf.m), BCD(msf.s), BCD(msf.f), this->toc.tracks[this->track].type);
//printf("\n\x1b[0m");
#endif // P3DO_DEBUG
if (this->sectorSize == 2048 || (this->lba - this->toc.tracks[this->track].start) < 0) {
header[0] = 0x00;
header[1] = header[2] = header[3] = header[4] = header[5] = header[6] = header[7] = header[8] = header[9] = header[10] = 0xFF;
header[11] = 0x00;
header[12] = BCD(msf.m);
header[13] = BCD(msf.s);
header[14] = BCD(msf.f);
header[15] = (uint8_t)(this->toc.tracks[this->track].type != TT_CDDA);
DataSectorSend(header);
}
else {
DataSectorSend(0);
}
}
else
{
//AudioSectorSend(this->audioFirst);
}
#ifdef P3DO_DEBUG
//printf("\x1b[32m3DO: ");
//printf("Update read data, lba = %i, blocks = %i msf = %u:%u:%u", this->lba, this->block_reads, msf.m, msf.s, msf.f);
//printf(" (%u)\n\x1b[0m", p3do_frame_cnt);
#endif // P3DO_DEBUG
this->lba++;
this->track = this->toc.GetTrackByLBA(this->lba);
this->block_reads--;
this->block_count++;
//if (this->block_count == 16 || this->block_reads == 0) this->read_pend = false;
break;
case P3DO_Pause:
case P3DO_Stop:
this->track = this->toc.GetTrackByLBA(this->lba);
break;
}
}
void p3docdd_t::LBAToMSF(int lba, msf_t* msf) {
msf->m = (lba / 75) / 60;
msf->s = (lba / 75) % 60;
msf->f = (lba % 75);
}
int p3docdd_t::MSFToLBA(msf_t* msf) {
return (int)(msf->m) * 60 * 75 + (int)(msf->s) * 75 + (int)(msf->f);
}
int p3docdd_t::GetLBAFromCommand(uint8_t* cmd) {
int lba = 0;
lba |= cmd[3] << 0;
lba |= cmd[2] << 8;
lba |= cmd[1] << 16;
if (cmd[4]) {
return lba;
}
msf_t msf = { cmd[1],cmd[2],cmd[3] };
return MSFToLBA(&msf);
}
int p3docdd_t::GetBlocksFromCommand(uint8_t* cmd) {
int num = 0;
num |= cmd[6] << 0;
num |= cmd[5] << 8;
return num;
}
uint8_t* p3docdd_t::GetStatus() {
return this->stat;
}
int p3docdd_t::SetCommand(uint8_t* data) {
memcpy(this->comm, data, 8);
return 0;
}
void p3docdd_t::ReadData(uint8_t *buf)
{
int offs = 0;
if (this->toc.tracks[this->track].type == TT_MODE1)
{
int lba_ = this->lba >= 0 ? this->lba : 0;
if (this->toc.chd_f)
{
int read_offset = 0;
if (this->sectorSize == 2048)
{
read_offset += 16;
}
mister_chd_read_sector(this->toc.chd_f, lba_ + this->toc.tracks[this->track].offset, read_offset, 0, this->sectorSize, buf, this->chd_hunkbuf, &this->chd_hunknum);
}
else {
if (this->sectorSize == 2048)
{
offs = (lba_ * 2048) - this->toc.tracks[this->track].offset;
FileSeek(&this->toc.tracks[this->track].f, offs, SEEK_SET);
FileReadAdv(&this->toc.tracks[this->track].f, buf + 16, 2048);
}
else {
offs = (lba_ * 2352) - this->toc.tracks[this->track].offset;
FileSeek(&this->toc.tracks[this->track].f, offs, SEEK_SET);
FileReadAdv(&this->toc.tracks[this->track].f, buf, 2352);
}
#ifdef P3DO_DEBUG
//printf("\x1b[32m3DO: ");
//printf("Read data, lba = %i, track = %i, offset = %i", lba_, this->track, offs);
//printf(" (%u)\n\x1b[0m", p3do_frame_cnt);
#endif // P3DO_DEBUG
}
}
}
int p3docdd_t::DataSectorSend(uint8_t* header)
{
if (header) {
ReadData(cd_buf);
memcpy(cd_buf, header, 16);
}
else {
ReadData(cd_buf);
}
uint16_t crc = 0;
for (int i = 0; i < 2048; i++)
{
crc += cd_buf[i + 16];
}
cd_buf[16 + 2048 + 0] = crc & 0xFF;
cd_buf[16 + 2048 + 1] = crc >> 8;
if (SendData)
return SendData(cd_buf + 16, 2048 + 2, CD_DATA_IO_INDEX);
return 0;
}