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
///
/// Device
/// Path to the device
/// Prefix for output data files
/// Plugin for output file
/// How many times to retry
/// Force to continue dump whenever possible
/// Store whatever data the drive returned on error
/// Stop dump on first error
/// Information for dump resuming
/// Dump logger
/// Encoding to use when analyzing dump
/// Path to output file
/// Formats to pass to output file plugin
/// Existing sidecar
/// How many sectors to skip on errors
/// Don't create metadata sidecar
/// Don't trim errors
/// If you asked to dump long sectors from a SCSI Streaming device
public void PlayStationPortable(Device dev, string devicePath,
IWritableImage outputPlugin, ushort retryPasses,
bool force, bool persistent,
bool stopOnError, ref Resume resume,
ref DumpLog dumpLog, Encoding encoding,
string outputPrefix, string outputPath,
Dictionary formatOptions, CICMMetadataType preSidecar,
uint skip, bool nometadata,
bool notrim)
{
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(dev, devicePath, outputPlugin, retryPasses, force, persistent, stopOnError,
ref resume,
ref dumpLog, encoding, outputPrefix, outputPath, formatOptions, preSidecar, skip,
nometadata,
notrim);
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(dev, devicePath, outputPlugin, retryPasses, force, persistent, stopOnError,
ref resume,
ref dumpLog, encoding, outputPrefix, outputPath, formatOptions, preSidecar, skip,
nometadata,
notrim);
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(dev, devicePath, outputPlugin, retryPasses, force, persistent, stopOnError,
ref resume,
ref dumpLog, encoding, outputPrefix, outputPath, formatOptions, preSidecar, skip,
nometadata,
notrim);
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(dev, devicePath, outputPlugin, retryPasses, force, persistent, stopOnError,
ref resume,
ref dumpLog, encoding, outputPrefix, outputPath, formatOptions, preSidecar, skip,
nometadata,
notrim);
return;
}
if(outputPlugin is IWritableOpticalImage opticalPlugin)
DumpUmd(dev, devicePath, opticalPlugin, retryPasses, force, persistent, stopOnError,
ref resume,
ref dumpLog, encoding, outputPrefix, outputPath, formatOptions, preSidecar, skip,
nometadata,
notrim);
else StoppingErrorMessage?.Invoke("The specified plugin does not support storing optical disc images.");
}
void DumpUmd(Device dev, string devicePath, IWritableOpticalImage outputPlugin,
ushort retryPasses, bool force,
bool persistent, bool stopOnError, ref Resume resume,
ref DumpLog dumpLog, Encoding encoding,
string outputPrefix, string outputPath, Dictionary formatOptions,
CICMMetadataType preSidecar, uint skip, bool nometadata,
bool notrim)
{
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;
bool aborted = false;
DateTime start;
DateTime end;
System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;
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;
MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, BLOCK_SIZE, blocksToRead);
IbgLog 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.SetTracks(new List