0

refac: PackFile

This commit is contained in:
2026-02-11 23:18:27 +03:00
parent 3fc318dee2
commit b9d31e6fbe
4 changed files with 204 additions and 143 deletions

View File

@@ -23,6 +23,9 @@ import lwjake2.Globals;
import lwjake2.game.Cmd;
import lwjake2.game.cvar_t;
import lwjake2.sys.Sys;
import ru.di9.lwjake2.PackFile;
import ru.di9.lwjake2.PackFileEntry;
import ru.di9.lwjake2.PackLoader;
import java.io.File;
import java.io.FileInputStream;
@@ -30,12 +33,11 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
/**
* FS
@@ -53,32 +55,6 @@ public final class FS extends Globals {
* ==================================================
*/
public static class packfile_t {
static final int SIZE = 64;
static final int NAME_SIZE = 56;
String name; // char name[56]
int filepos, filelen;
public String toString() {
return name + " [ length: " + filelen + " pos: " + filepos + " ]";
}
}
public static class pack_t {
String filename;
RandomAccessFile handle;
ByteBuffer backbuffer;
int numfiles;
Hashtable<String, packfile_t> files; // with packfile_t entries
}
public static String fs_gamedir;
private static String fs_userdir;
@@ -103,7 +79,7 @@ public final class FS extends Globals {
public static class searchpath_t {
String filename;
pack_t pack; // only one of filename or pack will be used
PackFile pack; // only one of filename or pack will be used
searchpath_t next;
}
@@ -166,7 +142,7 @@ public final class FS extends Globals {
public static int FileLength(String filename) {
searchpath_t search;
String netpath;
pack_t pak;
PackFile pak;
filelink_t link;
file_from_pak = 0;
@@ -194,20 +170,20 @@ public final class FS extends Globals {
// look through all the pak file elements
pak = search.pack;
filename = filename.toLowerCase();
packfile_t entry = pak.files.get(filename);
PackFileEntry entry = pak.getFiles().get(filename);
if (entry != null) {
// found it!
file_from_pak = 1;
Com.DPrintf("PackFile: " + pak.filename + " : " + filename
Com.DPrintf("PackFile: " + pak.getFilename() + " : " + filename
+ '\n');
// open a new file on the pakfile
File file = new File(pak.filename);
File file = new File(pak.getFilename());
if (!file.canRead()) {
Com.Error(Defines.ERR_FATAL, "Couldn't reopen "
+ pak.filename);
+ pak.getFilename());
}
return entry.filelen;
return entry.getFileLen();
}
} else {
// check a file in the directory tree
@@ -238,7 +214,7 @@ public final class FS extends Globals {
throws IOException {
searchpath_t search;
String netpath;
pack_t pak;
PackFile pak;
filelink_t link;
File file = null;
@@ -269,25 +245,25 @@ public final class FS extends Globals {
// look through all the pak file elements
pak = search.pack;
filename = filename.toLowerCase();
packfile_t entry = pak.files.get(filename);
PackFileEntry entry = pak.getFiles().get(filename);
if (entry != null) {
// found it!
file_from_pak = 1;
//Com.DPrintf ("PackFile: " + pak.filename + " : " +
// filename + '\n');
file = new File(pak.filename);
file = new File(pak.getFilename());
if (!file.canRead())
Com.Error(Defines.ERR_FATAL, "Couldn't reopen "
+ pak.filename);
if (pak.handle == null || !pak.handle.getFD().valid()) {
+ pak.getFilename());
if (pak.getHandle() == null || !pak.getHandle().getFD().valid()) {
// hold the pakfile handle open
pak.handle = new RandomAccessFile(pak.filename, "r");
pak.setHandle(new RandomAccessFile(pak.getFilename(), "r"));
}
// open a new file on the pakfile
RandomAccessFile raf = new RandomAccessFile(file, "r");
raf.seek(entry.filepos);
raf.seek(entry.getFilePos());
return raf;
}
@@ -390,7 +366,7 @@ public final class FS extends Globals {
public static ByteBuffer LoadMappedFile(String filename) {
searchpath_t search;
String netpath;
pack_t pak;
PackFile pak;
filelink_t link;
File file = null;
@@ -431,32 +407,32 @@ public final class FS extends Globals {
// look through all the pak file elements
pak = search.pack;
filename = filename.toLowerCase();
packfile_t entry = pak.files.get(filename);
PackFileEntry entry = pak.getFiles().get(filename);
if (entry != null) {
// found it!
file_from_pak = 1;
//Com.DPrintf ("PackFile: " + pak.filename + " : " +
// filename + '\n');
file = new File(pak.filename);
file = new File(pak.getFilename());
if (!file.canRead())
Com.Error(Defines.ERR_FATAL, "Couldn't reopen "
+ pak.filename);
if (pak.handle == null || !pak.handle.getFD().valid()) {
+ pak.getFilename());
if (pak.getHandle() == null || !pak.getHandle().getFD().valid()) {
// hold the pakfile handle open
pak.handle = new RandomAccessFile(pak.filename, "r");
pak.setHandle(new RandomAccessFile(pak.getFilename(), "r"));
}
// open a new file on the pakfile
if (pak.backbuffer == null) {
channel = pak.handle.getChannel();
pak.backbuffer = channel.map(
if (pak.getBackBuffer() == null) {
channel = pak.getHandle().getChannel();
pak.setBackBuffer(channel.map(
FileChannel.MapMode.READ_ONLY, 0,
pak.handle.length());
pak.getHandle().length()));
channel.close();
}
pak.backbuffer.position(entry.filepos);
buffer = pak.backbuffer.slice();
buffer.limit(entry.filelen);
pak.getBackBuffer().position(entry.getFilePos());
buffer = pak.getBackBuffer().slice();
buffer.limit(entry.getFileLen());
return buffer;
}
} else {
@@ -508,86 +484,6 @@ public final class FS extends Globals {
static final int MAX_FILES_IN_PACK = 4096;
// buffer for C-Strings char[56]
static byte[] tmpText = new byte[packfile_t.NAME_SIZE];
/*
* LoadPackFile
*
* Takes an explicit (not game tree related) path to a pak file.
*
* Loads the header and directory, adding the files at the beginning of the
* list so they override previous pack files.
*/
static pack_t LoadPackFile(String packfile) {
dpackheader_t header;
Hashtable<String, packfile_t> newfiles;
RandomAccessFile file;
int numpackfiles = 0;
pack_t pack = null;
// unsigned checksum;
//
try {
file = new RandomAccessFile(packfile, "r");
FileChannel fc = file.getChannel();
ByteBuffer packhandle = fc.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
packhandle.order(ByteOrder.LITTLE_ENDIAN);
fc.close();
if (packhandle == null || packhandle.limit() < 1)
return null;
//
header = new dpackheader_t();
header.ident = packhandle.getInt();
header.dirofs = packhandle.getInt();
header.dirlen = packhandle.getInt();
if (header.ident != IDPAKHEADER)
Com.Error(Defines.ERR_FATAL, packfile + " is not a packfile");
numpackfiles = header.dirlen / packfile_t.SIZE;
if (numpackfiles > MAX_FILES_IN_PACK)
Com.Error(Defines.ERR_FATAL, packfile + " has " + numpackfiles
+ " files");
newfiles = new Hashtable<String, packfile_t>(numpackfiles);
packhandle.position(header.dirofs);
// parse the directory
packfile_t entry = null;
for (int i = 0; i < numpackfiles; i++) {
packhandle.get(tmpText);
entry = new packfile_t();
entry.name = new String(tmpText).trim();
entry.filepos = packhandle.getInt();
entry.filelen = packhandle.getInt();
newfiles.put(entry.name.toLowerCase(), entry);
}
} catch (IOException e) {
Com.DPrintf(e.getMessage() + '\n');
return null;
}
pack = new pack_t();
pack.filename = new String(packfile);
pack.handle = file;
pack.numfiles = numpackfiles;
pack.files = newfiles;
Com.Printf("Added packfile " + packfile + " (" + numpackfiles
+ " files)\n");
return pack;
}
/*
* AddGameDirectory
*
@@ -597,7 +493,7 @@ public final class FS extends Globals {
static void AddGameDirectory(String dir) {
int i;
searchpath_t search;
pack_t pak;
PackFile pak;
String pakfile;
fs_gamedir = new String(dir);
@@ -622,9 +518,17 @@ public final class FS extends Globals {
if (!(new File(pakfile).canRead()))
continue;
pak = LoadPackFile(pakfile);
if (pak == null)
try {
Optional<PackFile> opt = PackLoader.INSTANCE.loadFromFile(pakfile);
if (opt.isEmpty()) {
continue;
}
pak = opt.get();
Com.Printf("Added packfile " + pak.getFilename() + " (" + pak.getNumFiles()
+ " files)\n");
} catch (IOException e) {
throw new RuntimeException(e);
}
search = new searchpath_t();
search.pack = pak;
@@ -695,13 +599,13 @@ public final class FS extends Globals {
while (fs_searchpaths != fs_base_searchpaths) {
if (fs_searchpaths.pack != null) {
try {
fs_searchpaths.pack.handle.close();
fs_searchpaths.pack.getHandle().close();
} catch (IOException e) {
Com.DPrintf(e.getMessage() + '\n');
}
// clear the hashtable
fs_searchpaths.pack.files.clear();
fs_searchpaths.pack.files = null;
fs_searchpaths.pack.getFiles().clear();
fs_searchpaths.pack.setFiles(null);
fs_searchpaths.pack = null;
}
next = fs_searchpaths.next;
@@ -840,7 +744,7 @@ public final class FS extends Globals {
if (s == fs_base_searchpaths)
Com.Printf("----------\n");
if (s.pack != null)
Com.Printf(s.pack.filename + " (" + s.pack.numfiles
Com.Printf(s.pack.getFilename() + " (" + s.pack.getNumFiles()
+ " files)\n");
else
Com.Printf(s.filename + '\n');

View File

@@ -0,0 +1,53 @@
package ru.di9.lwjake2;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.Map;
public class PackFile {
private final String filename;
private final int numFiles;
private Map<String, PackFileEntry> files;
private RandomAccessFile handle;
private ByteBuffer backBuffer;
public PackFile(String filename, RandomAccessFile handle, int numFiles, Map<String, PackFileEntry> files) {
this.filename = filename;
this.handle = handle;
this.numFiles = numFiles;
this.files = files;
}
public String getFilename() {
return filename;
}
public int getNumFiles() {
return numFiles;
}
public RandomAccessFile getHandle() {
return handle;
}
public void setHandle(RandomAccessFile handle) {
this.handle = handle;
}
public Map<String, PackFileEntry> getFiles() {
return files;
}
public void setFiles(Map<String, PackFileEntry> files) {
this.files = files;
}
public ByteBuffer getBackBuffer() {
return backBuffer;
}
public void setBackBuffer(ByteBuffer backBuffer) {
this.backBuffer = backBuffer;
}
}

View File

@@ -0,0 +1,30 @@
package ru.di9.lwjake2;
public class PackFileEntry {
private final String name;
private final int filePos;
private final int fileLen;
public PackFileEntry(String name, int filePos, int fileLen) {
this.name = name;
this.filePos = filePos;
this.fileLen = fileLen;
}
public String getName() {
return name;
}
public int getFilePos() {
return filePos;
}
public int getFileLen() {
return fileLen;
}
@Override
public String toString() {
return name + " [ length: " + fileLen + " pos: " + filePos + " ]";
}
}

View File

@@ -0,0 +1,74 @@
package ru.di9.lwjake2;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class PackLoader {
public static final PackLoader INSTANCE = new PackLoader();
private static final String FLAG_READ_ONLY = "r";
private static final int IDPAKHEADER = (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P');
private static final int SIZE = 64;
private static final int MAX_FILES_IN_PACK = 4096;
private static final int NAME_SIZE = 56;
/*
* LoadPackFile
*
* Takes an explicit (not game tree related) path to a pak file.
*
* Loads the header and directory, adding the files at the beginning of the
* list so they override previous pack files.
*/
public Optional<PackFile> loadFromFile(String file) throws IOException {
RandomAccessFile raf = new RandomAccessFile(file, FLAG_READ_ONLY);
ByteBuffer packHandle;
try(FileChannel channel = raf.getChannel()) {
packHandle = channel.map(FileChannel.MapMode.READ_ONLY, 0, raf.length());
if (packHandle == null || packHandle.limit() < 1) {
raf.close();
return Optional.empty();
}
packHandle.order(ByteOrder.LITTLE_ENDIAN);
}
int headerIdent = packHandle.getInt();
if (headerIdent != IDPAKHEADER) {
raf.close();
throw new IOException(file + " is not a packfile");
}
int headerDirofs = packHandle.getInt();
int headerDirlen = packHandle.getInt();
int numPackFiles = headerDirlen / SIZE;
if (numPackFiles > MAX_FILES_IN_PACK) {
raf.close();
throw new IOException(file + " has " + numPackFiles + " files");
}
Map<String, PackFileEntry> newFiles = new HashMap<>(numPackFiles);
packHandle.position(headerDirofs);
PackFileEntry entry;
byte[] tmpBuff = new byte[NAME_SIZE];
for (int i = 0; i < numPackFiles; i++) {
packHandle.get(tmpBuff);
String name = new String(tmpBuff).trim();
int filePos = packHandle.getInt();
int fileLen = packHandle.getInt();
entry = new PackFileEntry(name, filePos, fileLen);
newFiles.put(entry.getName().toLowerCase(), entry);
}
return Optional.of(new PackFile(file, raf, numPackFiles, newFiles));
}
}