/* * SourceFileLoader.java * Copyright 2010 Connor Petty <cpmeister@users.sourceforge.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Created on Apr 30, 2010, 10:02:45 PM */ package pcgen.persistence; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Observable; import java.util.Observer; import java.util.Set; import java.util.TreeSet; import java.util.logging.Handler; import java.util.logging.LogRecord; import pcgen.base.formula.base.LegalScope; import pcgen.base.util.AbstractMapToList; import pcgen.base.util.FormatManager; import pcgen.base.util.HashMapToList; import pcgen.cdom.base.Constants; import pcgen.cdom.content.ContentDefinition; import pcgen.cdom.content.fact.FactDefinition; import pcgen.cdom.content.factset.FactSetDefinition; import pcgen.cdom.enumeration.IntegerKey; import pcgen.cdom.enumeration.ListKey; import pcgen.cdom.enumeration.MapKey; import pcgen.cdom.enumeration.ObjectKey; import pcgen.cdom.enumeration.SourceFormat; import pcgen.cdom.enumeration.StringKey; import pcgen.cdom.enumeration.Type; import pcgen.cdom.util.CControl; import pcgen.cdom.util.ControlUtilities; import pcgen.core.Ability; import pcgen.core.AbilityCategory; import pcgen.core.ArmorProf; import pcgen.core.Campaign; import pcgen.core.CustomData; import pcgen.core.DataSet; import pcgen.core.Deity; import pcgen.core.Description; import pcgen.core.Domain; import pcgen.core.Equipment; import pcgen.core.EquipmentList; import pcgen.core.EquipmentModifier; import pcgen.core.GameMode; import pcgen.core.Globals; import pcgen.core.Language; import pcgen.core.PCAlignment; import pcgen.core.PCCheck; import pcgen.core.PCStat; import pcgen.core.PCTemplate; import pcgen.core.Race; import pcgen.core.SettingsHandler; import pcgen.core.ShieldProf; import pcgen.core.SizeAdjustment; import pcgen.core.Skill; import pcgen.core.SystemCollections; import pcgen.core.WeaponProf; import pcgen.core.analysis.EqModAttachment; import pcgen.core.prereq.PrereqHandler; import pcgen.core.prereq.Prerequisite; import pcgen.core.spell.Spell; import pcgen.facade.core.CampaignFacade; import pcgen.facade.core.DataSetFacade; import pcgen.facade.core.SourceSelectionFacade; import pcgen.facade.core.UIDelegate; import pcgen.facade.util.DefaultListFacade; import pcgen.io.PCGFile; import pcgen.persistence.lst.AbilityCategoryLoader; import pcgen.persistence.lst.AbilityLoader; import pcgen.persistence.lst.BioSetLoader; import pcgen.persistence.lst.CampaignLoader; import pcgen.persistence.lst.CampaignSourceEntry; import pcgen.persistence.lst.CompanionModLoader; import pcgen.persistence.lst.FeatLoader; import pcgen.persistence.lst.GenericLoader; import pcgen.persistence.lst.GenericLocalVariableLoader; import pcgen.persistence.lst.GlobalModifierLoader; import pcgen.persistence.lst.KitLoader; import pcgen.persistence.lst.LstFileLoader; import pcgen.persistence.lst.LstLineFileLoader; import pcgen.persistence.lst.LstObjectFileLoader; import pcgen.persistence.lst.PCClassLoader; import pcgen.persistence.lst.SourceEntry; import pcgen.persistence.lst.VariableLoader; import pcgen.rules.context.AbstractReferenceContext; import pcgen.rules.context.LoadContext; import pcgen.rules.context.LoadValidator; import pcgen.rules.context.ReferenceContextUtilities; import pcgen.rules.context.VariableContext; import pcgen.rules.persistence.CDOMControlLoader; import pcgen.rules.persistence.DynamicLoader; import pcgen.rules.persistence.TableLoader; import pcgen.system.ConfigurationSettings; import pcgen.system.LanguageBundle; import pcgen.system.PCGenSettings; import pcgen.system.PCGenTask; import pcgen.util.Logging; /** * * @author Connor Petty <cpmeister@users.sourceforge.net> */ public class SourceFileLoader extends PCGenTask implements Observer { /* * File lists */ private final AbstractMapToList<ListKey<?>, CampaignSourceEntry> fileLists = new HashMapToList<>(); /* * Loaders */ private final PCClassLoader classLoader = new PCClassLoader(); private final LstObjectFileLoader<Language> languageLoader = new GenericLoader<>(Language.class); private final LstLineFileLoader abilityCategoryLoader = new AbilityCategoryLoader(); private final LstLineFileLoader companionModLoader = new CompanionModLoader(); private final LstObjectFileLoader kitLoader = new KitLoader(); private final LstLineFileLoader bioLoader = new BioSetLoader(); private final LstObjectFileLoader abilityLoader = new AbilityLoader(); private final LstObjectFileLoader featLoader = new FeatLoader(); private final LstObjectFileLoader<PCTemplate> templateLoader = new GenericLoader<>(PCTemplate.class); private final LstObjectFileLoader<Equipment> equipmentLoader = new GenericLocalVariableLoader<>(Equipment.class, "EQUIPMENT"); private final LstObjectFileLoader<EquipmentModifier> eqModLoader = new GenericLocalVariableLoader<>(EquipmentModifier.class, "EQUIPMENT.PART"); private final LstObjectFileLoader<Race> raceLoader = new GenericLoader<>(Race.class); private final LstObjectFileLoader<Skill> skillLoader = new GenericLocalVariableLoader<>(Skill.class, "SKILL"); private final LstObjectFileLoader<WeaponProf> wProfLoader = new GenericLoader<>(WeaponProf.class); private final LstObjectFileLoader<ArmorProf> aProfLoader = new GenericLoader<>(ArmorProf.class); private final LstObjectFileLoader<ShieldProf> sProfLoader = new GenericLoader<>(ShieldProf.class); private final LstObjectFileLoader<Deity> deityLoader = new GenericLoader<>(Deity.class); private final LstObjectFileLoader<Domain> domainLoader = new GenericLoader<>(Domain.class); private final LstObjectFileLoader<PCCheck> savesLoader = new GenericLocalVariableLoader<>(PCCheck.class, "SAVE"); private final LstObjectFileLoader<PCAlignment> alignmentLoader = new GenericLoader<>(PCAlignment.class); private final LstObjectFileLoader<PCStat> statLoader = new GenericLocalVariableLoader<>(PCStat.class, "STAT"); private final LstObjectFileLoader<SizeAdjustment> sizeLoader = new GenericLocalVariableLoader<>(SizeAdjustment.class, "SIZE"); private final LstObjectFileLoader<Spell> spellLoader = new GenericLoader<>(Spell.class); private final LstLineFileLoader dataControlLoader = new CDOMControlLoader(); private final VariableLoader variableLoader = new VariableLoader(); private final LstLineFileLoader tableLoader = new TableLoader(); private final LstLineFileLoader globalModifierLoader = new GlobalModifierLoader(); private final LstLineFileLoader dynamicLoader = new DynamicLoader(); /* * Other properties */ private final Collection<CampaignSourceEntry> licenseFiles = new ArrayList<>(); private final Collection<String> sourcesSet = new TreeSet<>(); private final Collection<Campaign> loadedCampaigns = new ArrayList<>(); private final StringBuilder sec15 = new StringBuilder(500); private final StringBuilder licensesToDisplayString = new StringBuilder(500); private final StringBuilder matureCampaigns = new StringBuilder(100); private final CampaignSourceEntry globalCampaign; private boolean showD20 = false; private boolean showLicensed = true; private boolean showMature = false; private boolean showOGL = false; private final List<Campaign> selectedCampaigns; private final GameMode selectedGame; private DataSet dataset = null; private int progress = 0; private final UIDelegate uiDelegate; public SourceFileLoader(SourceSelectionFacade selection, UIDelegate delegate) { //Ensure object lists are not null (but rather empty) for (ListKey<CampaignSourceEntry> lk : CampaignLoader.OBJECT_FILE_LISTKEY) { fileLists.initializeListFor(lk); } this.uiDelegate = delegate; selectedCampaigns = new ArrayList<>(); for (CampaignFacade campaign : selection.getCampaigns()) { Campaign camp = Globals.getCampaignKeyed(campaign.getName()); selectedCampaigns.add(camp); } selectedGame = SystemCollections.getGameModeNamed(selection.getGameMode() .get().getName()); globalCampaign = new CampaignSourceEntry(new Campaign(), URI.create("file:/System%20Configuration%20Document")); abilityCategoryLoader.addObserver(this); bioLoader.addObserver(this); companionModLoader.addObserver(this); deityLoader.addObserver(this); domainLoader.addObserver(this); equipmentLoader.addObserver(this); eqModLoader.addObserver(this); abilityLoader.addObserver(this); featLoader.addObserver(this); kitLoader.addObserver(this); languageLoader.addObserver(this); classLoader.addObserver(this); raceLoader.addObserver(this); skillLoader.addObserver(this); spellLoader.addObserver(this); templateLoader.addObserver(this); wProfLoader.addObserver(this); aProfLoader.addObserver(this); sProfLoader.addObserver(this); savesLoader.addObserver(this); alignmentLoader.addObserver(this); statLoader.addObserver(this); dataControlLoader.addObserver(this); dynamicLoader.addObserver(this); } @Override public void execute() { Globals.emptyLists(); SettingsHandler.setGame(selectedGame.getName()); Globals.initPreferences(); Globals.emptyLists(); Handler handler = new LoadHandler(); Logging.registerHandler(handler); try { loadCampaigns(); } catch (PersistenceLayerException e) { Logging.errorPrint("Failed to load sources", e); uiDelegate.showErrorMessage(Constants.APPLICATION_NAME, "Failed to load sources, see log for details."); } Logging.removeHandler(handler); } public String getOGL() { return sec15.toString(); } public String getLicenses() { return licensesToDisplayString.toString(); } /** * @return a list of licenses read from the campaign license files */ public Iterable<String> getOtherLicenses() { Collection<String> licenses = new ArrayList<>(); for (CampaignSourceEntry licenseFile : licenseFiles) { try { StringBuilder dataBuffer = LstFileLoader.readFromURI(licenseFile.getURI()); licenses.add(dataBuffer.toString()); } catch (PersistenceLayerException e) { Logging.errorPrint("Could not read license at " + licenseFile, e); } } return licenses; } public String getMatureInfo() { return matureCampaigns.toString(); } /** * * @return the dataSet that contains all of the data that was loaded */ public DataSetFacade getDataSetFacade() { return dataset; } /** * @return total files to load */ private int countTotalFilesToLoad() { int count = 0; for (ListKey<?> lk : fileLists.getKeySet()) { count += fileLists.sizeOfListFor(lk); } return count; } private void addCustomFilesToStartOfList() { CampaignSourceEntry tempSource = null; // The dummy campaign for custom data. Campaign customCampaign = new Campaign(); customCampaign.setName("Custom"); customCampaign.addToListFor(ListKey.DESCRIPTION, new Description( "Custom data")); // // Add the custom bioset file to the start of the list if it exists // File bioSetFile = new File(CustomData.customBioSetFilePath(true)); if (bioSetFile.exists()) { tempSource = new CampaignSourceEntry(customCampaign, bioSetFile.toURI()); fileLists.removeFromListFor(ListKey.FILE_BIO_SET, tempSource); fileLists.addToListFor(ListKey.FILE_BIO_SET, 0, tempSource); } // // Add the custom class file to the start of the list if it exists // File classFile = new File(CustomData.customClassFilePath(true)); if (classFile.exists()) { tempSource = new CampaignSourceEntry(customCampaign, classFile.toURI()); fileLists.removeFromListFor(ListKey.FILE_CLASS, tempSource); fileLists.addToListFor(ListKey.FILE_CLASS, 0, tempSource); } // // Add the custom deity file to the start of the list if it exists // File deityFile = new File(CustomData.customDeityFilePath(true)); if (deityFile.exists()) { tempSource = new CampaignSourceEntry(customCampaign, deityFile.toURI()); fileLists.removeFromListFor(ListKey.FILE_DEITY, tempSource); fileLists.addToListFor(ListKey.FILE_DEITY, 0, tempSource); } // // Add the custom domain file to the start of the list if it exists // File domainFile = new File(CustomData.customDomainFilePath(true)); if (domainFile.exists()) { tempSource = new CampaignSourceEntry(customCampaign, domainFile.toURI()); fileLists.removeFromListFor(ListKey.FILE_DOMAIN, tempSource); fileLists.addToListFor(ListKey.FILE_DOMAIN, 0, tempSource); } // // Add the custom ability file to the start of the list if it exists // File abilityFile = new File(CustomData.customAbilityFilePath(true)); if (abilityFile.exists()) { tempSource = new CampaignSourceEntry(customCampaign, abilityFile.toURI()); fileLists.removeFromListFor(ListKey.FILE_ABILITY, tempSource); fileLists.addToListFor(ListKey.FILE_ABILITY, 0, tempSource); } // // Add the custom feat file to the start of the list if it exists // File featFile = new File(CustomData.customFeatFilePath(true)); if (featFile.exists()) { tempSource = new CampaignSourceEntry(customCampaign, featFile.toURI()); fileLists.removeFromListFor(ListKey.FILE_FEAT, tempSource); fileLists.addToListFor(ListKey.FILE_FEAT, 0, tempSource); } // // Add the custom language file to the start of the list if it exists // File languageFile = new File(CustomData.customLanguageFilePath(true)); if (languageFile.exists()) { tempSource = new CampaignSourceEntry(customCampaign, languageFile.toURI()); fileLists.removeFromListFor(ListKey.FILE_LANGUAGE, tempSource); fileLists.addToListFor(ListKey.FILE_LANGUAGE, 0, tempSource); } // // Add the custom race file to the start of the list if it exists // File raceFile = new File(CustomData.customRaceFilePath(true)); if (raceFile.exists()) { tempSource = new CampaignSourceEntry(customCampaign, raceFile.toURI()); fileLists.removeFromListFor(ListKey.FILE_RACE, tempSource); fileLists.addToListFor(ListKey.FILE_RACE, 0, tempSource); } // // Add the custom skill file to the start of the list if it exists // File skillFile = new File(CustomData.customSkillFilePath(true)); if (skillFile.exists()) { tempSource = new CampaignSourceEntry(customCampaign, skillFile.toURI()); fileLists.removeFromListFor(ListKey.FILE_SKILL, tempSource); fileLists.addToListFor(ListKey.FILE_SKILL, 0, tempSource); } // // Add the custom spell file to the start of the list if it exists // File spellFile = new File(CustomData.customSpellFilePath(true)); if (spellFile.exists()) { tempSource = new CampaignSourceEntry(customCampaign, spellFile.toURI()); fileLists.removeFromListFor(ListKey.FILE_SPELL, tempSource); fileLists.addToListFor(ListKey.FILE_SPELL, 0, tempSource); } // // Add the custom template file to the start of the list if it exists // File templateFile = new File(CustomData.customTemplateFilePath(true)); if (templateFile.exists()) { tempSource = new CampaignSourceEntry(customCampaign, templateFile.toURI()); fileLists.removeFromListFor(ListKey.FILE_TEMPLATE, tempSource); fileLists.addToListFor(ListKey.FILE_TEMPLATE, 0, tempSource); } } private void addDefaultEquipmentMods(LoadContext context) throws PersistenceLayerException { URI uri = URI.create("file:/" + eqModLoader.getClass().getName() + ".java"); context.setSourceURI(uri); SourceEntry source = new CampaignSourceEntry(new Campaign(), uri); LoadContext subContext = context.dropIntoContext("EQUIPMENT"); String aLine; aLine = "Add Type\tKEY:ADDTYPE\tTYPE:ALL\tCOST:0\tNAMEOPT:NONAME\tSOURCELONG:PCGen Internal\tCHOOSE:EQBUILDER.EQTYPE|COUNT=ALL|TITLE=desired TYPE(s)"; eqModLoader.parseLine(subContext, null, aLine, source); // // Add internal equipment modifier for adding weapon/armor types to // equipment // aLine = Constants.INTERNAL_EQMOD_WEAPON + "\tTYPE:Weapon\tVISIBLE:NO\tCHOOSE:NOCHOICE\tNAMEOPT:NONAME"; eqModLoader.parseLine(subContext, null, aLine, source); aLine = Constants.INTERNAL_EQMOD_ARMOR + "\tTYPE:Armor\tVISIBLE:NO\tCHOOSE:NOCHOICE\tNAMEOPT:NONAME"; eqModLoader.parseLine(subContext, null, aLine, source); } private void loadCampaigns() throws PersistenceLayerException { // Unload the existing campaigns and load our selected campaign Globals.emptyLists(); PersistenceManager pManager = PersistenceManager.getInstance(); List<URI> uris = new ArrayList<>(); for (CampaignFacade campaignFacade : selectedCampaigns) { uris.add(((Campaign) campaignFacade).getSourceURI()); } pManager.setChosenCampaignSourcefiles(uris); sourcesSet.clear(); licenseFiles.clear(); if (selectedCampaigns.isEmpty()) { throw new PersistenceLayerException( "You must select at least one campaign to load."); } // 21 Nov 2002: Put load inside a try/finally block to make sure // that file lines were cleared even if an exception occurred. // -- sage_sam try { LoadContext context = Globals.getContext(); loadCampaigns(selectedGame, selectedCampaigns, context); // Load custom items loadCustomItems(context); finishLoad(selectedCampaigns, context); // Check for valid race types // checkRaceTypes(); // Verify weapons are melee or ranged verifyWeaponsMeleeOrRanged(context); // Auto-gen additional equipment if (PCGenSettings.OPTIONS_CONTEXT.initBoolean( PCGenSettings.OPTION_AUTOCREATE_MW_MAGIC_EQUIP, false)) { EquipmentList.autoGenerateEquipment(); } for (Campaign campaign : selectedCampaigns) { sourcesSet.add(SourceFormat.getFormattedString(campaign, SourceFormat.MEDIUM, true)); } context.setLoaded(selectedCampaigns); /* * This needs to happen after auto equipment generation and after * context.setLoaded, not in finishLoad */ context.loadCampaignFacets(); dataset = new DataSet( context, selectedGame, new DefaultListFacade<>(selectedCampaigns)); // // Show the licenses // showLicensesIfNeeded(); // showSponsorsIfNeeded(); } catch (Throwable thr) { Logging.errorPrint("Exception loading files.", thr); uiDelegate.showErrorMessage(Constants.APPLICATION_NAME, "Failed to load campaigns, see log for details."); } } private void loadCampaigns(GameMode gamemode, final List<Campaign> aSelectedCampaignsList, LoadContext context) throws PersistenceLayerException { Logging.log(Logging.INFO, "Loading game " + gamemode + " and sources " + aSelectedCampaignsList + "."); // // The first thing we need to do is load the // // correct statsandchecks.lst file for this gameMode // GameMode gamemode = SettingsHandler.getGame(); // if (gamemode == null) // { // // Autoload campaigns is set but there // // is no current gameMode, so just return // return; // } File gameModeDir = new File(ConfigurationSettings.getSystemsDir(), "gameModes"); File specificGameModeDir = new File(gameModeDir, gamemode.getFolderName()); // Sort the campaigns sortCampaignsByRank(aSelectedCampaignsList); // Read the campaigns Collection<Campaign> loaded = readPccFiles(aSelectedCampaignsList); // Add custom campaign files at the start of the lists addCustomFilesToStartOfList(); // Notify our observers of how many files we intend // to load in total so that they can set up any // progress meters that they want to. setMaximum(countTotalFilesToLoad()); // Load using the new LstFileLoaders List<CampaignSourceEntry> dataDefFileList = fileLists.getListFor(ListKey.FILE_DATACTRL); dataDefFileList = addDefaultDataControlIfNeeded(dataDefFileList); dataControlLoader.loadLstFiles(context, dataDefFileList); processFactDefinitions(context); tableLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_DATATABLE)); dynamicLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_DYNAMIC)); //Load Variables (foundation for other items) variableLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_VARIABLE)); defineBuiltinVariables(context); List<CampaignSourceEntry> globalModFileList = fileLists.getListFor(ListKey.FILE_GLOBALMOD); if (globalModFileList.isEmpty()) { File defaultGameModeDir = new File(gameModeDir, "default"); File df = new File(defaultGameModeDir, "compatibilityGlobalModifier.lst"); Campaign c = new Campaign(); c.setName("Default Global Modifier File"); CampaignSourceEntry cse = new CampaignSourceEntry(c, df.toURI()); globalModFileList.add(cse); } globalModifierLoader.loadLstFiles(context, globalModFileList); // load ability categories first as they used to only be at the game mode abilityCategoryLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_ABILITY_CATEGORY)); for (Campaign c : loaded) { c.applyTo(context.getReferenceContext()); } sizeLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_SIZE)); //Now load PCC stat, check, alignment statLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_STAT)); savesLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_SAVE)); alignmentLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_ALIGNMENT)); // load weapon profs first wProfLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_WEAPON_PROF)); WeaponProf wp = context.getReferenceContext().silentlyGetConstructedCDOMObject( WeaponProf.class, "Unarmed Strike"); if (wp == null) { wp = new WeaponProf(); wp.setName(LanguageBundle.getString("Equipment.UnarmedStrike")); wp.put(StringKey.KEY_NAME, "Unarmed Strike"); wp.addToListFor(ListKey.TYPE, Type.SIMPLE); context.getReferenceContext().importObject(wp); } aProfLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_ARMOR_PROF)); sProfLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_SHIELD_PROF)); // load skills before classes to handle class skills skillLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_SKILL)); // load before races to handle auto known languages languageLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_LANGUAGE)); // load before race or class to handle feats featLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_FEAT)); // load before race or class to handle abilities abilityLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_ABILITY)); raceLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_RACE)); //Domain must load before CLASS - thpr 10/29/06 domainLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_DOMAIN)); spellLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_SPELL)); deityLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_DEITY)); classLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_CLASS)); templateLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_TEMPLATE)); // loaded before equipment (required) eqModLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_EQUIP_MOD)); equipmentLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_EQUIP)); companionModLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_COMPANION_MOD)); kitLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_KIT)); // Load the bio settings files bioLoader.setGameMode(gamemode.getName()); bioLoader.loadLstFiles(context, fileLists.getListFor(ListKey.FILE_BIO_SET)); // Check for the default deities checkRequiredDeities(specificGameModeDir, context); // Add default EQ mods addDefaultEquipmentMods(context); classLoader.loadSubLines(context); /* * This is technically bad behavior, but we at least want to provide the * hint here since we are using WeakReferences as a container for * references to ensure those that are not used are not resolved. */ System.gc(); } private void defineBuiltinVariables(LoadContext context) { if (!ControlUtilities.hasControlToken(context, CControl.FACE)) { VariableContext varContext = context.getVariableContext(); FormatManager<?> opManager = context.getReferenceContext().getFormatManager("ORDEREDPAIR"); defineVariable(varContext, opManager, "Face"); } } private void defineVariable(VariableContext varContext, FormatManager<?> formatManager, String varName) { LegalScope varScope = varContext.getScope("Global"); varContext.assertLegalVariableID(varScope, formatManager, varName); } /** * Add default data control files to the supplied list, but only if it is empty. * * @param dataDefFileList The list of data control files. */ public static List<CampaignSourceEntry> addDefaultDataControlIfNeeded( List<CampaignSourceEntry> dataDefFileList) { if (dataDefFileList == null) { dataDefFileList = new ArrayList<>(); } if (dataDefFileList.isEmpty()) { File gameModeDir = new File(ConfigurationSettings.getSystemsDir(), "gameModes"); File defaultGameModeDir = new File(gameModeDir, "default"); File df = new File(defaultGameModeDir, "compatibilityDataControl.lst"); Campaign c = new Campaign(); c.setName("Default Data Control File"); CampaignSourceEntry cse = new CampaignSourceEntry(c, df.toURI()); dataDefFileList.add(cse); } return dataDefFileList; } public static void processFactDefinitions(LoadContext context) { Collection<? extends ContentDefinition> defs = context.getReferenceContext().getConstructedCDOMObjects( FactDefinition.class); for (ContentDefinition<?, ?> fd : defs) { fd.activate(context); } defs = context.getReferenceContext().getConstructedCDOMObjects( FactSetDefinition.class); for (ContentDefinition<?, ?> fd : defs) { fd.activate(context); } } private void finishLoad(final List<Campaign> aSelectedCampaignsList, LoadContext context) { createLangBonusObject(context); AbstractReferenceContext refContext = context.getReferenceContext(); refContext.buildDeferredObjects(); refContext.buildDerivedObjects(); referenceAllCategories(context); context.resolveDeferredTokens(); LoadValidator validator = new LoadValidator(aSelectedCampaignsList); refContext.validate(validator); refContext.resolveReferences(validator); context.resolvePostValidationTokens(); context.resolvePostDeferredTokens(); ReferenceContextUtilities.validateAssociations(refContext, validator); for (Equipment eq : refContext .getConstructedCDOMObjects(Equipment.class)) { eq.setToCustomSize(null); EqModAttachment.finishEquipment(eq); } } private void referenceAllCategories(LoadContext context) { GameMode gamemode = SettingsHandler.getGame(); for (AbilityCategory cat : gamemode.getAllAbilityCategories()) { /* * Yes, these are thrown away... just need to make sure the * manufacturer was built. */ context.getReferenceContext().getManufacturer(Ability.class, cat); } } public static void createLangBonusObject(LoadContext context) { Ability a = context.getReferenceContext().constructCDOMObject( Ability.class, "*LANGBONUS"); context.getReferenceContext().reassociateCategory( AbilityCategory.LANGBONUS, a); a.put(ObjectKey.INTERNAL, true); context.unconditionallyProcess(a, "CHOOSE", "LANG|!PC,LANGBONUS"); context.unconditionallyProcess(a, "VISIBLE", "NO"); context.unconditionallyProcess(a, "AUTO", "LANG|%LIST"); context.unconditionallyProcess(a, "MULT", "YES"); } private void loadCustomItems(LoadContext context) { if (!PCGenSettings.OPTIONS_CONTEXT .getBoolean(PCGenSettings.OPTION_SAVE_CUSTOM_EQUIPMENT)) { return; } final BufferedReader br = CustomData.getCustomEquipmentReader(); // Why is this here? This implies it is somehow // order-independent and should precede the opening of // the file. This is almost assuredly a bug of some // kind waiting to happen. Aha! Just look at what is // in the "finally" clause below. --bko XXX EquipmentList.setAutoGeneration(true); /* * if (br == null) { return; } */ try { while (br != null) { String aLine = br.readLine(); if (aLine == null) { break; } if (aLine.startsWith("BASEITEM:")) { final int idx = aLine.indexOf('\t', 9); if (idx < 10) { continue; } final String baseItemKey = aLine.substring(9, idx); aLine = aLine.substring(idx + 1); Equipment aEq = context.getReferenceContext() .silentlyGetConstructedCDOMObject( Equipment.class, baseItemKey); if (aEq != null) { aEq = aEq.clone(); aEq.setBase(); aEq.load(aLine, "\t", ":", null); if (!aEq.isType(Constants.TYPE_CUSTOM)) { aEq.addType(Type.CUSTOM); } context.getReferenceContext().importObject(aEq); } } } CustomData.setCustomItemsLoaded(true); } catch (IOException e) { logError("Error when loading custom items", e); } finally { EquipmentList.setAutoGeneration(false); try { if (br != null) { br.close(); } } catch (IOException ex) { logError( "Error when closing infile after loading custom items", ex); } } } /** * This method checks to make sure that the deities required for the current * mode have been loaded into the Globals as Deities. Prior to calling this * method, deities are stored as simple String objects. * * @throws PersistenceLayerException * if something bizarre occurs, such as this method being * invoked more than once, a change to DeityLoader, or an * invalid LST file containing the default deities. */ private void checkRequiredDeities(File dir, LoadContext context) throws PersistenceLayerException { context.setSourceURI(new File(dir, "miscinfo.lst").toURI()); // // Add in the default deities (unless they're already there) // final List<String> gDeities = Globals.getGlobalDeityList(); if ((gDeities != null) && (!gDeities.isEmpty())) { for (String aLine : gDeities) { deityLoader.parseLine(context, null, aLine, globalCampaign); } } } /** * This method is called to verify that all weapons loaded from the * equipment files are classified as either Melee or Ranged. This is * required so that to-hit values can be calculated for that weapon. * * @throws PersistenceLayerException * if a weapon is neither melee or ranged, indicating the name * of the weapon that caused the error */ private void verifyWeaponsMeleeOrRanged(LoadContext context) throws PersistenceLayerException { // // Check all the weapons to see if they are either Melee or Ranged, to avoid // problems when we go to export/preview the character // for (Equipment aEq : context.getReferenceContext() .getConstructedCDOMObjects(Equipment.class)) { if (aEq.isWeapon() && !aEq.isMelee() && !aEq.isRanged()) { throw new PersistenceLayerException( "Weapon: " + aEq.getName() + " is neither Melee nor Ranged." + Constants.LINE_SEPARATOR + Constants.APPLICATION_NAME + " cannot calculate \"to hit\" unless one of these is selected." + Constants.LINE_SEPARATOR + "Source: " + aEq.getSourceURI()); } } } /** * This method sorts the provided listof Campaign objects by rank. * * @param aSelectedCampaignsList * List of Campaign objects to sort */ private void sortCampaignsByRank(final List<Campaign> aSelectedCampaignsList) { aSelectedCampaignsList.sort(new Comparator<Campaign>() { @Override public int compare(Campaign c1, Campaign c2) { return c1.getSafe(IntegerKey.CAMPAIGN_RANK) - c2.getSafe(IntegerKey.CAMPAIGN_RANK); } }); } /** * Logs an error that has occurred during data loading. This will not only * log the message to the system error log, but it will also notify all * observers of the error. * * @param message * the error to notify listeners about * @param e */ private void logError(String message, Throwable e) { Logging.errorPrint(message, e); //setChanged(); } /** * This method reads the PCC (Campaign) files and, if options are allowed to * be set in the sources, sets the SettingsHandler settings to reflect the * changes from the campaign files. * * @param aSelectedCampaignsList * List of Campaigns to load */ private Collection<Campaign> readPccFiles( Iterable<Campaign> aSelectedCampaignsList) { Collection<Campaign> loadedSet = new HashSet<>(); // Create aggregate collections of source files to load // along with any options required by the campaigns... for (Campaign campaign : aSelectedCampaignsList) { if (Logging.isDebugMode()) { Logging.debugPrint("Loading campaign " + campaign); } loadedCampaigns.add(campaign); List<String> copyright = campaign.getListFor(ListKey.SECTION_15); if (copyright != null) { sec15.append("<br><b>Source Material:</b>"); sec15.append(SourceFormat.getFormattedString(campaign, SourceFormat.LONG, true)); sec15.append("<br>"); sec15.append("<b>Section 15 Entry in Source Material:</b><br>"); for (String license : copyright) { sec15.append(license).append("<br>"); } } // Update whether licenses need shown showOGL |= campaign.getSafe(ObjectKey.IS_OGL); showD20 |= campaign.getSafe(ObjectKey.IS_D20); showLicensed |= campaign.getSafe(ObjectKey.IS_LICENSED); if (campaign.getSafe(ObjectKey.IS_LICENSED)) { List<String> licenseList = campaign.getSafeListFor(ListKey.LICENSE); if (licenseList != null && !licenseList.isEmpty()) { licensesToDisplayString.append(licenseList); } List<CampaignSourceEntry> licenseURIs = campaign.getSafeListFor(ListKey.LICENSE_FILE); if (licenseURIs != null) { licenseFiles.addAll(licenseURIs); } } // check if maturity warning needs to be shown showMature |= campaign.getSafe(ObjectKey.IS_MATURE); if (campaign.getSafe(ObjectKey.IS_MATURE)) { matureCampaigns.append(SourceFormat.LONG.getField(campaign) + " (" + campaign.getSafe(StringKey.PUB_NAME_LONG) + ")<br>"); } // Load the LST files to be loaded for the campaign addQualifiedSources(campaign, ListKey.FILE_LST_EXCLUDE); for (ListKey<CampaignSourceEntry> lk : CampaignLoader.OBJECT_FILE_LISTKEY) { addQualifiedSources(campaign, lk); } loadedSet.add(campaign); if (PCGenSettings.OPTIONS_CONTEXT.initBoolean( PCGenSettings.OPTION_ALLOWED_IN_SOURCES, true)) { setCampaignOptions(campaign); } // Add all sub-files to the main campaign, regardless of exclusions for (CampaignSourceEntry fName : campaign .getSafeListFor(ListKey.FILE_PCC)) { URI uri = fName.getURI(); if (PCGFile.isPCGenCampaignFile(uri)) { loadedSet.add(Globals.getCampaignByURI(uri, false)); } else { Logging.errorPrint("The referenced source " + uri + " is not valid."); } } } // ...but make sure to remove those files specified by LSTEXCLUDE; // LSTEXCLUDE should be treated as a global exclusion. // sage_sam 29 Dec 2003, Bug #834834 stripLstExcludes(); return loadedSet; } /** * Add only those source files that either have no requirements, or that the * requirements are satisfied. * * @param targetList * The list being populated. * @param sources * The list of potential sources to be added. */ private void addQualifiedSources(Campaign c, ListKey<CampaignSourceEntry> lk) { for (CampaignSourceEntry cse : c.getSafeListFor(lk)) { List<Prerequisite> prerequisites = cse.getPrerequisites(); if (prerequisites.isEmpty() || PrereqHandler.passesAll(prerequisites, null, cse)) { fileLists.addToListFor(lk, cse); } } } /** * Sets the options specified in the campaign aCamp. * * @param aCamp */ private static void setCampaignOptions(Campaign aCamp) { Set<String> keys = aCamp.getKeysFor(MapKey.PROPERTY); if (keys != null) { for (final String key : keys) { String value = aCamp.get(MapKey.PROPERTY, key); if (key.contains(".")) { PCGenSettings.getInstance().setProperty(key, value); } else { PCGenSettings.OPTIONS_CONTEXT.setProperty(key, value); } // Note: This is just until we transition all settings from the legacy settings SettingsHandler.setPCGenOption(key, value); } // Make sure any game mode settings are applied. SystemCollections.getUnmodifiableGameModeList().forEach(GameMode::applyPreferences); } } /** * This method makes sure that the files specified by an LSTEXCLUDE tag are * stripped out of the source files to be loaded on a global basis. */ private void stripLstExcludes() { for (ListKey<?> lk : fileLists.getKeySet()) { if (!ListKey.FILE_LST_EXCLUDE.equals(lk)) { stripLstExcludes(lk); } } } private void stripLstExcludes(ListKey<?> lk) { List<CampaignSourceEntry> excludes = fileLists.getListFor(ListKey.FILE_LST_EXCLUDE); if (excludes != null) { for (CampaignSourceEntry exc : excludes) { URI uri = exc.getURI(); for (CampaignSourceEntry cse : fileLists.getListFor(lk)) { if (cse.getURI().equals(uri)) { fileLists.removeFromListFor(lk, cse); } } } } } public boolean hasD20Campaign() { return showD20; } public boolean hasMatureCampaign() { return showMature; } public boolean hasOGLCampaign() { return showOGL; } public boolean hasLicensedCampaign() { return showLicensed; } @Override public void update(Observable o, Object arg) { if (arg instanceof URI) { progress++; URI uri = (URI) arg; if ("file".equalsIgnoreCase(uri.getScheme())) { setProgress(new File(uri).getName(), progress); } else { setProgress(String.valueOf(uri), progress); } //setProgress(progress); } else if (arg instanceof Exception) { sendErrorMessage((Exception) arg); } } private class LoadHandler extends Handler { private LoadHandler() { setLevel(Logging.LST_WARNING); } @Override public void close() throws SecurityException { // Nothing to do } @Override public void flush() { // Nothing to do } @Override public void publish(final LogRecord arg0) { sendErrorMessage(arg0); } } }