mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
263 lines
9.0 KiB
C#
263 lines
9.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
using Aaru.CommonTypes.Enums;
|
|
using Aaru.CommonTypes.Structs;
|
|
|
|
namespace Aaru.Images;
|
|
|
|
public sealed partial class AaruFormat
|
|
{
|
|
Dictionary<int, byte> _trackFlags;
|
|
Dictionary<int, string> _trackIsrcs;
|
|
List<Track> _tracks;
|
|
|
|
// AARU_EXPORT int32_t AARU_CALL aaruf_get_tracks(const void *context, uint8_t *buffer, size_t *length)
|
|
[LibraryImport("libaaruformat", EntryPoint = "aaruf_get_tracks", SetLastError = true)]
|
|
[UnmanagedCallConv(CallConvs = [typeof(CallConvStdcall)])]
|
|
private static partial Status aaruf_get_tracks(IntPtr context, byte[] buffer, ref nuint length);
|
|
|
|
// AARU_EXPORT int32_t AARU_CALL aaruf_set_tracks(void *context, TrackEntry *tracks, const int count)
|
|
[LibraryImport("libaaruformat", EntryPoint = "aaruf_set_tracks", SetLastError = true)]
|
|
[UnmanagedCallConv(CallConvs = [typeof(CallConvStdcall)])]
|
|
private static partial Status aaruf_set_tracks(IntPtr context, [In] byte[] tracks, int count);
|
|
|
|
#region IWritableOpticalImage Members
|
|
|
|
/// <inheritdoc />
|
|
public List<Track> Tracks
|
|
{
|
|
get
|
|
{
|
|
if(_tracks is not null) return _tracks;
|
|
|
|
nuint length = 0;
|
|
|
|
Status res = aaruf_get_tracks(_context, null, ref length);
|
|
|
|
if(res != Status.BufferTooSmall)
|
|
{
|
|
ErrorMessage = StatusToErrorMessage(res);
|
|
|
|
return null;
|
|
}
|
|
|
|
var buffer = new byte[length];
|
|
res = aaruf_get_tracks(_context, buffer, ref length);
|
|
|
|
if(res != Status.Ok)
|
|
{
|
|
ErrorMessage = StatusToErrorMessage(res);
|
|
|
|
return null;
|
|
}
|
|
|
|
// Marshal the received buffer, that's an array of TrackEntry structures into a list of Track
|
|
int trackEntrySize = Marshal.SizeOf<TrackEntry>();
|
|
var trackCount = (int)(length / (ulong)trackEntrySize);
|
|
|
|
_tracks = new List<Track>(trackCount);
|
|
_trackFlags = [];
|
|
_trackIsrcs = [];
|
|
|
|
IntPtr ptr = Marshal.AllocHGlobal(trackCount * trackEntrySize);
|
|
|
|
try
|
|
{
|
|
Marshal.Copy(buffer, 0, ptr, (int)length);
|
|
|
|
for(var i = 0; i < trackCount; i++)
|
|
{
|
|
var trackPtr = IntPtr.Add(ptr, i * trackEntrySize);
|
|
TrackEntry entry = Marshal.PtrToStructure<TrackEntry>(trackPtr);
|
|
|
|
var track = new Track
|
|
{
|
|
Sequence = entry.Sequence,
|
|
Type = entry.Type,
|
|
StartSector = (ulong)entry.Start,
|
|
EndSector = (ulong)entry.End,
|
|
Pregap = (ulong)entry.Pregap,
|
|
Session = entry.Session,
|
|
FileType = "BINARY"
|
|
};
|
|
|
|
_tracks.Add(track);
|
|
|
|
if(entry.Type == TrackType.Data) continue;
|
|
|
|
_trackFlags[entry.Sequence] = entry.Flags;
|
|
|
|
if(!string.IsNullOrEmpty(entry.Isrc)) _trackIsrcs[entry.Sequence] = entry.Isrc;
|
|
|
|
if(_trackFlags.Count > 0 && !_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdTrackFlags))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdTrackFlags);
|
|
|
|
if(_trackIsrcs.Count > 0 && !_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdTrackIsrc))
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdTrackIsrc);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
_tracks = null;
|
|
#pragma warning disable ERP022
|
|
}
|
|
#pragma warning restore ERP022
|
|
finally
|
|
{
|
|
Marshal.FreeHGlobal(ptr);
|
|
}
|
|
|
|
if(_tracks is null) return _tracks;
|
|
|
|
foreach(Track track in Tracks.OrderBy(static t => t.StartSector))
|
|
{
|
|
switch(track.Sequence)
|
|
{
|
|
case 0:
|
|
track.Pregap = 150;
|
|
track.Indexes[0] = -150;
|
|
track.Indexes[1] = (int)track.StartSector;
|
|
|
|
continue;
|
|
case 1 when Tracks.All(static t => t.Sequence != 0):
|
|
track.Pregap = 150;
|
|
track.Indexes[0] = -150;
|
|
track.Indexes[1] = (int)track.StartSector;
|
|
|
|
continue;
|
|
}
|
|
|
|
if(track.Pregap > 0)
|
|
{
|
|
track.Indexes[0] = (int)track.StartSector;
|
|
track.Indexes[1] = (int)(track.StartSector + track.Pregap);
|
|
}
|
|
else
|
|
track.Indexes[1] = (int)track.StartSector;
|
|
}
|
|
|
|
Track[] tracks = Tracks.ToArray();
|
|
|
|
foreach(Track trk in tracks)
|
|
{
|
|
ReadSector(trk.StartSector, false, out byte[] sector, out _);
|
|
trk.BytesPerSector = sector?.Length ?? (trk.Type == TrackType.Audio ? 2352 : 2048);
|
|
|
|
ErrorNumber errno = ReadSectorLong(trk.StartSector, false, out byte[] longSector, out _);
|
|
|
|
if(errno == ErrorNumber.NoError)
|
|
trk.RawBytesPerSector = longSector.Length;
|
|
else
|
|
trk.RawBytesPerSector = sector?.Length ?? (trk.Type == TrackType.Audio ? 2352 : 2048);
|
|
|
|
if(_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubchannel))
|
|
trk.SubchannelType = TrackSubchannelType.Raw;
|
|
}
|
|
|
|
return _tracks;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public List<Session> Sessions
|
|
{
|
|
get
|
|
{
|
|
if(Tracks is null) return null;
|
|
|
|
List<Session> sessions = [];
|
|
|
|
for(var i = 1; i <= Tracks.Max(static t => t.Session); i++)
|
|
{
|
|
sessions.Add(new Session
|
|
{
|
|
Sequence = (ushort)i,
|
|
StartTrack = Tracks.Where(t => t.Session == i).Min(static t => t.Sequence),
|
|
EndTrack = Tracks.Where(t => t.Session == i).Max(static t => t.Sequence),
|
|
StartSector = Tracks.Where(t => t.Session == i).Min(static t => t.StartSector),
|
|
EndSector = Tracks.Where(t => t.Session == i).Max(static t => t.EndSector)
|
|
});
|
|
}
|
|
|
|
return sessions;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public List<Track> GetSessionTracks(Session session) => Tracks?.Where(t => t.Session == session.Sequence).ToList();
|
|
|
|
/// <inheritdoc />
|
|
public List<Track> GetSessionTracks(ushort session) => Tracks?.Where(t => t.Session == session).ToList();
|
|
|
|
/// <inheritdoc />
|
|
public bool SetTracks(List<Track> tracks)
|
|
{
|
|
_tracks = tracks;
|
|
List<TrackEntry> trackEntries = [];
|
|
|
|
_trackFlags ??= [];
|
|
_trackIsrcs ??= [];
|
|
|
|
foreach(Track track in Tracks)
|
|
{
|
|
_trackFlags.TryGetValue((byte)track.Sequence, out byte flags);
|
|
_trackIsrcs.TryGetValue((byte)track.Sequence, out string isrc);
|
|
|
|
if((flags & (int)CdFlags.DataTrack) == 0 && track.Type != TrackType.Audio) flags += (byte)CdFlags.DataTrack;
|
|
|
|
trackEntries.Add(new TrackEntry
|
|
{
|
|
Sequence = (byte)track.Sequence,
|
|
Type = track.Type,
|
|
Start = (long)track.StartSector,
|
|
End = (long)track.EndSector,
|
|
Pregap = (long)track.Pregap,
|
|
Session = (byte)track.Session,
|
|
Isrc = isrc,
|
|
Flags = flags
|
|
});
|
|
|
|
switch(track.Indexes.ContainsKey(0))
|
|
{
|
|
case false when track.Pregap > 0:
|
|
track.Indexes[0] = (int)track.StartSector;
|
|
track.Indexes[1] = (int)(track.StartSector + track.Pregap);
|
|
|
|
break;
|
|
case false when !track.Indexes.ContainsKey(1):
|
|
track.Indexes[0] = (int)track.StartSector;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If there are tracks build the tracks block
|
|
var blockStream = new MemoryStream();
|
|
|
|
foreach(TrackEntry entry in trackEntries)
|
|
{
|
|
IntPtr structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf<TrackEntry>());
|
|
|
|
var structureBytes = new byte[Marshal.SizeOf<TrackEntry>()];
|
|
Marshal.StructureToPtr(entry, structurePointer, true);
|
|
|
|
Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length);
|
|
|
|
Marshal.FreeHGlobal(structurePointer);
|
|
blockStream.Write(structureBytes, 0, structureBytes.Length);
|
|
}
|
|
|
|
byte[] blockBytes = blockStream.ToArray();
|
|
Status res = aaruf_set_tracks(_context, blockBytes, trackEntries.Count);
|
|
|
|
ErrorMessage = StatusToErrorMessage(res);
|
|
|
|
return res == Status.Ok;
|
|
}
|
|
|
|
#endregion
|
|
} |