351 lines
10 KiB
C#
351 lines
10 KiB
C#
using System;
|
|
using System.IO;
|
|
using static Vita.PsvImgTools.SceIoStat;
|
|
|
|
namespace Vita.PsvImgTools
|
|
{
|
|
public class PSVIMGFileStream : Stream
|
|
{
|
|
|
|
long length = 0;
|
|
long startPos = 0;
|
|
long endPos = 0;
|
|
long position = 0;
|
|
|
|
private PSVIMGStream psvStream;
|
|
public PSVIMGFileStream(PSVIMGStream psv, string Path)
|
|
{
|
|
psvStream = psv;
|
|
if (Path.First() == '/')
|
|
findFile(Path);
|
|
else
|
|
findFileMatching(Path);
|
|
}
|
|
|
|
public void WriteToFile(string FilePath)
|
|
{
|
|
using (FileStream fs = File.OpenWrite(FilePath))
|
|
{
|
|
fs.SetLength(0);
|
|
int written = 0;
|
|
long left = length - written;
|
|
byte[] work_buf;
|
|
Seek(0x00, SeekOrigin.Begin);
|
|
while (left > 0)
|
|
{
|
|
left = length - written;
|
|
if (left < 0x10000)
|
|
{
|
|
work_buf = new byte[left];
|
|
}
|
|
else
|
|
{
|
|
work_buf = new byte[0x10000];
|
|
}
|
|
Read(work_buf, 0x00, work_buf.Length);
|
|
fs.Write(work_buf, 0x00, work_buf.Length);
|
|
written += work_buf.Length;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
public override bool CanRead
|
|
{
|
|
get
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public override bool CanSeek
|
|
{
|
|
get
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
public override bool CanWrite
|
|
{
|
|
get
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
public override long Length
|
|
{
|
|
get
|
|
{
|
|
return length;
|
|
}
|
|
}
|
|
|
|
public override long Position
|
|
{
|
|
get
|
|
{
|
|
return position;
|
|
}
|
|
set
|
|
{
|
|
Seek(value, SeekOrigin.Begin);
|
|
}
|
|
}
|
|
|
|
public override void Flush()
|
|
{
|
|
psvStream.Flush();
|
|
}
|
|
private int _read(byte[] buffer, int offset, int count)
|
|
{
|
|
|
|
using (MemoryStream ms = new MemoryStream())
|
|
{
|
|
int read = 0;
|
|
while (true)
|
|
{
|
|
int remainBlock = Convert.ToInt32(psvStream.BlockRemaining - PSVIMGConstants.SHA256_BLOCK_SIZE);
|
|
int remainRead = count - read;
|
|
|
|
if (remainRead < remainBlock)
|
|
{
|
|
byte[] tmp = new byte[remainRead];
|
|
psvStream.Read(tmp, 0x00, remainRead);
|
|
ms.Write(tmp, 0x00, tmp.Length);
|
|
read += remainRead;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
byte[] tmp = new byte[remainBlock];
|
|
psvStream.Read(tmp, 0x00, remainBlock);
|
|
ms.Write(tmp, 0x00, tmp.Length);
|
|
psvStream.Seek(PSVIMGConstants.SHA256_BLOCK_SIZE, SeekOrigin.Current);
|
|
read += Convert.ToInt32(remainBlock);
|
|
}
|
|
}
|
|
ms.Seek(0x00, SeekOrigin.Begin);
|
|
return ms.Read(buffer, offset, count);
|
|
}
|
|
|
|
}
|
|
|
|
private long _seek(long amount, SeekOrigin orig)
|
|
{
|
|
long ToSeek = 0;
|
|
long SeekAdd = 0;
|
|
long remainBlock = psvStream.BlockRemaining - PSVIMGConstants.SHA256_BLOCK_SIZE;
|
|
while (true)
|
|
{
|
|
long remainSeek = amount - ToSeek;
|
|
|
|
if (remainSeek < remainBlock)
|
|
{
|
|
ToSeek += remainSeek;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ToSeek += remainBlock;
|
|
SeekAdd += PSVIMGConstants.SHA256_BLOCK_SIZE;
|
|
}
|
|
remainBlock = PSVIMGConstants.PSVIMG_BLOCK_SIZE;
|
|
}
|
|
ToSeek += SeekAdd;
|
|
return psvStream.Seek(ToSeek, orig);
|
|
}
|
|
public override int Read(byte[] buffer, int offset, int count)
|
|
{
|
|
if (startPos + count > endPos)
|
|
{
|
|
count = Convert.ToInt32(endPos);
|
|
}
|
|
return _read(buffer, offset, count);
|
|
}
|
|
|
|
public override long Seek(long offset, SeekOrigin origin)
|
|
{
|
|
if (origin == SeekOrigin.Begin)
|
|
{
|
|
if (offset <= endPos)
|
|
{
|
|
psvStream.Seek(startPos, SeekOrigin.Begin);
|
|
_seek(offset, SeekOrigin.Current);
|
|
position = offset;
|
|
return position;
|
|
}
|
|
else
|
|
{
|
|
throw new IndexOutOfRangeException("Offset is out of range of file");
|
|
}
|
|
|
|
}
|
|
else if (origin == SeekOrigin.Current)
|
|
{
|
|
if (offset <= endPos)
|
|
{
|
|
_seek(offset, SeekOrigin.Current);
|
|
position += offset;
|
|
return position;
|
|
}
|
|
else
|
|
{
|
|
throw new IndexOutOfRangeException("Offset is out of range of file");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
long realOffset = endPos + offset;
|
|
if (realOffset <= endPos)
|
|
{
|
|
_seek(realOffset, SeekOrigin.Begin);
|
|
return Position;
|
|
}
|
|
else
|
|
{
|
|
throw new IndexOutOfRangeException("Offset is out of range of file");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public override void SetLength(long value)
|
|
{
|
|
throw new NotImplementedException("PSVFileStream is Read-Only");
|
|
}
|
|
|
|
public override void Write(byte[] buffer, int offset, int count)
|
|
{
|
|
throw new NotImplementedException("PSVFileStream is Read-Only");
|
|
}
|
|
|
|
private ushort readUInt16()
|
|
{
|
|
byte[] intBuf = new byte[0x2];
|
|
_read(intBuf, 0x00, 0x02);
|
|
return BitConverter.ToUInt16(intBuf, 0);
|
|
}
|
|
private uint readUInt32()
|
|
{
|
|
byte[] intBuf = new byte[0x4];
|
|
_read(intBuf, 0x00, 0x04);
|
|
return BitConverter.ToUInt32(intBuf, 0);
|
|
}
|
|
private ulong readUInt64()
|
|
{
|
|
byte[] intBuf = new byte[0x8];
|
|
_read(intBuf, 0x00, 0x08);
|
|
return BitConverter.ToUInt64(intBuf, 0);
|
|
}
|
|
private SceDateTime readDatetime()
|
|
{
|
|
SceDateTime dateTime = new SceDateTime();
|
|
dateTime.Year = readUInt16();
|
|
dateTime.Month = readUInt16();
|
|
dateTime.Day = readUInt16();
|
|
dateTime.Hour = readUInt16();
|
|
dateTime.Minute = readUInt16();
|
|
dateTime.Second = readUInt16();
|
|
dateTime.Microsecond = readUInt32();
|
|
return dateTime;
|
|
}
|
|
private SceIoStat readStats()
|
|
{
|
|
SceIoStat stat = new SceIoStat();
|
|
stat.Mode = (Modes)readUInt32();
|
|
stat.Attributes = (AttributesEnum)readUInt32();
|
|
stat.Size = readUInt64();
|
|
stat.CreationTime = readDatetime();
|
|
stat.AccessTime = readDatetime();
|
|
stat.ModificaionTime = readDatetime();
|
|
for (int i = 0; i < stat.Private.Length; i++)
|
|
{
|
|
stat.Private[i] = readUInt32();
|
|
}
|
|
return stat;
|
|
}
|
|
private PsvImgHeader readHeader()
|
|
{
|
|
PsvImgHeader header = new PsvImgHeader();
|
|
header.SysTime = readUInt64();
|
|
header.Flags = readUInt64();
|
|
header.Statistics = readStats();
|
|
_read(header.bParentPath, 0x00, 256);
|
|
header.unk_16C = readUInt32();
|
|
_read(header.bPath, 0x00, 256);
|
|
_read(header.Padding, 0x00, 904);
|
|
_read(header.bEnd, 0x00, 12);
|
|
return header;
|
|
}
|
|
|
|
public override void Close()
|
|
{
|
|
psvStream.Dispose();
|
|
base.Close();
|
|
}
|
|
|
|
private PsvImgTailer readTailer()
|
|
{
|
|
PsvImgTailer tailer = new PsvImgTailer();
|
|
tailer.Flags = readUInt64();
|
|
_read(tailer.Padding, 0x00, 1004);
|
|
_read(tailer.bEnd, 0x00, 12);
|
|
return tailer;
|
|
}
|
|
|
|
private void findFileMatching(string path)
|
|
{
|
|
_seek(0x00, SeekOrigin.Begin);
|
|
while (psvStream.Position < psvStream.Length)
|
|
{
|
|
PsvImgHeader header = readHeader();
|
|
long size = (long)header.Statistics.Size;
|
|
long padding = PSVIMGPadding.GetPadding(size);
|
|
|
|
if (header.Path.Contains(path, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
length = size;
|
|
startPos = psvStream.Position;
|
|
endPos = startPos + length;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
_seek(size + padding, SeekOrigin.Current);
|
|
PsvImgTailer tailer = readTailer();
|
|
}
|
|
|
|
}
|
|
throw new FileNotFoundException("Cannot find file specified");
|
|
|
|
}
|
|
private void findFile(string path)
|
|
{
|
|
_seek(0x00, SeekOrigin.Begin);
|
|
while (psvStream.Position < psvStream.Length)
|
|
{
|
|
PsvImgHeader header = readHeader();
|
|
long size = (long)header.Statistics.Size;
|
|
long padding = PSVIMGPadding.GetPadding(size);
|
|
|
|
if (header.Path.Equals(path, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
length = size;
|
|
startPos = psvStream.Position;
|
|
endPos = startPos + length;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
_seek(size + padding, SeekOrigin.Current);
|
|
PsvImgTailer tailer = readTailer();
|
|
}
|
|
|
|
}
|
|
throw new FileNotFoundException("Cannot find file specified");
|
|
|
|
}
|
|
}
|
|
}
|