using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Extents;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Metadata;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Console;
using DiscImageChef.Core.Logging;
using DiscImageChef.Decoders.SCSI;
using DiscImageChef.Devices;
using Schemas;
using MediaType = DiscImageChef.CommonTypes.MediaType;
using TrackType = DiscImageChef.CommonTypes.Enums.TrackType;
using Version = DiscImageChef.CommonTypes.Interop.Version;
namespace DiscImageChef.Core.Devices.Dumping
{
public partial class Dump
{
static readonly byte[] FatSignature =
{
0x46, 0x41, 0x54, 0x31, 0x36, 0x20, 0x20, 0x20
};
static readonly byte[] IsoExtension =
{
0x49, 0x53, 0x4F
};
/// Dumps a CFW PlayStation Portable UMD
void PlayStationPortable()
{
if(!outputPlugin.SupportedMediaTypes.Contains(MediaType.MemoryStickDuo) &&
!outputPlugin.SupportedMediaTypes.Contains(MediaType.MemoryStickProDuo) &&
!outputPlugin.SupportedMediaTypes.Contains(MediaType.UMD))
{
dumpLog.WriteLine("Selected output plugin does not support MemoryStick Duo or UMD, cannot dump...");
StoppingErrorMessage?.
Invoke("Selected output plugin does not support MemoryStick Duo or UMD, cannot dump...");
return;
}
UpdateStatus?.Invoke("Checking if media is UMD or MemoryStick...");
dumpLog.WriteLine("Checking if media is UMD or MemoryStick...");
bool sense = dev.ModeSense6(out byte[] buffer, out _, false, ScsiModeSensePageControl.Current, 0,
dev.Timeout, out _);
if(sense)
{
dumpLog.WriteLine("Could not get MODE SENSE...");
StoppingErrorMessage?.Invoke("Could not get MODE SENSE...");
return;
}
Modes.DecodedMode? decoded = Modes.DecodeMode6(buffer, PeripheralDeviceTypes.DirectAccess);
if(!decoded.HasValue)
{
dumpLog.WriteLine("Could not decode MODE SENSE...");
StoppingErrorMessage?.Invoke("Could not decode MODE SENSE...");
return;
}
// UMDs are always write protected
if(!decoded.Value.Header.WriteProtected)
{
DumpMs();
return;
}
sense = dev.Read12(out buffer, out _, 0, false, true, false, false, 0, 512, 0, 1, false, dev.Timeout,
out _);
if(sense)
{
dumpLog.WriteLine("Could not read...");
StoppingErrorMessage?.Invoke("Could not read...");
return;
}
byte[] tmp = new byte[8];
Array.Copy(buffer, 0x36, tmp, 0, 8);
// UMDs are stored inside a FAT16 volume
if(!tmp.SequenceEqual(FatSignature))
{
DumpMs();
return;
}
ushort fatStart = (ushort)((buffer[0x0F] << 8) + buffer[0x0E]);
ushort sectorsPerFat = (ushort)((buffer[0x17] << 8) + buffer[0x16]);
ushort rootStart = (ushort)(sectorsPerFat * 2 + fatStart);
UpdateStatus?.Invoke($"Reading root directory in sector {rootStart}...");
dumpLog.WriteLine("Reading root directory in sector {0}...", rootStart);
sense = dev.Read12(out buffer, out _, 0, false, true, false, false, rootStart, 512, 0, 1, false,
dev.Timeout, out _);
if(sense)
{
StoppingErrorMessage?.Invoke("Could not read...");
dumpLog.WriteLine("Could not read...");
return;
}
tmp = new byte[3];
Array.Copy(buffer, 0x28, tmp, 0, 3);
if(!tmp.SequenceEqual(IsoExtension))
{
DumpMs();
return;
}
UpdateStatus?.Invoke($"FAT starts at sector {fatStart} and runs for {sectorsPerFat} sectors...");
dumpLog.WriteLine("FAT starts at sector {0} and runs for {1} sectors...", fatStart, sectorsPerFat);
UpdateStatus?.Invoke("Reading FAT...");
dumpLog.WriteLine("Reading FAT...");
byte[] fat = new byte[sectorsPerFat * 512];
uint position = 0;
while(position < sectorsPerFat)
{
uint transfer = 64;
if(transfer + position > sectorsPerFat)
transfer = sectorsPerFat - position;
sense = dev.Read12(out buffer, out _, 0, false, true, false, false, position + fatStart, 512, 0,
transfer, false, dev.Timeout, out _);
if(sense)
{
StoppingErrorMessage?.Invoke("Could not read...");
dumpLog.WriteLine("Could not read...");
return;
}
Array.Copy(buffer, 0, fat, position * 512, transfer * 512);
position += transfer;
}
UpdateStatus?.Invoke("Traversing FAT...");
dumpLog.WriteLine("Traversing FAT...");
ushort previousCluster = BitConverter.ToUInt16(fat, 4);
for(int i = 3; i < fat.Length / 2; i++)
{
ushort nextCluster = BitConverter.ToUInt16(fat, i * 2);
if(nextCluster == previousCluster + 1)
{
previousCluster = nextCluster;
continue;
}
if(nextCluster == 0xFFFF)
break;
DumpMs();
return;
}
if(outputPlugin is IWritableOpticalImage)
DumpUmd();
else
StoppingErrorMessage?.Invoke("The specified plugin does not support storing optical disc images.");
}
void DumpUmd()
{
const uint BLOCK_SIZE = 2048;
const MediaType DSK_TYPE = MediaType.UMD;
uint blocksToRead = 16;
double totalDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
DateTime start;
DateTime end;
bool sense = dev.Read12(out byte[] readBuffer, out _, 0, false, true, false, false, 0, 512, 0, 1, false,
dev.Timeout, out _);
if(sense)
{
dumpLog.WriteLine("Could not read...");
StoppingErrorMessage?.Invoke("Could not read...");
return;
}
ushort fatStart = (ushort)((readBuffer[0x0F] << 8) + readBuffer[0x0E]);
ushort sectorsPerFat = (ushort)((readBuffer[0x17] << 8) + readBuffer[0x16]);
ushort rootStart = (ushort)(sectorsPerFat * 2 + fatStart);
ushort rootSize = (ushort)(((readBuffer[0x12] << 8) + readBuffer[0x11]) * 32 / 512);
ushort umdStart = (ushort)(rootStart + rootSize);
UpdateStatus?.Invoke($"Reading root directory in sector {rootStart}...");
dumpLog.WriteLine("Reading root directory in sector {0}...", rootStart);
sense = dev.Read12(out readBuffer, out _, 0, false, true, false, false, rootStart, 512, 0, 1, false,
dev.Timeout, out _);
if(sense)
{
dumpLog.WriteLine("Could not read...");
StoppingErrorMessage?.Invoke("Could not read...");
return;
}
uint umdSizeInBytes = BitConverter.ToUInt32(readBuffer, 0x3C);
ulong blocks = umdSizeInBytes / BLOCK_SIZE;
string mediaPartNumber = Encoding.ASCII.GetString(readBuffer, 0, 11).Trim();
UpdateStatus?.
Invoke($"Media has {blocks} blocks of {BLOCK_SIZE} bytes/each. (for a total of {blocks * (ulong)BLOCK_SIZE} bytes)");
UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * BLOCK_SIZE} bytes).");
UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time.");
UpdateStatus?.Invoke($"Device reports {BLOCK_SIZE} bytes per logical block.");
UpdateStatus?.Invoke($"Device reports {2048} bytes per physical block.");
UpdateStatus?.Invoke($"SCSI device type: {dev.ScsiType}.");
UpdateStatus?.Invoke($"Media identified as {DSK_TYPE}.");
UpdateStatus?.Invoke($"Media part number is {mediaPartNumber}.");
dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * BLOCK_SIZE);
dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
dumpLog.WriteLine("Device reports {0} bytes per logical block.", BLOCK_SIZE);
dumpLog.WriteLine("Device reports {0} bytes per physical block.", 2048);
dumpLog.WriteLine("SCSI device type: {0}.", dev.ScsiType);
dumpLog.WriteLine("Media identified as {0}.", DSK_TYPE);
dumpLog.WriteLine("Media part number is {0}.", mediaPartNumber);
bool ret;
var mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, BLOCK_SIZE, blocksToRead);
var ibgLog = new IbgLog(outputPrefix + ".ibg", 0x0010);
ret = outputPlugin.Create(outputPath, DSK_TYPE, formatOptions, blocks, BLOCK_SIZE);
// Cannot create image
if(!ret)
{
dumpLog.WriteLine("Error creating output image, not continuing.");
dumpLog.WriteLine(outputPlugin.ErrorMessage);
StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine +
outputPlugin.ErrorMessage);
return;
}
start = DateTime.UtcNow;
double imageWriteDuration = 0;
(outputPlugin as IWritableOpticalImage).SetTracks(new List