more discsys reorg
This commit is contained in:
parent
33f23e137d
commit
691531421a
|
@ -357,13 +357,13 @@ namespace BizHawk.Client.DiscoHawk
|
|||
var src_dsr = new DiscSectorReader(src_disc);
|
||||
var dst_dsr = new DiscSectorReader(dst_disc);
|
||||
|
||||
var src_toc = src_disc.TOCRaw;
|
||||
var dst_toc = dst_disc.TOCRaw;
|
||||
var src_toc = src_disc.TOC;
|
||||
var dst_toc = dst_disc.TOC;
|
||||
|
||||
var src_databuf = new byte[2448];
|
||||
var dst_databuf = new byte[2448];
|
||||
|
||||
Action<DiscTOCRaw.TOCItem> sw_dump_toc_one = (item) =>
|
||||
Action<DiscTOC.TOCItem> sw_dump_toc_one = (item) =>
|
||||
{
|
||||
if (!item.Exists)
|
||||
sw.Write("(---missing---)");
|
||||
|
@ -386,12 +386,12 @@ namespace BizHawk.Client.DiscoHawk
|
|||
}
|
||||
|
||||
//verify TOC match
|
||||
if (src_disc.TOCRaw.FirstRecordedTrackNumber != dst_disc.TOCRaw.FirstRecordedTrackNumber
|
||||
|| src_disc.TOCRaw.LastRecordedTrackNumber != dst_disc.TOCRaw.LastRecordedTrackNumber)
|
||||
if (src_disc.TOC.FirstRecordedTrackNumber != dst_disc.TOC.FirstRecordedTrackNumber
|
||||
|| src_disc.TOC.LastRecordedTrackNumber != dst_disc.TOC.LastRecordedTrackNumber)
|
||||
{
|
||||
sw.WriteLine("Mismatch of RecordedTrackNumbers: {0}-{1} vs {2}-{3}",
|
||||
src_disc.TOCRaw.FirstRecordedTrackNumber, src_disc.TOCRaw.LastRecordedTrackNumber,
|
||||
dst_disc.TOCRaw.FirstRecordedTrackNumber, dst_disc.TOCRaw.LastRecordedTrackNumber
|
||||
src_disc.TOC.FirstRecordedTrackNumber, src_disc.TOC.LastRecordedTrackNumber,
|
||||
dst_disc.TOC.FirstRecordedTrackNumber, dst_disc.TOC.LastRecordedTrackNumber
|
||||
);
|
||||
goto SKIPPO;
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ namespace BizHawk.Emulation.Common.Components
|
|||
|
||||
public void PlayStartingAtLba(int lba)
|
||||
{
|
||||
var track = Disc.Structure.SeekTrack(lba);
|
||||
var track = Disc.Session1.SeekTrack(lba);
|
||||
if (track == null) return;
|
||||
|
||||
PlayingTrack = track.Number;
|
||||
|
|
|
@ -421,7 +421,7 @@ namespace BizHawk.Emulation.Cores.Sega.Saturn
|
|||
|
||||
rTOC[99] = (int)(rTOC[0] & 0xff000000 | 0x010000);
|
||||
rTOC[100] = (int)(rTOC[ntrk - 1] & 0xff000000 | (uint)(ntrk << 16));
|
||||
rTOC[101] = (int)(rTOC[ntrk - 1] & 0xff000000 | (uint)(CD.TOCRaw.LeadoutLBA.Sector)); //zero 03-jul-2014 - maybe off by 150
|
||||
rTOC[101] = (int)(rTOC[ntrk - 1] & 0xff000000 | (uint)(CD.TOC.LeadoutLBA.Sector)); //zero 03-jul-2014 - maybe off by 150
|
||||
|
||||
|
||||
Marshal.Copy(rTOC, 0, dest, 102);
|
||||
|
|
|
@ -160,15 +160,15 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
|
|||
|
||||
int ShockDisc_ReadTOC(IntPtr opaque, OctoshockDll.ShockTOC* read_target, OctoshockDll.ShockTOCTrack* tracks101)
|
||||
{
|
||||
read_target->disc_type = (byte)Disc.TOCRaw.Session1Format;
|
||||
read_target->first_track = (byte)Disc.TOCRaw.FirstRecordedTrackNumber; //i _think_ thats what is meant here
|
||||
read_target->last_track = (byte)Disc.TOCRaw.LastRecordedTrackNumber; //i _think_ thats what is meant here
|
||||
read_target->disc_type = (byte)Disc.TOC.Session1Format;
|
||||
read_target->first_track = (byte)Disc.TOC.FirstRecordedTrackNumber; //i _think_ thats what is meant here
|
||||
read_target->last_track = (byte)Disc.TOC.LastRecordedTrackNumber; //i _think_ thats what is meant here
|
||||
|
||||
tracks101[0].lba = tracks101[0].adr = tracks101[0].control = 0;
|
||||
|
||||
for (int i = 1; i < 100; i++)
|
||||
{
|
||||
var item = Disc.TOCRaw.TOCItems[i];
|
||||
var item = Disc.TOC.TOCItems[i];
|
||||
tracks101[i].adr = (byte)(item.Exists ? 1 : 0);
|
||||
tracks101[i].lba = (uint)item.LBATimestamp.Sector;
|
||||
tracks101[i].control = (byte)item.Control;
|
||||
|
@ -177,7 +177,7 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
|
|||
////the lead-out track is to be synthesized
|
||||
tracks101[read_target->last_track + 1].adr = 1;
|
||||
tracks101[read_target->last_track + 1].control = 0;
|
||||
tracks101[read_target->last_track + 1].lba = (uint)Disc.TOCRaw.LeadoutLBA.Sector;
|
||||
tracks101[read_target->last_track + 1].lba = (uint)Disc.TOC.LeadoutLBA.Sector;
|
||||
|
||||
//element 100 is to be copied as the lead-out track
|
||||
tracks101[100] = tracks101[read_target->last_track + 1];
|
||||
|
|
|
@ -18,126 +18,6 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
/// </summary>
|
||||
public List<Session> Sessions = new List<Session>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determines which track of session 1 is at the specified LBA.
|
||||
/// Returns null if it's before track 1
|
||||
/// </summary>
|
||||
public Track SeekTrack(int lba)
|
||||
{
|
||||
var ses = Sessions[1];
|
||||
|
||||
//take care with this loop bounds:
|
||||
for (int i = 1; i <= ses.InformationTrackCount; i++)
|
||||
{
|
||||
var track = ses.Tracks[i];
|
||||
if (track.LBA > lba)
|
||||
return (i==1)?null:ses.Tracks[i];
|
||||
}
|
||||
return ses.Tracks[ses.Tracks.Count];
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Synthesizes the DiscStructure from RawTOCEntriesJob
|
||||
///// </summary>
|
||||
//public class SynthesizeFromRawTOCEntriesJob
|
||||
//{
|
||||
// public IEnumerable<RawTOCEntry> Entries;
|
||||
// public DiscStructure Result;
|
||||
|
||||
// public void Run()
|
||||
// {
|
||||
// Result = new DiscStructure();
|
||||
// var session = new Session();
|
||||
// Result.Sessions.Add(session);
|
||||
|
||||
// //TODO - are these necessarily in order?
|
||||
// foreach (var te in Entries)
|
||||
// {
|
||||
// int pt = te.QData.q_index.DecimalValue;
|
||||
// int lba = te.QData.Timestamp.Sector;
|
||||
// var bcd2 = new BCD2 { BCDValue = (byte)pt };
|
||||
// if (bcd2.DecimalValue > 99) //A0 A1 A2 leadout and crap
|
||||
// continue;
|
||||
// var track = new Track { LBA = lba, Number = pt };
|
||||
// session.Tracks.Add(track);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
public class SynthesizeFromTOCRawJob
|
||||
{
|
||||
public Disc IN_Disc;
|
||||
public DiscTOCRaw TOCRaw;
|
||||
public DiscStructure Result;
|
||||
|
||||
public void Run()
|
||||
{
|
||||
var dsr = new DiscSectorReader(IN_Disc);
|
||||
dsr.Policy.DeterministicClearBuffer = false;
|
||||
|
||||
Result = new DiscStructure();
|
||||
var session = new Session();
|
||||
Result.Sessions.Add(null); //placeholder session for reindexing
|
||||
Result.Sessions.Add(session);
|
||||
|
||||
session.Number = 1;
|
||||
|
||||
if(TOCRaw.FirstRecordedTrackNumber != 1)
|
||||
throw new InvalidOperationException("Unsupported: FirstRecordedTrackNumber != 1");
|
||||
|
||||
//add a lead-in track
|
||||
session.Tracks.Add(new DiscStructure.Track() {
|
||||
Number = 0,
|
||||
Control = EControlQ.None, //TODO - not accurate (take from track 1?)
|
||||
LBA = -150 //TODO - not accurate
|
||||
});
|
||||
|
||||
int ntracks = TOCRaw.LastRecordedTrackNumber - TOCRaw.FirstRecordedTrackNumber + 1;
|
||||
for(int i=0;i<ntracks;i++)
|
||||
{
|
||||
var item = TOCRaw.TOCItems[i+1];
|
||||
var track = new DiscStructure.Track() {
|
||||
Number = i+1,
|
||||
Control = item.Control,
|
||||
LBA = item.LBATimestamp.Sector
|
||||
};
|
||||
session.Tracks.Add(track);
|
||||
|
||||
if (!item.IsData)
|
||||
track.Mode = 0;
|
||||
else
|
||||
{
|
||||
//determine the mode by a hardcoded heuristic: check mode of first sector
|
||||
track.Mode = dsr.ReadLBA_Mode(track.LBA);
|
||||
}
|
||||
|
||||
//determine track length according to... how? It isn't clear.
|
||||
//Let's not do this until it's needed.
|
||||
//if (i == ntracks - 1)
|
||||
// track.Length = TOCRaw.LeadoutLBA.Sector - track.LBA;
|
||||
//else track.Length = (TOCRaw.TOCItems[i + 2].LBATimestamp.Sector - track.LBA);
|
||||
}
|
||||
|
||||
//add lead-out track
|
||||
session.Tracks.Add(new DiscStructure.Track()
|
||||
{
|
||||
Number = 0xA0, //right?
|
||||
Control = EControlQ.None, //TODO - not accurate (take from track 1?)
|
||||
LBA = TOCRaw.LeadoutLBA.Sector
|
||||
});
|
||||
|
||||
//link track list
|
||||
for (int i = 0; i < session.Tracks.Count - 1; i++)
|
||||
{
|
||||
session.Tracks[i].NextTrack = session.Tracks[i + 1];
|
||||
}
|
||||
|
||||
//other misc fields
|
||||
session.InformationTrackCount = session.Tracks.Count - 2;
|
||||
}
|
||||
}
|
||||
|
||||
public class Session
|
||||
{
|
||||
//Notable omission:
|
||||
|
@ -178,6 +58,24 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
/// Effectively, the end of the user area of the disc.
|
||||
/// </summary>
|
||||
public Track LeadoutTrack { get { return Tracks[Tracks.Count - 1]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines which track of the session is at the specified LBA.
|
||||
/// Returns null if it's before track 1
|
||||
/// </summary>
|
||||
public Track SeekTrack(int lba)
|
||||
{
|
||||
var ses = this;
|
||||
|
||||
//take care with this loop bounds:
|
||||
for (int i = 1; i <= ses.InformationTrackCount; i++)
|
||||
{
|
||||
var track = ses.Tracks[i];
|
||||
if (track.LBA > lba)
|
||||
return (i == 1) ? null : ses.Tracks[i];
|
||||
}
|
||||
return ses.Tracks[ses.Tracks.Count];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.DiscSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents our best guess at what a disc drive firmware will receive by reading the TOC from the lead-in track, modeled after CCD contents and mednafen/PSX needs.
|
||||
/// </summary>
|
||||
public class DiscTOC
|
||||
{
|
||||
/// <summary>
|
||||
/// The TOC specifies the first recorded track number, independently of whatever may actually be recorded
|
||||
/// </summary>
|
||||
public int FirstRecordedTrackNumber = -1;
|
||||
|
||||
/// <summary>
|
||||
/// The TOC specifies the last recorded track number, independently of whatever may actually be recorded
|
||||
/// </summary>
|
||||
public int LastRecordedTrackNumber = -1;
|
||||
|
||||
/// <summary>
|
||||
/// The TOC specifies the format of the session, so here it is.
|
||||
/// </summary>
|
||||
public SessionFormat Session1Format = SessionFormat.None;
|
||||
|
||||
/// <summary>
|
||||
/// Information about a single track in the TOC
|
||||
/// </summary>
|
||||
public struct TOCItem
|
||||
{
|
||||
/// <summary>
|
||||
/// [IEC10149] "the control field used in the information track"
|
||||
/// the raw TOC entries do have a control field which is supposed to match what's found in the track.
|
||||
/// Determining whether a track contains audio or data is very important.
|
||||
/// A track mode can't be safely determined from reading sectors from the actual track if it's an audio track (there's no sector header with a mode byte)
|
||||
/// </summary>
|
||||
public EControlQ Control;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the Control indicates that this is data
|
||||
/// </summary>
|
||||
public bool IsData { get { return (Control & EControlQ.DATA) != 0; } }
|
||||
|
||||
/// <summary>
|
||||
/// The location of the track (Index 1)
|
||||
/// </summary>
|
||||
public Timestamp LBATimestamp;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this entry exists (since the table is 101 entries long always)
|
||||
/// </summary>
|
||||
public bool Exists;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a convenient format for storing the TOC (taken from mednafen)
|
||||
/// Index 0 is empty, so that track 1 is in index 1.
|
||||
/// Index 100 is the Lead-out track
|
||||
/// </summary>
|
||||
public TOCItem[] TOCItems = new TOCItem[101];
|
||||
|
||||
/// <summary>
|
||||
/// The timestamp of the leadout track. In other words, the end of the user area.
|
||||
/// </summary>
|
||||
public Timestamp LeadoutLBA { get { return TOCItems[100].LBATimestamp; } }
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.DiscSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents our best guess at what a disc drive firmware will receive by reading the TOC from the lead-in track, modeled after CCD contents and mednafen/PSX needs.
|
||||
/// </summary>
|
||||
public class DiscTOCRaw
|
||||
{
|
||||
/// <summary>
|
||||
/// Synthesizes the TOC from a set of raw entries.
|
||||
/// When a disc drive firmware reads the lead-in area, it builds this TOC from finding q-mode 1 sectors in the Q subchannel of the lead-in area.
|
||||
/// Question: I guess it must ignore q-mode != 1? what else would it do with it?
|
||||
/// </summary>
|
||||
public class SynthesizeFromRawTOCEntriesJob
|
||||
{
|
||||
public IEnumerable<RawTOCEntry> Entries;
|
||||
public List<string> Log = new List<string>();
|
||||
public DiscTOCRaw Result;
|
||||
|
||||
public void Run()
|
||||
{
|
||||
SynthesizeFromRawTOCEntriesJob job = this;
|
||||
DiscTOCRaw ret = new DiscTOCRaw();
|
||||
|
||||
//this is a dummy, for convenience in array indexing, so that track 1 is at array index 1
|
||||
ret.TOCItems[0].LBATimestamp = new Timestamp(0); //arguably could be -150, but let's not just yet
|
||||
ret.TOCItems[0].Control = 0;
|
||||
ret.TOCItems[0].Exists = false;
|
||||
|
||||
//just in case this doesnt get set...
|
||||
ret.FirstRecordedTrackNumber = 0;
|
||||
ret.LastRecordedTrackNumber = 0;
|
||||
|
||||
int maxFoundTrack = 0;
|
||||
|
||||
foreach (var te in job.Entries)
|
||||
{
|
||||
var q = te.QData;
|
||||
var point = q.q_index.DecimalValue;
|
||||
|
||||
//see ECMD-394 page 5-14 for info about point = 0xA0, 0xA1, 0xA2
|
||||
|
||||
if (point == 0x00)
|
||||
job.Log.Add("unexpected POINT=00 in lead-in Q-channel");
|
||||
else if (point == 255)
|
||||
throw new InvalidOperationException("point == 255");
|
||||
else if (point <= 99)
|
||||
{
|
||||
maxFoundTrack = Math.Max(maxFoundTrack, point);
|
||||
ret.TOCItems[point].LBATimestamp = new Timestamp(q.AP_Timestamp.Sector - 150); //RawTOCEntries contained an absolute time
|
||||
ret.TOCItems[point].Control = q.CONTROL;
|
||||
ret.TOCItems[point].Exists = true;
|
||||
}
|
||||
else if (point == 100) //0xA0 bcd
|
||||
{
|
||||
ret.FirstRecordedTrackNumber = q.ap_min.DecimalValue;
|
||||
if (q.ap_frame.DecimalValue != 0) job.Log.Add("PFRAME should be 0 for POINT=0xA0");
|
||||
if (q.ap_sec.DecimalValue == 0x00) ret.Session1Format = DiscTOCRaw.SessionFormat.Type00_CDROM_CDDA;
|
||||
else if (q.ap_sec.DecimalValue == 0x10) ret.Session1Format = DiscTOCRaw.SessionFormat.Type10_CDI;
|
||||
else if (q.ap_sec.DecimalValue == 0x20) ret.Session1Format = DiscTOCRaw.SessionFormat.Type20_CDXA;
|
||||
else job.Log.Add("Unrecognized session format: PSEC should be one of {0x00,0x10,0x20} for POINT=0xA0");
|
||||
}
|
||||
else if (point == 101) //0xA1 bcd
|
||||
{
|
||||
ret.LastRecordedTrackNumber = q.ap_min.DecimalValue;
|
||||
if (q.ap_sec.DecimalValue != 0) job.Log.Add("PSEC should be 0 for POINT=0xA1");
|
||||
if (q.ap_frame.DecimalValue != 0) job.Log.Add("PFRAME should be 0 for POINT=0xA1");
|
||||
}
|
||||
else if (point == 102) //0xA2 bcd
|
||||
{
|
||||
ret.TOCItems[100].LBATimestamp = new Timestamp(q.AP_Timestamp.Sector - 150); //RawTOCEntries contained an absolute time
|
||||
ret.TOCItems[100].Control = 0; //not clear what this should be
|
||||
ret.TOCItems[100].Exists = true;
|
||||
}
|
||||
}
|
||||
|
||||
//this is speculative:
|
||||
//well, nothing to be done here..
|
||||
if (ret.FirstRecordedTrackNumber == -1) { }
|
||||
if (ret.LastRecordedTrackNumber == -1) { ret.LastRecordedTrackNumber = maxFoundTrack; }
|
||||
if (ret.Session1Format == SessionFormat.None) ret.Session1Format = SessionFormat.Type00_CDROM_CDDA;
|
||||
|
||||
//if (!ret.LeadoutTimestamp.Valid) {
|
||||
// //we're DOOMED. we cant know the length of the last track without this....
|
||||
//}
|
||||
job.Result = ret;
|
||||
}
|
||||
}
|
||||
|
||||
public enum SessionFormat
|
||||
{
|
||||
None = -1,
|
||||
Type00_CDROM_CDDA = 0x00,
|
||||
Type10_CDI = 0x10,
|
||||
Type20_CDXA = 0x20
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The TOC specifies the first recorded track number, independently of whatever may actually be recorded
|
||||
/// </summary>
|
||||
public int FirstRecordedTrackNumber = -1;
|
||||
|
||||
/// <summary>
|
||||
/// The TOC specifies the last recorded track number, independently of whatever may actually be recorded
|
||||
/// </summary>
|
||||
public int LastRecordedTrackNumber = -1;
|
||||
|
||||
/// <summary>
|
||||
/// The TOC specifies the format of the session, so here it is.
|
||||
/// </summary>
|
||||
public SessionFormat Session1Format = SessionFormat.None;
|
||||
|
||||
/// <summary>
|
||||
/// Information about a single track in the TOC
|
||||
/// </summary>
|
||||
public struct TOCItem
|
||||
{
|
||||
/// <summary>
|
||||
/// [IEC10149] "the control field used in the information track"
|
||||
/// the raw TOC entries do have a control field which is supposed to match what's found in the track.
|
||||
/// Determining whether a track contains audio or data is very important.
|
||||
/// A track mode can't be safely determined from reading sectors from the actual track if it's an audio track (there's no sector header with a mode byte)
|
||||
/// </summary>
|
||||
public EControlQ Control;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the Control indicates that this is data
|
||||
/// </summary>
|
||||
public bool IsData { get { return (Control & EControlQ.DATA) != 0; } }
|
||||
|
||||
/// <summary>
|
||||
/// The location of the track (Index 1)
|
||||
/// </summary>
|
||||
public Timestamp LBATimestamp;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this entry exists (since the table is 101 entries long always)
|
||||
/// </summary>
|
||||
public bool Exists;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a convenient format for storing the TOC (taken from mednafen)
|
||||
/// Index 0 is empty, so that track 1 is in index 1.
|
||||
/// Index 100 is the Lead-out track
|
||||
/// </summary>
|
||||
public TOCItem[] TOCItems = new TOCItem[101];
|
||||
|
||||
/// <summary>
|
||||
/// The timestamp of the leadout track. In other words, the end of the user area.
|
||||
/// </summary>
|
||||
public Timestamp LeadoutLBA { get { return TOCItems[100].LBATimestamp; } }
|
||||
}
|
||||
}
|
|
@ -56,7 +56,7 @@
|
|||
<Compile Include="API\DiscSectorReader.cs" />
|
||||
<Compile Include="API\DiscStream.cs" />
|
||||
<Compile Include="API\DiscStructure.cs" />
|
||||
<Compile Include="API\TOCRaw.cs" />
|
||||
<Compile Include="API\DiscTOC.cs" />
|
||||
<Compile Include="Blobs\Blob_ECM.cs" />
|
||||
<Compile Include="Blobs\Blob_RawFile.cs" />
|
||||
<Compile Include="Blobs\Blob_WaveFile.cs" />
|
||||
|
@ -88,6 +88,9 @@
|
|||
<Compile Include="Jobs\LoadSBIJob.cs" />
|
||||
<Compile Include="Jobs\LoggedJob.cs" />
|
||||
<Compile Include="Jobs\Synthesize_A0A1A2_Job.cs" />
|
||||
<Compile Include="Jobs\Synthesize_DiscStructure_From_DiscTOC_Job.cs" />
|
||||
<Compile Include="Jobs\Synthesize_DiscTOC_From_RawTOCEntries_Job.cs" />
|
||||
<Compile Include="Jobs\Synthesize_Leadout_Job.cs" />
|
||||
<Compile Include="M3U_file.cs" />
|
||||
<Compile Include="MednaDiscAPI.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
@ -104,8 +107,6 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="docs\notes.txt" />
|
||||
<Content Include="docs\todo.txt" />
|
||||
<Content Include="~notes-2015.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
|
|
@ -81,7 +81,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
internal class CompiledDiscInfo
|
||||
{
|
||||
public int FirstRecordedTrackNumber, LastRecordedTrackNumber;
|
||||
public DiscTOCRaw.SessionFormat SessionFormat;
|
||||
public SessionFormat SessionFormat;
|
||||
}
|
||||
|
||||
internal class CompiledCueTrack
|
||||
|
@ -182,13 +182,13 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
{
|
||||
case CueFile.TrackType.Mode2_2336:
|
||||
case CueFile.TrackType.Mode2_2352:
|
||||
OUT_CompiledDiscInfo.SessionFormat = DiscTOCRaw.SessionFormat.Type20_CDXA;
|
||||
OUT_CompiledDiscInfo.SessionFormat = SessionFormat.Type20_CDXA;
|
||||
discinfo_session1Format_determined = true;
|
||||
break;
|
||||
|
||||
case CueFile.TrackType.CDI_2336:
|
||||
case CueFile.TrackType.CDI_2352:
|
||||
OUT_CompiledDiscInfo.SessionFormat = DiscTOCRaw.SessionFormat.Type10_CDI;
|
||||
OUT_CompiledDiscInfo.SessionFormat = SessionFormat.Type10_CDI;
|
||||
discinfo_session1Format_determined = true;
|
||||
break;
|
||||
|
||||
|
|
|
@ -17,89 +17,10 @@ using System.Collections.Generic;
|
|||
//We should probably have some richer policy specifications for this kind of thing, but it's not a high priority. Main workflow is still discohawking.
|
||||
//Alternate policies would probably be associated with copious warnings (examples: ? ? ?)
|
||||
|
||||
|
||||
//https://books.google.com/books?id=caF_AAAAQBAJ&lpg=PA124&ots=OA9Ttj9CHZ&dq=disc%20TOC%20point%20A2&pg=PA124
|
||||
//http://www.staff.uni-mainz.de/tacke/scsi/SCSI2-14.html
|
||||
//http://www.pctechguide.com/iso-9660-data-format-for-cds-cd-roms-cd-rs-and-cd-rws
|
||||
//http://linux.die.net/man/1/cue2toc
|
||||
//http://cdemu.sourceforge.net/project.php#sf
|
||||
|
||||
//apparently cdrdao is the ultimate linux tool for doing this stuff but it doesnt support DAO96 (or other DAO modes) that would be necessary to extract P-Q subchannels
|
||||
//(cdrdao only supports R-W)
|
||||
|
||||
//here is a featureset list of windows cd burning programs (useful for cuesheet compatibility info)
|
||||
//http://www.dcsoft.com/cue_mastering_progs.htm
|
||||
|
||||
//good links
|
||||
//http://linux-sxs.org/bedtime/cdapi.html
|
||||
//http://en.wikipedia.org/wiki/Track_%28CD%29
|
||||
//http://docs.google.com/viewer?a=v&q=cache:imNKye05zIEJ:www.13thmonkey.org/documentation/SCSI/mmc-r10a.pdf+q+subchannel+TOC+format&hl=en&gl=us&pid=bl&srcid=ADGEEShtYqlluBX2lgxTL3pVsXwk6lKMIqSmyuUCX4RJ3DntaNq5vI2pCvtkyze-fumj7vvrmap6g1kOg5uAVC0IxwU_MRhC5FB0c_PQ2BlZQXDD7P3GeNaAjDeomelKaIODrhwOoFNb&sig=AHIEtbRXljAcFjeBn3rMb6tauHWjSNMYrw
|
||||
//http://digitalx.org/cue-sheet/examples/
|
||||
|
||||
//"qemu cdrom emulator"
|
||||
//http://www.koders.com/c/fid7171440DEC7C18B932715D671DEE03743111A95A.aspx
|
||||
|
||||
//less good
|
||||
//http://www.cyberciti.biz/faq/getting-volume-information-from-cds-iso-images/
|
||||
//http://www.cims.nyu.edu/cgi-systems/man.cgi?section=7I&topic=cdio
|
||||
|
||||
//some other docs
|
||||
//http://www.emutalk.net/threads/54428-Reference-for-8-byte-sub-header-used-in-CDROM-XA references http://ccsun.nchu.edu.tw/~imtech/cou...act%20Disc.pdf which is pretty cool
|
||||
|
||||
//ideas:
|
||||
/*
|
||||
* do some stuff asynchronously. for example, decoding mp3 sectors.
|
||||
* keep a list of sectors and the blob/offset from which they pull -- also whether the sector is available
|
||||
* if it is not available and something requests it then it will have to block while that sector gets generated
|
||||
* perhaps the blobs know how to resolve themselves and the requested sector can be immediately resolved (priority boost)
|
||||
* mp3 blobs should be hashed and dropped in %TEMP% as a wav decode
|
||||
*/
|
||||
|
||||
//here is an MIT licensed C mp3 decoder
|
||||
//http://core.fluendo.com/gstreamer/src/gst-fluendo-mp3/
|
||||
|
||||
/*information on saturn TOC and session data structures is on pdf page 58 of System Library User's Manual;
|
||||
* as seen in yabause, there are 1000 u32s in this format:
|
||||
* Ctrl[4bit] Adr[4bit] StartFrameAddressFAD[24bit] (nonexisting tracks are 0xFFFFFFFF)
|
||||
* Followed by Fist Track Information, Last Track Information..
|
||||
* Ctrl[4bit] Adr[4bit] FirstTrackNumber/LastTrackNumber[8bit] and then some stuff I dont understand
|
||||
* ..and Read Out Information:
|
||||
* Ctrl[4bit] Adr[4bit] ReadOutStartFrameAddress[24bit]
|
||||
*
|
||||
* Also there is some stuff about FAD of sessions.
|
||||
* This should be generated by the saturn core, but we need to make sure we pass down enough information to do it
|
||||
*/
|
||||
|
||||
//2048 bytes packed into 2352:
|
||||
//12 bytes sync(00 ff ff ff ff ff ff ff ff ff ff 00)
|
||||
//3 bytes sector address (min+A0),sec,frac //does this correspond to ccd `point` field in the TOC entries?
|
||||
//sector mode byte (0: silence; 1: 2048Byte mode (EDC,ECC,CIRC), 2: mode2 (could be 2336[vanilla mode2], 2048[xa mode2 form1], 2324[xa mode2 form2])
|
||||
//cue sheets may use mode1_2048 (and the error coding needs to be regenerated to get accurate raw data) or mode1_2352 (the entire sector is present)
|
||||
//audio is a different mode, seems to be just 2352 bytes with no sync, header or error correction. i guess the CIRC error correction is still there
|
||||
|
||||
namespace BizHawk.Emulation.DiscSystem
|
||||
{
|
||||
|
||||
public partial class Disc : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Free-form optional memos about the disc
|
||||
/// </summary>
|
||||
public Dictionary<string, object> Memos = new Dictionary<string, object>();
|
||||
|
||||
/// <summary>
|
||||
/// The raw TOC entries found in the lead-in track.
|
||||
/// These aren't very useful, but theyre one of the most lowest-level data structures from which other TOC-related stuff is derived
|
||||
/// </summary>
|
||||
public List<RawTOCEntry> RawTOCEntries = new List<RawTOCEntry>();
|
||||
|
||||
/// <summary>
|
||||
/// The DiscTOCRaw corresponding to the RawTOCEntries.
|
||||
/// TODO - rename to TOC
|
||||
/// TODO - there's one of these for every session, so... having one here doesnt make sense
|
||||
/// </summary>
|
||||
public DiscTOCRaw TOCRaw;
|
||||
|
||||
/// <summary>
|
||||
/// The DiscStructure corresponding to the TOCRaw
|
||||
/// </summary>
|
||||
|
@ -110,6 +31,41 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
/// </summary>
|
||||
public DiscStructure.Session Session1 { get { return Structure.Sessions[1]; } }
|
||||
|
||||
/// <summary>
|
||||
/// The DiscTOCRaw corresponding to the RawTOCEntries.
|
||||
/// TODO - rename to TOC
|
||||
/// TODO - there's one of these for every session, so... having one here doesnt make sense
|
||||
/// </summary>
|
||||
public DiscTOC TOC;
|
||||
|
||||
/// <summary>
|
||||
/// The raw TOC entries found in the lead-in track.
|
||||
/// These aren't very useful, but theyre one of the most lowest-level data structures from which other TOC-related stuff is derived
|
||||
/// </summary>
|
||||
public List<RawTOCEntry> RawTOCEntries = new List<RawTOCEntry>();
|
||||
|
||||
/// <summary>
|
||||
/// Free-form optional memos about the disc
|
||||
/// </summary>
|
||||
public Dictionary<string, object> Memos = new Dictionary<string, object>();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var res in DisposableResources)
|
||||
{
|
||||
res.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The DiscMountPolicy used to mount the disc. Consider this read-only.
|
||||
/// NOT SURE WE NEED THIS
|
||||
/// </summary>
|
||||
//public DiscMountPolicy DiscMountPolicy;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Disposable resources (blobs, mostly) referenced by this disc
|
||||
/// </summary>
|
||||
|
@ -125,72 +81,11 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
/// </summary>
|
||||
internal SectorSynthParams SynthParams = new SectorSynthParams();
|
||||
|
||||
/// <summary>
|
||||
/// The DiscMountPolicy used to mount the disc. Consider this read-only.
|
||||
/// NOT SURE WE NEED THIS
|
||||
/// </summary>
|
||||
//public DiscMountPolicy DiscMountPolicy;
|
||||
|
||||
public Disc()
|
||||
internal Disc()
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var res in DisposableResources)
|
||||
{
|
||||
res.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// generates lead-out sectors according to very crude approximations
|
||||
/// TODO - this isnt being used right now
|
||||
/// </summary>
|
||||
public class SynthesizeLeadoutJob
|
||||
{
|
||||
public int Length;
|
||||
public Disc Disc;
|
||||
|
||||
public void Run()
|
||||
{
|
||||
//TODO: encode_mode2_form2_sector
|
||||
|
||||
var leadoutTs = Disc.TOCRaw.LeadoutLBA;
|
||||
var lastTrackTOCItem = Disc.TOCRaw.TOCItems[Disc.TOCRaw.LastRecordedTrackNumber]; //NOTE: in case LastRecordedTrackNumber is al ie, this will malfunction
|
||||
|
||||
//leadout flags.. let's set them the same as the last track.
|
||||
//THIS IS NOT EXACTLY THE SAME WAY MEDNAFEN DOES IT
|
||||
EControlQ leadoutFlags = lastTrackTOCItem.Control;
|
||||
|
||||
//TODO - needs to be encoded as a certain mode (mode 2 form 2 for psx... i guess...)
|
||||
|
||||
for (int i = 0; i < Length; i++)
|
||||
{
|
||||
//var se = new SectorEntry(sz);
|
||||
//Disc.Sectors.Add(se);
|
||||
SubchannelQ sq = new SubchannelQ();
|
||||
|
||||
int track_relative_msf = i;
|
||||
sq.min = BCD2.FromDecimal(new Timestamp(track_relative_msf).MIN);
|
||||
sq.sec = BCD2.FromDecimal(new Timestamp(track_relative_msf).SEC);
|
||||
sq.frame = BCD2.FromDecimal(new Timestamp(track_relative_msf).FRAC);
|
||||
|
||||
int absolute_msf = i + leadoutTs.Sector;
|
||||
sq.ap_min = BCD2.FromDecimal(new Timestamp(absolute_msf+150).MIN);
|
||||
sq.ap_sec = BCD2.FromDecimal(new Timestamp(absolute_msf + 150).SEC);
|
||||
sq.ap_frame = BCD2.FromDecimal(new Timestamp(absolute_msf + 150).FRAC);
|
||||
|
||||
sq.q_tno.DecimalValue = 0xAA; //special value for leadout
|
||||
sq.q_index.DecimalValue = 1;
|
||||
|
||||
byte ADR = 1;
|
||||
sq.SetStatus(ADR, leadoutFlags);
|
||||
|
||||
//TODO - actually stash the subQ
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Automagically loads a disc, without any fine-tuned control at all
|
||||
|
|
|
@ -19,4 +19,12 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
{
|
||||
BizHawk, MednaDisc, LibMirage
|
||||
}
|
||||
|
||||
public enum SessionFormat
|
||||
{
|
||||
None = -1,
|
||||
Type00_CDROM_CDDA = 0x00,
|
||||
Type10_CDI = 0x10,
|
||||
Type20_CDXA = 0x20
|
||||
}
|
||||
}
|
|
@ -93,11 +93,11 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
{
|
||||
//generate toc and structure:
|
||||
//1. TOCRaw from RawTOCEntries
|
||||
var tocSynth = new DiscTOCRaw.SynthesizeFromRawTOCEntriesJob() { Entries = OUT_Disc.RawTOCEntries };
|
||||
var tocSynth = new Synthesize_DiscTOC_From_RawTOCEntries_Job() { Entries = OUT_Disc.RawTOCEntries };
|
||||
tocSynth.Run();
|
||||
OUT_Disc.TOCRaw = tocSynth.Result;
|
||||
OUT_Disc.TOC = tocSynth.Result;
|
||||
//2. Structure frmo TOCRaw
|
||||
var structureSynth = new DiscStructure.SynthesizeFromTOCRawJob() { IN_Disc = OUT_Disc, TOCRaw = OUT_Disc.TOCRaw };
|
||||
var structureSynth = new Synthesize_DiscStructure_From_DiscTOC_Job() { IN_Disc = OUT_Disc, TOCRaw = OUT_Disc.TOC };
|
||||
structureSynth.Run();
|
||||
OUT_Disc.Structure = structureSynth.Result;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ using System.Collections.Generic;
|
|||
|
||||
namespace BizHawk.Emulation.DiscSystem
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Returns ERRORS for things which will can't be processed any further. Processing of this job will continue though to try to collect the maximum amount of feedback.
|
||||
/// Returns WARNINGS for things which will are irregular or erroneous but later jobs might be able to handle, or which can be worked around by configuration assumptions.
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
/// <summary>
|
||||
/// The session format for this TOC
|
||||
/// </summary>
|
||||
public DiscTOCRaw.SessionFormat IN_Session1Format;
|
||||
public SessionFormat IN_Session1Format;
|
||||
|
||||
/// <summary>
|
||||
/// Appends the new entries to the provided list
|
||||
|
@ -53,9 +53,9 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
switch(IN_Session1Format)
|
||||
{
|
||||
//TODO these probably shouldn't be decimal values
|
||||
case DiscTOCRaw.SessionFormat.Type00_CDROM_CDDA: sq.ap_sec.DecimalValue = 0x00; break;
|
||||
case DiscTOCRaw.SessionFormat.Type10_CDI: sq.ap_sec.DecimalValue = 0x10; break;
|
||||
case DiscTOCRaw.SessionFormat.Type20_CDXA: sq.ap_sec.DecimalValue = 0x20; break;
|
||||
case SessionFormat.Type00_CDROM_CDDA: sq.ap_sec.DecimalValue = 0x00; break;
|
||||
case SessionFormat.Type10_CDI: sq.ap_sec.DecimalValue = 0x10; break;
|
||||
case SessionFormat.Type20_CDXA: sq.ap_sec.DecimalValue = 0x20; break;
|
||||
default: throw new InvalidOperationException("Invalid Session1Format");
|
||||
}
|
||||
sq.ap_frame.DecimalValue = 0;
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.DiscSystem
|
||||
{
|
||||
public class Synthesize_DiscStructure_From_DiscTOC_Job
|
||||
{
|
||||
public Disc IN_Disc;
|
||||
public DiscTOC TOCRaw;
|
||||
public DiscStructure Result;
|
||||
|
||||
public void Run()
|
||||
{
|
||||
var dsr = new DiscSectorReader(IN_Disc);
|
||||
dsr.Policy.DeterministicClearBuffer = false;
|
||||
|
||||
Result = new DiscStructure();
|
||||
var session = new DiscStructure.Session();
|
||||
Result.Sessions.Add(null); //placeholder session for reindexing
|
||||
Result.Sessions.Add(session);
|
||||
|
||||
session.Number = 1;
|
||||
|
||||
if (TOCRaw.FirstRecordedTrackNumber != 1)
|
||||
throw new InvalidOperationException("Unsupported: FirstRecordedTrackNumber != 1");
|
||||
|
||||
//add a lead-in track
|
||||
session.Tracks.Add(new DiscStructure.Track()
|
||||
{
|
||||
Number = 0,
|
||||
Control = EControlQ.None, //TODO - not accurate (take from track 1?)
|
||||
LBA = -150 //TODO - not accurate
|
||||
});
|
||||
|
||||
int ntracks = TOCRaw.LastRecordedTrackNumber - TOCRaw.FirstRecordedTrackNumber + 1;
|
||||
for (int i = 0; i < ntracks; i++)
|
||||
{
|
||||
var item = TOCRaw.TOCItems[i + 1];
|
||||
var track = new DiscStructure.Track()
|
||||
{
|
||||
Number = i + 1,
|
||||
Control = item.Control,
|
||||
LBA = item.LBATimestamp.Sector
|
||||
};
|
||||
session.Tracks.Add(track);
|
||||
|
||||
if (!item.IsData)
|
||||
track.Mode = 0;
|
||||
else
|
||||
{
|
||||
//determine the mode by a hardcoded heuristic: check mode of first sector
|
||||
track.Mode = dsr.ReadLBA_Mode(track.LBA);
|
||||
}
|
||||
|
||||
//determine track length according to... how? It isn't clear.
|
||||
//Let's not do this until it's needed.
|
||||
//if (i == ntracks - 1)
|
||||
// track.Length = TOCRaw.LeadoutLBA.Sector - track.LBA;
|
||||
//else track.Length = (TOCRaw.TOCItems[i + 2].LBATimestamp.Sector - track.LBA);
|
||||
}
|
||||
|
||||
//add lead-out track
|
||||
session.Tracks.Add(new DiscStructure.Track()
|
||||
{
|
||||
Number = 0xA0, //right?
|
||||
Control = EControlQ.None, //TODO - not accurate (take from track 1?)
|
||||
LBA = TOCRaw.LeadoutLBA.Sector
|
||||
});
|
||||
|
||||
//link track list
|
||||
for (int i = 0; i < session.Tracks.Count - 1; i++)
|
||||
{
|
||||
session.Tracks[i].NextTrack = session.Tracks[i + 1];
|
||||
}
|
||||
|
||||
//other misc fields
|
||||
session.InformationTrackCount = session.Tracks.Count - 2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.DiscSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Synthesizes the TOC from a set of raw entries.
|
||||
/// When a disc drive firmware reads the lead-in area, it builds this TOC from finding q-mode 1 sectors in the Q subchannel of the lead-in area.
|
||||
/// Question: I guess it must ignore q-mode != 1? what else would it do with it?
|
||||
/// </summary>
|
||||
public class Synthesize_DiscTOC_From_RawTOCEntries_Job
|
||||
{
|
||||
public IEnumerable<RawTOCEntry> Entries;
|
||||
public List<string> Log = new List<string>();
|
||||
public DiscTOC Result;
|
||||
|
||||
public void Run()
|
||||
{
|
||||
var job = this;
|
||||
DiscTOC ret = new DiscTOC();
|
||||
|
||||
//this is a dummy, for convenience in array indexing, so that track 1 is at array index 1
|
||||
ret.TOCItems[0].LBATimestamp = new Timestamp(0); //arguably could be -150, but let's not just yet
|
||||
ret.TOCItems[0].Control = 0;
|
||||
ret.TOCItems[0].Exists = false;
|
||||
|
||||
//just in case this doesnt get set...
|
||||
ret.FirstRecordedTrackNumber = 0;
|
||||
ret.LastRecordedTrackNumber = 0;
|
||||
|
||||
int maxFoundTrack = 0;
|
||||
|
||||
foreach (var te in job.Entries)
|
||||
{
|
||||
var q = te.QData;
|
||||
var point = q.q_index.DecimalValue;
|
||||
|
||||
//see ECMD-394 page 5-14 for info about point = 0xA0, 0xA1, 0xA2
|
||||
|
||||
if (point == 0x00)
|
||||
job.Log.Add("unexpected POINT=00 in lead-in Q-channel");
|
||||
else if (point == 255)
|
||||
throw new InvalidOperationException("point == 255");
|
||||
else if (point <= 99)
|
||||
{
|
||||
maxFoundTrack = Math.Max(maxFoundTrack, point);
|
||||
ret.TOCItems[point].LBATimestamp = new Timestamp(q.AP_Timestamp.Sector - 150); //RawTOCEntries contained an absolute time
|
||||
ret.TOCItems[point].Control = q.CONTROL;
|
||||
ret.TOCItems[point].Exists = true;
|
||||
}
|
||||
else if (point == 100) //0xA0 bcd
|
||||
{
|
||||
ret.FirstRecordedTrackNumber = q.ap_min.DecimalValue;
|
||||
if (q.ap_frame.DecimalValue != 0) job.Log.Add("PFRAME should be 0 for POINT=0xA0");
|
||||
if (q.ap_sec.DecimalValue == 0x00) ret.Session1Format = SessionFormat.Type00_CDROM_CDDA;
|
||||
else if (q.ap_sec.DecimalValue == 0x10) ret.Session1Format = SessionFormat.Type10_CDI;
|
||||
else if (q.ap_sec.DecimalValue == 0x20) ret.Session1Format = SessionFormat.Type20_CDXA;
|
||||
else job.Log.Add("Unrecognized session format: PSEC should be one of {0x00,0x10,0x20} for POINT=0xA0");
|
||||
}
|
||||
else if (point == 101) //0xA1 bcd
|
||||
{
|
||||
ret.LastRecordedTrackNumber = q.ap_min.DecimalValue;
|
||||
if (q.ap_sec.DecimalValue != 0) job.Log.Add("PSEC should be 0 for POINT=0xA1");
|
||||
if (q.ap_frame.DecimalValue != 0) job.Log.Add("PFRAME should be 0 for POINT=0xA1");
|
||||
}
|
||||
else if (point == 102) //0xA2 bcd
|
||||
{
|
||||
ret.TOCItems[100].LBATimestamp = new Timestamp(q.AP_Timestamp.Sector - 150); //RawTOCEntries contained an absolute time
|
||||
ret.TOCItems[100].Control = 0; //not clear what this should be
|
||||
ret.TOCItems[100].Exists = true;
|
||||
}
|
||||
}
|
||||
|
||||
//this is speculative:
|
||||
//well, nothing to be done here..
|
||||
if (ret.FirstRecordedTrackNumber == -1) { }
|
||||
if (ret.LastRecordedTrackNumber == -1) { ret.LastRecordedTrackNumber = maxFoundTrack; }
|
||||
if (ret.Session1Format == SessionFormat.None) ret.Session1Format = SessionFormat.Type00_CDROM_CDDA;
|
||||
|
||||
//if (!ret.LeadoutTimestamp.Valid) {
|
||||
// //we're DOOMED. we cant know the length of the last track without this....
|
||||
//}
|
||||
job.Result = ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
//TODO - generate correct Q subchannel CRC
|
||||
|
||||
namespace BizHawk.Emulation.DiscSystem
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// generates lead-out sectors according to very crude approximations
|
||||
/// TODO - this isnt being used right now
|
||||
/// </summary>
|
||||
public class Synthesize_LeadoutJob
|
||||
{
|
||||
public int Length;
|
||||
public Disc Disc;
|
||||
|
||||
public void Run()
|
||||
{
|
||||
//TODO: encode_mode2_form2_sector
|
||||
|
||||
var leadoutTs = Disc.TOC.LeadoutLBA;
|
||||
var lastTrackTOCItem = Disc.TOC.TOCItems[Disc.TOC.LastRecordedTrackNumber]; //NOTE: in case LastRecordedTrackNumber is al ie, this will malfunction
|
||||
|
||||
//leadout flags.. let's set them the same as the last track.
|
||||
//THIS IS NOT EXACTLY THE SAME WAY MEDNAFEN DOES IT
|
||||
EControlQ leadoutFlags = lastTrackTOCItem.Control;
|
||||
|
||||
//TODO - needs to be encoded as a certain mode (mode 2 form 2 for psx... i guess...)
|
||||
|
||||
for (int i = 0; i < Length; i++)
|
||||
{
|
||||
//var se = new SectorEntry(sz);
|
||||
//Disc.Sectors.Add(se);
|
||||
SubchannelQ sq = new SubchannelQ();
|
||||
|
||||
int track_relative_msf = i;
|
||||
sq.min = BCD2.FromDecimal(new Timestamp(track_relative_msf).MIN);
|
||||
sq.sec = BCD2.FromDecimal(new Timestamp(track_relative_msf).SEC);
|
||||
sq.frame = BCD2.FromDecimal(new Timestamp(track_relative_msf).FRAC);
|
||||
|
||||
int absolute_msf = i + leadoutTs.Sector;
|
||||
sq.ap_min = BCD2.FromDecimal(new Timestamp(absolute_msf + 150).MIN);
|
||||
sq.ap_sec = BCD2.FromDecimal(new Timestamp(absolute_msf + 150).SEC);
|
||||
sq.ap_frame = BCD2.FromDecimal(new Timestamp(absolute_msf + 150).FRAC);
|
||||
|
||||
sq.q_tno.DecimalValue = 0xAA; //special value for leadout
|
||||
sq.q_index.DecimalValue = 1;
|
||||
|
||||
byte ADR = 1;
|
||||
sq.SetStatus(ADR, leadoutFlags);
|
||||
|
||||
//TODO - actually stash the subQ
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,20 @@
|
|||
//lets leave some notes here until we've rewritten every possible thing -- error recovery, ECM file format, ISO filesystem, etc.
|
||||
//OLD ideas:
|
||||
/*
|
||||
* do some stuff asynchronously. for example, decoding mp3 sectors.
|
||||
* keep a list of sectors and the blob/offset from which they pull -- also whether the sector is available
|
||||
* if it is not available and something requests it then it will have to block while that sector gets generated
|
||||
* perhaps the blobs know how to resolve themselves and the requested sector can be immediately resolved (priority boost)
|
||||
* mp3 blobs should be hashed and dropped in %TEMP% as a wav decode
|
||||
*/
|
||||
|
||||
|
||||
//2048 bytes packed into 2352:
|
||||
//12 bytes sync(00 ff ff ff ff ff ff ff ff ff ff 00)
|
||||
//3 bytes sector address (min+A0),sec,frac //does this correspond to ccd `point` field in the TOC entries?
|
||||
//sector mode byte (0: silence; 1: 2048Byte mode (EDC,ECC,CIRC), 2: mode2 (could be 2336[vanilla mode2], 2048[xa mode2 form1], 2324[xa mode2 form2])
|
||||
//cue sheets may use mode1_2048 (and the error coding needs to be regenerated to get accurate raw data) or mode1_2352 (the entire sector is present)
|
||||
//audio is a different mode, seems to be just 2352 bytes with no sync, header or error correction. i guess the CIRC error correction is still there
|
||||
|
||||
|
||||
//we used this code
|
||||
//https://code.google.com/p/iso-parser/
|
||||
|
@ -36,3 +52,49 @@
|
|||
//check this for some iso stuff but seems like it ripped off corlett's code
|
||||
//http://lioneditor.googlecode.com/svn/trunk/utils/isopatcherv05/src/
|
||||
//http://code.ohloh.net/file?fid=185llKM04w3QCqwC2MdFgtUiQ94&cid=yPMRq_HKxUg&s=ecc_computeblock%28pSector%20%2B%200xC%2C%2052%2C%2043%2C%2086%2C%2088%2C%20pSector%20%2B%200x8C8%29&mp=1&ml=1&me=1&md=1&browser=Default#L106
|
||||
|
||||
|
||||
|
||||
|
||||
//https://books.google.com/books?id=caF_AAAAQBAJ&lpg=PA124&ots=OA9Ttj9CHZ&dq=disc%20TOC%20point%20A2&pg=PA124
|
||||
//http://www.staff.uni-mainz.de/tacke/scsi/SCSI2-14.html
|
||||
//http://www.pctechguide.com/iso-9660-data-format-for-cds-cd-roms-cd-rs-and-cd-rws
|
||||
//http://linux.die.net/man/1/cue2toc
|
||||
//http://cdemu.sourceforge.net/project.php#sf
|
||||
|
||||
//apparently cdrdao is the ultimate linux tool for doing this stuff but it doesnt support DAO96 (or other DAO modes) that would be necessary to extract P-Q subchannels
|
||||
//(cdrdao only supports R-W)
|
||||
|
||||
//here is a featureset list of windows cd burning programs (useful for cuesheet compatibility info)
|
||||
//http://www.dcsoft.com/cue_mastering_progs.htm
|
||||
|
||||
//good links
|
||||
//http://linux-sxs.org/bedtime/cdapi.html
|
||||
//http://en.wikipedia.org/wiki/Track_%28CD%29
|
||||
//http://docs.google.com/viewer?a=v&q=cache:imNKye05zIEJ:www.13thmonkey.org/documentation/SCSI/mmc-r10a.pdf+q+subchannel+TOC+format&hl=en&gl=us&pid=bl&srcid=ADGEEShtYqlluBX2lgxTL3pVsXwk6lKMIqSmyuUCX4RJ3DntaNq5vI2pCvtkyze-fumj7vvrmap6g1kOg5uAVC0IxwU_MRhC5FB0c_PQ2BlZQXDD7P3GeNaAjDeomelKaIODrhwOoFNb&sig=AHIEtbRXljAcFjeBn3rMb6tauHWjSNMYrw
|
||||
//http://digitalx.org/cue-sheet/examples/
|
||||
|
||||
//"qemu cdrom emulator"
|
||||
//http://www.koders.com/c/fid7171440DEC7C18B932715D671DEE03743111A95A.aspx
|
||||
|
||||
//less good
|
||||
//http://www.cyberciti.biz/faq/getting-volume-information-from-cds-iso-images/
|
||||
//http://www.cims.nyu.edu/cgi-systems/man.cgi?section=7I&topic=cdio
|
||||
|
||||
//some other docs
|
||||
//http://www.emutalk.net/threads/54428-Reference-for-8-byte-sub-header-used-in-CDROM-XA references http://ccsun.nchu.edu.tw/~imtech/cou...act%20Disc.pdf which is pretty cool
|
||||
|
||||
//here is an MIT licensed C mp3 decoder
|
||||
//http://core.fluendo.com/gstreamer/src/gst-fluendo-mp3/
|
||||
|
||||
/*information on saturn TOC and session data structures is on pdf page 58 of System Library User's Manual;
|
||||
* as seen in yabause, there are 1000 u32s in this format:
|
||||
* Ctrl[4bit] Adr[4bit] StartFrameAddressFAD[24bit] (nonexisting tracks are 0xFFFFFFFF)
|
||||
* Followed by Fist Track Information, Last Track Information..
|
||||
* Ctrl[4bit] Adr[4bit] FirstTrackNumber/LastTrackNumber[8bit] and then some stuff I dont understand
|
||||
* ..and Read Out Information:
|
||||
* Ctrl[4bit] Adr[4bit] ReadOutStartFrameAddress[24bit]
|
||||
*
|
||||
* Also there is some stuff about FAD of sessions.
|
||||
* This should be generated by the saturn core, but we need to make sure we pass down enough information to do it
|
||||
*/
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
. consider normalizing everything to use -150 for lead-in crappola ?
|
Loading…
Reference in New Issue