// Near Infinity - An Infinity Engine Browser and Editor // Copyright (C) 2001 - 2005 Jon Olav Hauglid // See LICENSE.txt for license information package org.infinity.resource.sav; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.file.AccessDeniedException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.infinity.datatype.TextString; import org.infinity.resource.Profile; import org.infinity.resource.Writeable; import org.infinity.resource.key.FileResourceEntry; import org.infinity.resource.key.ResourceEntry; import org.infinity.util.FileDeletionHook; import org.infinity.util.io.StreamUtils; public final class IOHandler implements Writeable { private final ResourceEntry entry; private final TextString header; private Path tempFolder; private List<SavResourceEntry> fileEntries; public IOHandler(ResourceEntry entry) throws Exception { this.entry = entry; ByteBuffer buffer = entry.getResourceBuffer(true); // ignoreOverride - no real effect header = new TextString(buffer, 0, 8, null); if (!header.toString().equals("SAV V1.0")) { throw new Exception("Unsupported version: " + header); } fileEntries = new ArrayList<SavResourceEntry>(); int offset = 8; while (offset < buffer.limit()) { SavResourceEntry fileEntry = new SavResourceEntry(buffer, offset); fileEntries.add(fileEntry); offset = fileEntry.getEndOffset(); } Collections.sort(fileEntries); } // --------------------- Begin Interface Writeable --------------------- @Override public void write(OutputStream os) throws IOException { header.write(os); for (final SavResourceEntry entry: fileEntries) { entry.write(os); } } // --------------------- End Interface Writeable --------------------- public void close() { if (tempFolder != null && Files.isDirectory(tempFolder)) { try (DirectoryStream<Path> dstream = Files.newDirectoryStream(tempFolder)) { for (final Path file: dstream) { try { Files.delete(file); } catch (AccessDeniedException e) { FileDeletionHook.getInstance().registerFile(file); } } } catch (IOException e) { e.printStackTrace(); } try { Files.delete(tempFolder); } catch (IOException e) { FileDeletionHook.getInstance().registerFile(tempFolder); e.printStackTrace(); } tempFolder = null; } } public void compress(List<? extends ResourceEntry> entries) throws Exception { fileEntries.clear(); for (int i = 0; i < entries.size(); i++) { fileEntries.add(new SavResourceEntry(entries.get(i))); } close(); } public List<ResourceEntry> decompress() throws Exception { List<ResourceEntry> entries = new ArrayList<ResourceEntry>(fileEntries.size()); tempFolder = createTempFolder(); if (tempFolder == null) { throw new Exception("Unable to create temp folder"); } Files.createDirectory(tempFolder); for (final SavResourceEntry entry: fileEntries) { Path file = tempFolder.resolve(entry.toString()); try (OutputStream os = StreamUtils.getOutputStream(file, true)) { StreamUtils.writeBytes(os, entry.decompress()); } entries.add(new FileResourceEntry(file)); } return entries; } public List<? extends ResourceEntry> getFileEntries() { return fileEntries; } public Path getTempFolder() { return tempFolder; } // Create a unique temp folder for current baldur.sav private Path createTempFolder() { for (int idx = 0; idx < Integer.MAX_VALUE; idx++) { Path path = Profile.getHomeRoot().resolve(String.format("%s.%03d", entry.getTreeFolder(), idx)); if (!Files.exists(path)) { return path; } } return null; } }