You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
5.1 KiB
D
168 lines
5.1 KiB
D
/*
|
|
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
Copyright (C) 2008 Nicolay Korslund
|
|
Email: < korslund@gmail.com >
|
|
WWW: http://openmw.snaptoad.com/
|
|
|
|
This file (esmmain.d) is part of the OpenMW package.
|
|
|
|
OpenMW is distributed as free software: you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License
|
|
version 3, as published by the Free Software Foundation.
|
|
|
|
This program 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
|
|
version 3 along with this program. If not, see
|
|
http://www.gnu.org/licenses/ .
|
|
|
|
*/
|
|
|
|
module esm.esmmain;
|
|
|
|
public import esm.records;
|
|
|
|
import ogre.ogre;
|
|
|
|
/* This file is the main module for loading from ESM, ESP and ESS
|
|
files. It stores all the data in the appropriate data structures
|
|
for later referal. TODO: Put this in a class or whatever? Nah, we
|
|
definately only need one structure like this at any one
|
|
time. However, we have to deal with unloading and reloading it,
|
|
even though that should be the exceptional case (change of plugins,
|
|
etc), not the rule (loading a savegame should not alter the base
|
|
data set, I think, but it's to early do decide.)*/
|
|
|
|
// Load a set of esm and esp files. For now, we just traverse in the
|
|
// order given. Later, we should sort these into 'masters' and
|
|
// 'plugins', because esms are always supposed to be loaded
|
|
// first. TODO: I'm not sure if I should load all these in one
|
|
// function. Do we need to be able to respond to errors in each file?
|
|
// Nah, if anything fails, give a general error message, remove the
|
|
// file from the list and try again. We have to be able to get a list
|
|
// of which files depend upon which, though... this can be done before
|
|
// this function is called.
|
|
void loadTESFiles(char[][] files)
|
|
{
|
|
// Set up all the lists to hold our data
|
|
initializeLists();
|
|
|
|
foreach(char[] filename; files)
|
|
{
|
|
esFile.open(filename, esmRegion);
|
|
while(esFile.hasMoreRecs())
|
|
{
|
|
uint flags;
|
|
|
|
// Read record header
|
|
char[] recName = esFile.getRecName();
|
|
esFile.getRecHeader(flags);
|
|
|
|
if(flags & RecordFlags.Unknown)
|
|
esFile.fail(format("UNKNOWN record flags: %xh", flags));
|
|
|
|
loadRecord(recName);
|
|
}
|
|
|
|
// We have to loop through the lists and check for broken
|
|
// references at this point, and if all forward references were
|
|
// loaded. There might be other end-of-file things to do also.
|
|
endFiles();
|
|
}
|
|
|
|
esFile.close();
|
|
|
|
// Put all inventory items into one list
|
|
items.addList(appas, ItemType.Apparatus);
|
|
items.addList(lockpicks, ItemType.Pick);
|
|
items.addList(probes, ItemType.Probe);
|
|
items.addList(repairs, ItemType.Repair);
|
|
items.addList(lights, ItemType.Light);
|
|
items.addList(ingreds, ItemType.Ingredient);
|
|
items.addList(potions, ItemType.Potion);
|
|
items.addList(armors, ItemType.Armor);
|
|
items.addList(weapons, ItemType.Weapon);
|
|
items.addList(books, ItemType.Book);
|
|
items.addList(clothes, ItemType.Clothing);
|
|
items.addList(miscItems, ItemType.Misc);
|
|
items.addList(itemLists, ItemType.ItemList); // Leveled item lists
|
|
|
|
// Same with all actors
|
|
actors.addList(creatures, ItemType.Creature);
|
|
actors.addList(creatureLists, ItemType.CreatureList);
|
|
actors.addList(npcs, ItemType.NPC);
|
|
|
|
// Finally, add everything that might be looked up in a cell into
|
|
// one list
|
|
cellRefs.addList(items);
|
|
cellRefs.addList(actors);
|
|
cellRefs.addList(doors, ItemType.Door);
|
|
cellRefs.addList(activators, ItemType.Activator);
|
|
cellRefs.addList(statics, ItemType.Static);
|
|
cellRefs.addList(containers, ItemType.Container);
|
|
|
|
// Check that all references are resolved
|
|
items.endMerge();
|
|
actors.endMerge();
|
|
cellRefs.endMerge();
|
|
|
|
// Put all NPC dialogues into the hyperlink list
|
|
foreach(char[] id, ref Dialogue dl; dialogues.names)
|
|
hyperlinks.add(id, &dl);
|
|
|
|
// Finally, sort the hyperlink lists
|
|
hyperlinks.sort();
|
|
}
|
|
|
|
// Contains the small bits of information that we currently extract
|
|
// from savegames.
|
|
struct PlayerSaveInfo
|
|
{
|
|
char[] cellName;
|
|
char[] playerName;
|
|
Placement pos;
|
|
}
|
|
|
|
// Load a TES savegame file (.ess). Currently VERY limited, reads the
|
|
// player's cell name and position
|
|
PlayerSaveInfo importSavegame(char[] file)
|
|
{
|
|
PlayerSaveInfo pi;
|
|
|
|
esFile.open(file, esmRegion);
|
|
scope(exit) esFile.close();
|
|
|
|
if(esFile.getFileType != FileType.Ess)
|
|
throw new TES3FileException(file ~ " is not a savegame");
|
|
|
|
with(esFile.saveData)
|
|
{
|
|
pi.cellName = stripz(cell);
|
|
pi.playerName = stripz(player);
|
|
}
|
|
|
|
with(esFile)
|
|
{
|
|
while(hasMoreRecs())
|
|
{
|
|
if(isNextHRec("REFR"))
|
|
{
|
|
while(hasMoreSubs())
|
|
{
|
|
getSubName();
|
|
if(retSubName() == "DATA")
|
|
readHExact(&pi.pos, pi.pos.sizeof);
|
|
else
|
|
skipHSub();
|
|
}
|
|
}
|
|
else
|
|
skipHRecord();
|
|
}
|
|
}
|
|
return pi;
|
|
}
|