BSA(String filePath, boolean load) throws FileNotFoundException, IOException, BadParameter { this.filePath = filePath; in.openFile(filePath); if (!in.extractString(0, 3).equals("BSA") || in.extractInt(1, 4) != 104) { throw new BadParameter("Was not a BSA file of version 104: " + filePath); } offset = in.extractInt(0, 4); archiveFlags = new LFlags(in.extract(0, 4)); folderCount = in.extractInt(0, 4); folders = new HashMap<>(folderCount); fileCount = in.extractInt(0, 4); folderNameLength = in.extractInt(0, 4); fileNameLength = in.extractInt(0, 4); fileFlags = new LFlags(in.extract(0, 4)); if (SPGlobal.debugBSAimport && SPGlobal.logging()) { SPGlobal.logSpecial(LogTypes.BSA, header, "|==================>"); SPGlobal.logSpecial(LogTypes.BSA, header, "| Imported " + filePath); SPGlobal.logSpecial( LogTypes.BSA, header, "| Offset " + offset + ", archiveFlags: " + archiveFlags); SPGlobal.logSpecial( LogTypes.BSA, header, "| hasDirectoryNames: " + archiveFlags.get(0) + ", hasFileNames: " + archiveFlags.get(1) + ", compressed: " + archiveFlags.get(2)); SPGlobal.logSpecial( LogTypes.BSA, header, "| FolderCount: " + Ln.prettyPrintHex(folderCount) + ", FileCount: " + Ln.prettyPrintHex(fileCount)); SPGlobal.logSpecial( LogTypes.BSA, header, "| totalFolderNameLength: " + Ln.prettyPrintHex(folderNameLength) + ", totalFileNameLength: " + Ln.prettyPrintHex(fileNameLength)); SPGlobal.logSpecial(LogTypes.BSA, header, "| fileFlags: " + fileFlags.toString()); SPGlobal.logSpecial(LogTypes.BSA, header, "|==================>"); } if (load) { loadFolders(); } }
static ArrayList<String> processINIline(String in) { if (SPGlobal.logging()) { SPGlobal.logSpecial(LogTypes.BSA, header, "Processing line: " + in); } ArrayList<String> out = new ArrayList<>(); int index = in.indexOf("="); if (index != -1) { in = in.substring(index + 1); String[] split = in.split(","); for (String s : split) { s = s.trim(); if (!s.isEmpty()) { out.add(s); } } } return out; }
/** * Returns BSA object associated with modlisting, or null if there is none. * * @param m * @return */ public static BSA getBSA(ModListing m) { if (pluginLoadOrder.containsKey(m)) { return pluginLoadOrder.get(m); } File bsaPath = new File(SPGlobal.pathToData + Ln.changeFileTypeTo(m.print(), "bsa")); if (bsaPath.exists()) { try { BSA bsa = new BSA(bsaPath, false); pluginLoadOrder.put(m, bsa); return bsa; } catch (IOException | BadParameter ex) { logBSAError(m.printNoSuffix() + ".bsa", ex); return null; } } if (SPGlobal.logging()) { SPGlobal.logSpecial( LogTypes.BSA, header, " BSA skipped because it didn't exist: " + bsaPath); } return null; }
static void loadPluginLoadOrder() { if (pluginsLoaded) { return; } if (SPGlobal.logging()) { SPGlobal.logSpecial(LogTypes.BSA, header, "Loading in active plugin BSA headers."); } try { ArrayList<ModListing> activeMods = SPImporter.getActiveModList(); for (ModListing m : activeMods) { if (!pluginLoadOrder.containsKey(m)) { BSA bsa = getBSA(m); if (bsa != null) { pluginLoadOrder.put(m, bsa); } } } } catch (IOException ex) { SPGlobal.logException(ex); } pluginsLoaded = true; }
/** * @param filePath File to query for. * @return The used file, which prioritizes loose files first, and then BSAs.<br> * NOTE: Not fully sophisticated yet for prioritizing between BSAs. * @throws IOException * @throws DataFormatException */ public static LShrinkArray getUsedFile(String filePath) throws IOException, DataFormatException { File outsideBSA = new File(SPGlobal.pathToData + filePath); if (outsideBSA.isFile()) { SPGlobal.logSpecial(LogTypes.BSA, header, "Loaded from loose files: " + outsideBSA.getPath()); return new LShrinkArray(outsideBSA); } else { Iterator<BSA> bsas = BSA.iterator(); BSA tmp, bsa = null; while (bsas.hasNext()) { tmp = bsas.next(); if (tmp.hasFile(filePath)) { bsa = tmp; } } if (bsa != null) { if (SPGlobal.logging()) { SPGlobal.logSpecial( LogTypes.BSA, header, "Loaded from BSA " + bsa.getFilePath() + ": " + filePath); } return bsa.getFile(filePath); } } return null; }
void loadHeader(LShrinkArray in) throws BadParameter { // Gamebryo header if (SPGlobal.debugNIFimport) { SPGlobal.logSync(header, "Loading nif file"); } if (!in.getString(20).equals("Gamebryo File Format")) { byte first = in.extract(1)[0]; if (!in.extractString((int) first, 20).equals("Gamebryo File Format")) { throw new BadParameter(fileName + " was not a NIF file."); } } in.extractLine(); // BlockTypes numBlocks = in.extractInt(9, 4); if (SPGlobal.debugNIFimport && SPGlobal.logging()) { SPGlobal.logSync(header, "Num Blocks: " + numBlocks); } in.skip(in.extractInt(4, 1)); // Author name in.skip(in.extractInt(1)); // Export Info 1 in.skip(in.extractInt(1)); // Export Info 2 int numBlockTypes = in.extractInt(2); if (SPGlobal.debugNIFimport && SPGlobal.logging()) { SPGlobal.logSync(header, "Num Block Types: " + numBlockTypes); } blockTypes = new ArrayList<>(numBlockTypes); for (int i = 0; i < numBlockTypes; i++) { String blockType = in.extractString(in.extractInt(4)); blockTypes.add(blockType); if (SPGlobal.debugNIFimport && SPGlobal.logging()) { SPGlobal.logSync(header, " Added block type[" + i + "]: " + blockType); } } // Blocks list if (SPGlobal.debugNIFimport && SPGlobal.logging()) { SPGlobal.logSync(header, "Block Type list: "); } nodes = new ArrayList<>(numBlocks); for (int i = 0; i < numBlocks; i++) { int type = in.extractInt(2); Node n = new Node(NodeType.SPvalueOf(blockTypes.get(type))); n.number = i; nodes.add(n); if (SPGlobal.debugNIFimport && SPGlobal.logging()) { SPGlobal.logSync( header, " Block list[" + i + "] has block type: " + type + ", " + blockTypes.get(type)); } } // Block lengths for (int i = 0; i < numBlocks; i++) { nodes.get(i).size = in.extractInt(4); } if (SPGlobal.debugNIFimport && SPGlobal.logging()) { SPGlobal.logSync(header, "Block headers: "); for (int i = 0; i < numBlocks; i++) { SPGlobal.logSync( header, " [" + i + "]: " + nodes.get(i).type + ", length: " + Ln.prettyPrintHex(nodes.get(i).size)); } } // Strings if (SPGlobal.debugNIFimport && SPGlobal.logging()) { SPGlobal.logSync(header, "Block Titles: "); } int numStrings = in.extractInt(4); in.skip(4); // max Length string ArrayList<String> strings = new ArrayList<>(numStrings); for (int i = 0; i < numStrings; i++) { strings.add(in.extractString(in.extractInt(4))); } in.skip(4); // unknown int for (int i = 0; i < numBlocks; i++) { nodes.get(i).data = new LShrinkArray(in, nodes.get(i).size); in.skip(nodes.get(i).size); } // Set titles for (int i = 0; i < numBlocks; i++) { NodeType type = nodes.get(i).type; if (type == NodeType.NINODE || type == NodeType.NITRISHAPE || type == NodeType.BSINVMARKER || type == NodeType.BSBEHAVIORGRAPHEXTRADATA) { Node n = nodes.get(i); int stringIndex = n.data.getInts(0, 4)[0]; n.title = strings.get(stringIndex); if (SPGlobal.debugNIFimport && SPGlobal.logging()) { SPGlobal.log( header, " [" + i + "]: " + nodes.get(i).type + ", string: " + nodes.get(i).title); } } } }
static void loadResourceLoadOrder() { if (resourceLoadOrder != null) { return; } try { ArrayList<String> resources = new ArrayList<>(); boolean line1 = false, line2 = false; try { File ini = SPGlobal.getSkyrimINI(); if (SPGlobal.logging()) { SPGlobal.logSpecial(LogTypes.BSA, header, "Loading in BSA list from Skyrim.ini: " + ini); } LInChannel input = new LInChannel(ini); String line = ""; // First line while (input.available() > 0 && !line.toUpperCase().contains("SRESOURCEARCHIVELIST")) { line = input.extractLine(); } if (line.toUpperCase().contains("SRESOURCEARCHIVELIST2")) { line2 = true; resources.addAll(processINIline(line)); } else { line1 = true; resources.addAll(0, processINIline(line)); } // Second line line = ""; while (input.available() > 0 && !line.toUpperCase().contains("SRESOURCEARCHIVELIST")) { line = Ln.cleanLine(input.extractLine(), "#"); } if (line.toUpperCase().contains("SRESOURCEARCHIVELIST2")) { line2 = true; resources.addAll(processINIline(line)); } else { line1 = true; resources.addAll(0, processINIline(line)); } } catch (IOException e) { SPGlobal.logException(e); } if (!line1 || !line2) { // Assume standard BSA listing if (!resources.contains("Skyrim - Misc.bsa")) { resources.add("Skyrim - Misc.bsa"); } if (!resources.contains("Skyrim - Shaders.bsa")) { resources.add("Skyrim - Shaders.bsa"); } if (!resources.contains("Skyrim - Textures.bsa")) { resources.add("Skyrim - Textures.bsa"); } if (!resources.contains("Skyrim - Interface.bsa")) { resources.add("Skyrim - Interface.bsa"); } if (!resources.contains("Skyrim - Animations.bsa")) { resources.add("Skyrim - Animations.bsa"); } if (!resources.contains("Skyrim - Meshes.bsa")) { resources.add("Skyrim - Meshes.bsa"); } if (!resources.contains("Skyrim - Sounds.bsa")) { resources.add("Skyrim - Sounds.bsa"); } if (!resources.contains("Skyrim - Sounds.bsa")) { resources.add("Skyrim - Voices.bsa"); } if (!resources.contains("Skyrim - Sounds.bsa")) { resources.add("Skyrim - VoicesExtra.bsa"); } } if (SPGlobal.logging()) { SPGlobal.logSpecial(LogTypes.BSA, header, "BSA resource load order: "); for (String s : resources) { SPGlobal.logSpecial(LogTypes.BSA, header, " " + s); } SPGlobal.logSpecial(LogTypes.BSA, header, "Loading in their headers."); } // Get BSAs loaded from all active pluging's plugin.ini files ArrayList<ModListing> activeMods = SPImporter.getActiveModList(); for (ModListing m : activeMods) { File pluginIni = new File(SPGlobal.pathToData + Ln.changeFileTypeTo(m.print(), "ini")); if (pluginIni.exists()) { LInChannel input = new LInChannel(pluginIni); String line = ""; // First line while (input.available() > 0 && !line.toUpperCase().contains("SRESOURCEARCHIVELIST")) { line = input.extractLine(); } if (line.toUpperCase().contains("SRESOURCEARCHIVELIST2")) { resources.addAll(processINIline(line)); } else { resources.addAll(0, processINIline(line)); } // Second line line = ""; while (input.available() > 0 && !line.toUpperCase().contains("SRESOURCEARCHIVELIST")) { line = Ln.cleanLine(input.extractLine(), "#"); } if (line.toUpperCase().contains("SRESOURCEARCHIVELIST2")) { resources.addAll(processINIline(line)); } else { resources.addAll(0, processINIline(line)); } } } resourceLoadOrder = new ArrayList<>(resources.size()); for (String s : resources) { File bsaPath = new File(SPGlobal.pathToData + s); if (bsaPath.exists()) { try { if (SPGlobal.logging()) { SPGlobal.logSpecial(LogTypes.BSA, header, "Loading: " + bsaPath); } BSA bsa = new BSA(bsaPath, false); resourceLoadOrder.add(bsa); } catch (BadParameter | FileNotFoundException ex) { logBSAError(s, ex); } } else if (SPGlobal.logging()) { SPGlobal.logSpecial( LogTypes.BSA, header, " BSA skipped because it didn't exist: " + bsaPath); } } } catch (IOException ex) { SPGlobal.logException(ex); } }
final void loadFolders() { if (loaded) { return; } loaded = true; if (SPGlobal.logging()) { SPGlobal.logSpecial(LogTypes.BSA, header, "|============================================"); SPGlobal.logSpecial(LogTypes.BSA, header, "|============ Loading " + this + " ============"); SPGlobal.logSpecial(LogTypes.BSA, header, "|============================================"); } try { String fileName; int fileCounter = 0; in.pos(offset); LShrinkArray folderData = new LShrinkArray(in.extract(0, folderCount * 16)); posAtFilenames(); LShrinkArray fileNames = new LShrinkArray(in.extract(0, fileNameLength)); for (int i = 0; i < folderCount; i++) { BSAFolder folder = new BSAFolder(); folderData.skip(8); // Skip Hash folder.setFileCount(folderData.extractInt(4)); folder.dataPos = folderData.extractInt(4); posAtFolder(folder); folder.name = in.extractString(0, in.read() - 1) + "\\"; folder.name = folder.name.toUpperCase(); in.skip(1); folders.put(folder.name, folder); if (SPGlobal.debugBSAimport && SPGlobal.logging()) { SPGlobal.logSpecial(LogTypes.BSA, header, "Loaded folder: " + folder.name); } for (int j = 0; j < folder.fileCount; j++) { BSAFileRef f = new BSAFileRef(); f.size = in.extractInt(8, 3); // Skip Hash LFlags sizeFlag = new LFlags(in.extract(1)); f.flippedCompression = sizeFlag.get(6); f.dataOffset = in.extractLong(0, 4); fileName = fileNames.extractString(); folder.files.put(fileName.toUpperCase(), f); if (SPGlobal.logging()) { SPGlobal.logSpecial( LogTypes.BSA, header, " " + fileName + ", size: " + Ln.prettyPrintHex(f.size) + ", offset: " + Ln.prettyPrintHex(f.dataOffset)); fileCounter++; } } } if (SPGlobal.logging()) { if (SPGlobal.debugBSAimport) { SPGlobal.logSpecial(LogTypes.BSA, header, "Loaded " + fileCounter + " files."); } SPGlobal.logSpecial(LogTypes.BSA, header, "Loaded BSA: " + getFilePath()); } } catch (Exception e) { SPGlobal.logException(e); SPGlobal.logError("BSA", "Skipped BSA " + this); bad = true; } }