/* Copyright 2019-2020 Dmitry Isaenko This file is part of Konogonka. Konogonka is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Konogonka is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Konogonka. If not, see . */ package libKonogonka.Tools.PFS0; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.Arrays; import static libKonogonka.LoperConverter.*; public class PFS0Provider implements IPFS0Provider{ private long rawFileDataStart; // Where data starts, excluding header, string table etc. private String magic; private int filesCount; private int stringTableSize; private byte[] padding; private PFS0subFile[] pfs0subFiles; private File file; public PFS0Provider(File fileWithPfs0) throws Exception{ this(fileWithPfs0, 0); } public PFS0Provider(File fileWithPfs0, long pfs0offsetPosition) throws Exception{ file = fileWithPfs0; RandomAccessFile raf = new RandomAccessFile(fileWithPfs0, "r"); // TODO: replace to bufferedInputStream raf.seek(pfs0offsetPosition); byte[] fileStartingBytes = new byte[0x10]; // Read PFS0Provider, files count, header, padding (4 zero bytes) if (raf.read(fileStartingBytes) != 0x10){ raf.close(); throw new Exception("PFS0Provider: Unable to read starting bytes"); } // Check PFS0Provider magic = new String(fileStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII); if (! magic.equals("PFS0")){ raf.close(); throw new Exception("PFS0Provider: Bad magic"); } // Get files count filesCount = getLEint(fileStartingBytes, 0x4); if (filesCount <= 0 ) { raf.close(); throw new Exception("PFS0Provider: Files count is too small"); } // Get string table stringTableSize = getLEint(fileStartingBytes, 0x8); if (stringTableSize <= 0 ){ raf.close(); throw new Exception("PFS0Provider: String table is too small"); } padding = Arrays.copyOfRange(fileStartingBytes, 0xc, 0x10); //--------------------------------------------------------------------------------------------------------- pfs0subFiles = new PFS0subFile[filesCount]; long[] offsetsSubFiles = new long[filesCount]; long[] sizesSubFiles = new long[filesCount]; int[] strTableOffsets = new int[filesCount]; byte[][] zeroBytes = new byte[filesCount][]; byte[] fileEntryTable = new byte[0x18]; for (int i=0; i= pfs0subFiles.length) { throw new Exception("PFS0Provider -> getPfs0subFilePipedInpStream(): Requested sub file doesn't exists"); } PipedOutputStream streamOut = new PipedOutputStream(); Thread workerThread; PipedInputStream streamIn = new PipedInputStream(streamOut); workerThread = new Thread(() -> { System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Executing thread"); try { long subFileRealPosition = rawFileDataStart + pfs0subFiles[subFileNumber].getOffset(); BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); if (bis.skip(subFileRealPosition) != subFileRealPosition) { System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to skip requested offset"); return; } int readPice = 8388608; // 8mb NOTE: consider switching to 1mb 1048576 long readFrom = 0; long realFileSize = pfs0subFiles[subFileNumber].getSize(); byte[] readBuf; while (readFrom < realFileSize) { if (realFileSize - readFrom < readPice) readPice = Math.toIntExact(realFileSize - readFrom); // it's safe, I guarantee readBuf = new byte[readPice]; if (bis.read(readBuf) != readPice) { System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to read requested size from file."); return; } streamOut.write(readBuf); readFrom += readPice; } bis.close(); streamOut.close(); } catch (IOException ioe) { System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to provide stream"); ioe.printStackTrace(); } System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Thread died"); }); workerThread.start(); return streamIn; } /** * Some sugar * */ @Override public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) throws Exception { for (int i = 0; i < pfs0subFiles.length; i++){ if (pfs0subFiles[i].getName().equals(subFileName)) return getProviderSubFilePipedInpStream(i); } return null; } }