User:Wrye/Bash for Fallout
Contents
Introduction[edit]
As I've mentioned elsewhere, I won't be porting Wrye Bash to Fallout. (In the interest of maintaining some balance of life, I'm not buying Fallout at all!) However, Bash code is GPL'ed and I'm hoping that someone will take on this project. It's pretty nontrivial however.
PS: I said it's non-trivial right? Keep in mind that bosh+basher == over 20,000 lines of code. This is advanced professional level code.
Code Overview[edit]
Bash is split into several files:
- bolt: Generic non-GUI stuff. Should not require any changes.
- balt: Generic GUI stuff (based on wxPython). Should not require any changes.
- bush: This is data storage. Sort of equivalent to a constants file. Almost all of this is Oblivion specific and can be chopped.
- bosh: The core non-GUI engine. Mixture of Oblivion specific and more generic. Much can/should be chopped.
- basher: Almost all GUI code is here. Mixture of generic and specific stuff. Majority can/should be chopped.
- bish: This is a command line interface that sits on top of bosh. Basically allows you to invoke/test bosh stuff and do very specialized functions w/o having to mess with a GUI. Very few people other than myself use it. Most functions are Oblivion specific and can be chopped. Some stuff is useful for prototypes for potential Fallout work. (I particularly used bish while working on low leve stuff in bosh.)
- bash.py: Outdated. Discard.
- Wrye Bash Launcher: Replaces bash.py. .pyw extension makes it double clickable. Basically a small front end on the graphics stuff that starts everything up. Can be ported with very little change.
Naming Scheme[edit]
Keep bolt and balt names the same. For rest of files (assuming same structure), the main thing that I would suggest is to keep the same "u,o,i,a" sequence. One theme might be "Falt" (Hence fult, folt, filt, falter, and Falt Launcher). (Always nice to have a good short name w/o too many googlegangers.)
Program Overview[edit]
For the GUI, there's are the fundamental abilities: Display of tabs with information on Mods, Saves and Screen shots. At this low level, there are some basic file system commands (move, delete, rename, etc.) mostly present under the File. menu. Mostly this is file system stuff with very little file comprehension required.
At fundamental logical level, there's the preceding, plus classes to support it (e.g. ModInfo, ModInfos, etc.), and there's basic header reading/writing. The latter requires that the mods/savegames be understood enough to parse the headers.
Then, layered on top of that (GUI wise) are all the specific commands. These basically show up as right-click and select command from context menu. These commands tend to be GUI-lite -- i.e. the GUI is just a simple definition of the menu item and handling, while the majority of the code is in bosh.
Then, there's the bashed patch. That's a pretty heavy function, mostly on the bosh side, but bash side has some chunky stuff for generating the GUI and beginning the build process.
Bosh Structure[edit]
Look for {{for comments.}}
# GPL License and Copyright Notice ============================================ {{Following is pretty basic stuff.}} # Localization ---------------------------------------------------------------- def formatInteger(value): def formatDate(value): # Imports --------------------------------------------------------------------- # Singletons, Constants ------------------------------------------------------- # Errors ---------------------------------------------------------------------- class FileError(BoltError): class FileEditError(BoltError): # Util Classes ---------------------------------------------------------------- class CountDict(dict): class Path(str): class PickleDict(bolt.PickleDict): # Util Constants -------------------------------------------------------------- # Util Functions -------------------------------------------------------------- #>>Some stuff is generic, other stuff is pretty specialized. def netString(x): # Groups def splitModGroup(offGroup): def joinModGroup(group,offset): # Reference (FormId) def strFormid(formid): def genFormId(modIndex,objectIndex): def getModIndex(formid): def getObjectIndex(formid): def getFormIndices(formid): # Mod I/O -------------------------------------------------------------------- {{Fairly generic, low level tes3 file handling stuff. May have to be adapted somewhat to account for low level differences in file structure. (e.g. way compressed subrecords are handled.) It's possible that this will work w/o any changes.}} class ModError(FileError): class ModReadError(ModError): class ModSizeError(ModError): class ModUnknownSubrecord(ModError): class ModReader: class ModWriter: # Mod Record Elements --------------------------------------------------------- {{Following defines constructive elements for building tes3 file to logical class elements. These are used for specific records below and are what enables me to define classes (e.g. for an ALCH (potion) record without defining a single new method.) }} # Constants class MelObject(object): class MelBase: class MelFormid(MelBase): class MelFormids(MelBase): class MelFormidList(MelFormids): class MelGroup(MelBase): class MelGroups(MelGroup): class MelNull(MelBase): class MelXpci(MelNull): class MelString(MelBase): class MelStrings(MelString): class MelStruct(MelBase): class MelStructs(MelStruct): class MelTuple(MelBase): # Common/Special Elements {{Some of this is pretty generic and may be discardable.}} class MelConditions(MelStructs): class MelEffects(MelGroups): class MelFull0(MelString): class MelModel(MelGroup): class MelOptStruct(MelStruct): class MelOwnership(MelGroup): class MelScrxen(MelFormids): class MelSet: # Flags class MelBipedFlags(Flags): # Mod Records 0 --------------------------------------------------------------- {{These are base records for records and subrecords. They define generic semi-low level versions of both, AND provide some basic functions. Especially MreRecord and MelRecord. MelRecord extends MreRecord in a way to support use of all the Mel building blocks. MreRecord also stores some generic stuff. (Type to constructor class dictinaries, etc.)}} class MreSubrecord: class MreRecord(object): class MelRecord(MreRecord): {{Specific mixin/parent types.}} class MreActor(MelRecord): class MreLeveledList(MelRecord): class MreHasEffects: # Mod Records 1 --------------------------------------------------------------- {{Specific object record info. Using MelXxx building blocks these can be built and tested fairly easily, once you understand what the file structue is. Actually, they're very helpful for helping to analyze file structure. You can append a .debug() to the end of any melset definition and to the end of any specific MelXxxx building block (e.g MelString(...).debug(),) this can then be used with some bish code (see bis temp() function) to parse out the file structure to see what its decoding to. }} class MreAchr(MelRecord): # Placed NPC class MreAcre(MelRecord): # Placed Creature class MreActi(MelRecord): class MreAlch(MelRecord,MreHasEffects): class MreAmmo(MelRecord): class MreAnio(MelRecord): class MreAppa(MelRecord): class MreArmo(MelRecord): class MreBook(MelRecord): class MreBsgn(MelRecord): class MreCell(MelRecord): class MreClas(MelRecord): class MreClot(MelRecord): class MreCont(MelRecord): class MreCrea(MreActor): class MreDial(MelRecord): class MreDoor(MelRecord): class MreEfsh(MelRecord): class MreEnch(MelRecord,MreHasEffects): class MreEyes(MelRecord): class MreFact(MelRecord): class MreFlor(MelRecord): class MreFurn(MelRecord): class MreGlob(MelRecord): class MreGmst(MelRecord): class MreGras(MelRecord): class MreHair(MelRecord): class MreInfo(MelRecord): class MreIngr(MelRecord,MreHasEffects): class MreKeym(MelRecord): class MreLigh(MelRecord): class MreLscr(MelRecord): class MreLvlc(MreLeveledList): class MreLvli(MreLeveledList): class MreLvsp(MreLeveledList): class MreMgef(MelRecord): class MreMisc(MelRecord): class MreNpc(MreActor): class MrePack(MelRecord): class MreQust(MelRecord): class MreRace(MelRecord): class MreRefr(MelRecord): # Placed object class MreScpt(MelRecord): class MreSgst(MelRecord,MreHasEffects): class MreSkil(MelRecord): class MreSlgm(MelRecord): class MreSoun(MelRecord): class MreSpel(MelRecord,MreHasEffects): class MreStat(MelRecord): {{Tes4 is very basic structure. It's the header for the mod file. Contains masters and other basic info. This is one of the first things to worry about.}} class MreTes4(MelRecord): class MreTree(MelRecord): class MreWatr(MelRecord): class MreWeap(MelRecord): class MreWrld(MelRecord): class MreWthr(MelRecord): # MreRecord.type_class # Mod Blocks, File ------------------------------------------------------------ #>>Master map is for converting between different sets of master strings. class MasterMapError(BoltError): class MasterMap: class MasterSet(set): #>>Load factory is pretty core chunk that says which chunks of a file being read should be read and how thoroughly. Typically, define LoadFactory and then feed it to a ModFile. class LoadFactory: {{Following is semi-low level stuff that handles groups and subgroups in tes3 file. Up through MobObjects isn't too hard. Cell stuff gets complicated though. Most of this will probably translate pretty well to Fallout.}} class MobBase(object): class MobObjects(MobBase): class MobDials(MobObjects): class MobCell(MobBase): class MobCells(MobBase): class MobICells(MobCells): class MobWorld(MobCells): class MobWorlds(MobBase): {{ModFile is core for mod processing. With one or two exceptions, whenever I manipulate a mod file, I first create a modfile, then load it, then manipulate it, then safe-save it.}} class ModFile: # Save I/O -------------------------------------------------------------------- {{Now comes Savefile stuff. In general, much less is known about savefiles. However, there are few important functions that depend on this.}} class SaveFileError(FileError): # Save Change Records --------------------------------------------------------- {{The one substantially decoded save record. Used for some info about the player. Useful for importing high level info about player. Low priority.}} class SreNPC(object): # Save File ------------------------------------------------------------------- class PluggyFile: {{Another basic thing to do. Responsible for basic save information. Player location, screenshot, etc. This is the abbreviated version of the header. Used for display of save files in the save tab.}} class SaveHeader: {{Here's the core file for serious manipulation of savegames. Pretty much just limited to specialized functions for changing players face, removing bloat, etc.}} class SaveFile: {{For handling cosave files. Only needed if FOSE starts creating them. Basically allows file copy, duplicate, renem operations on the base save to be paired with same operations on cosaves.}} class CoSaves: # File System ----------------------------------------------------------------- {{Now, we're looking at collections of data (all mod files, all save files, etc.) and we're getting closer to the UI.}} class BsaFile: class OblivionIni: class PluginsFullError(BoltError): {{Plugins.txt file (active mods)}} class Plugins: {{basically, you have modInfos (collection of ModInfo's) and saveInfos (collection of SaveInfo's). That's the core stuff here.}} class MasterInfo: class FileInfo: class ModInfo(FileInfo): class SaveInfo(FileInfo): class FileInfos(DataDict): class ResourceReplacer: class ModInfos(FileInfos): class SaveInfos(FileInfos): class ReplacersData(DataDict): # Mod Config Help ------------------------------------------------------------- {{New config helper stuff. Not important now. Chop it.}} class ModRuleSet: class ConfigHelpers: # TankDatas ------------------------------------------------------------------- {{Each of these is basically foundation for a tab in Bash. (BuildData and ModBaseData were never fully developed and so are not seen by users. Maybe later.) Messages and PeopleData are sort of outside of Oblivion info. really could be in a separate app. Anyway, they're low priority and can be chpped.) Probably main thing to keep is ScreensData.}} class PickleTankData: class BuildData(PickleTankData,bolt.TankData,DataDict): class Messages(DataDict): class ModBaseData(PickleTankData,bolt.TankData,DataDict): class PeopleData(PickleTankData,bolt.TankData,DataDict): class ScreensData(DataDict): # Utilities ------------------------------------------------------------------- {{Now we get into Oblivion specific operations. (Although some stuff may port very easily, e.g. EditorIds, FullNames, etc.) Basically this stuff defines the core engines for various Bash functions. Most of these things represent collections of specific types of information. E.g. ItemStats represents stats for weapons, armor, etc. Has functions for import/export from/to mod/csv files.}} class ActorFactions: class ActorLevels: class EditorIds: class FactionRelations: class FormidReplacer: class FullNames: class ItemStats: class ModDetails: class ModGroups: class PCFaces: class CleanMod: class UndeleteRefs: class SaveSpells: # Patchers 1 ------------------------------------------------------------------ {{This is core for Bashed patch.}} class PatchFile(ModFile): class Patcher: class ListPatcher(Patcher): class MultiTweakItem: class MultiTweaker(Patcher): # Patchers: 10 ---------------------------------------------------------------- {{Some specific low level patchers. Aliases probably ought to be chopped. PatchMerger is important and should port easily.}} class AliasesPatcher(Patcher): class PatchMerger(ListPatcher): # Patchers: 20 ---------------------------------------------------------------- {{Mostly these importers are about porting specific parts of records. E.g. names, graphics, etc. Most of this should port fairly well. (Once records are understood.)}} class ImportPatcher(ListPatcher): class CellImporter(ImportPatcher): class GraphicsPatcher(ImportPatcher): class ImportFactions(ImportPatcher): class ImportRelations(ImportPatcher): class ImportInventory(ImportPatcher): class NamesPatcher(ImportPatcher): class NpcFacePatcher(ImportPatcher): class SoundPatcher(ImportPatcher): class StatsPatcher(ImportPatcher): # Patchers: 30 ---------------------------------------------------------------- {{These are bit more Oblivon specific. However, a lot may port fairly easily.}} class AssortedTweak_ArmorShows(MultiTweakItem): class AssortedTweak_BowReach(MultiTweakItem): class AssortedTweak_ConsistentRings(MultiTweakItem): class AssortedTweak_DarnBooks(MultiTweakItem): class AssortedTweak_FogFix(MultiTweakItem): class AssortedTweak_NoLightFlicker(MultiTweakItem): class AssortedTweak_PotionWeight(MultiTweakItem): class AssortedTweak_StaffWeight(MultiTweakItem): class AssortedTweaker(MultiTweaker): class ClothesTweak(MultiTweakItem): class ClothesTweak_MaxWeight(ClothesTweak): class ClothesTweak_Unblock(ClothesTweak): class ClothesTweaker(MultiTweaker): class GmstTweak(MultiTweakItem): class GmstTweaker(MultiTweaker): class NamesTweak_BodyTags(MultiTweakItem): class NamesTweak_Body(MultiTweakItem): class NamesTweak_Potions(MultiTweakItem): class NamesTweak_Scrolls(MultiTweakItem): class NamesTweak_Spells(MultiTweakItem): class NamesTweak_Weapons(MultiTweakItem): class NamesTweaker(MultiTweaker): # Patchers: 40 ---------------------------------------------------------------- {{Again, more specific patch components. Some may port easily, others won't port or are irrelevant. E.g. ListsMerger is important and relatively easy to do. OTOH, AlchemicalCatalogs, MFactMarker are Oblivon specific and can be chopped.}} class SpecialPatcher: class AlchemicalCatalogs(SpecialPatcher,Patcher): class ListsMerger(SpecialPatcher,ListPatcher): class MFactMarker(SpecialPatcher,ListPatcher): class PowerExhaustion(SpecialPatcher,Patcher): class RacePatcher(SpecialPatcher,ListPatcher): class SEWorldEnforcer(SpecialPatcher,Patcher): # Initialization -------------------------------------------------------------- {{And at the very end of it all is some low level initialization stuff.}} def initDirs(personal='',localAppData=''): def initSettings(readOnly=False): # Main ------------------------------------------------------------------------
Basher Structure[edit]
In many ways, basher is much simpler than bosh. It mostly does not know guts of various files, etc. However, it does use such info. Still a lot of this code should be directly portable. Especially stuff that defines basic windows and tabs.
# GPL License and Copyright Notice ============================================ {{Start with some semi-generic stuff.}} # Imports --------------------------------------------------------------------- # Singletons ------------------------------------------------------------------ # Settings -------------------------------------------------------------------- # Exceptions ------------------------------------------------------------------ class BashError(BoltError): pass # Gui Ids --------------------------------------------------------------------- # Constants # Images ---------------------------------------------------------------------- class ColorChecks(balt.ImageList): # Windows --------------------------------------------------------------------- {{This is where almost all of the various windows, panels and panes are defined. Most of this is reusable. Probably want to chop Message list, Screens, Build, ModBase. (Latter two were never fully developed. This section basically goes from more basic components up to the interface class.}} class NotebookPanel(wx.Panel): class SashTankPanel(NotebookPanel): class ListCtrl(wx.ListCtrl, ListCtrlAutoWidthMixin): class List(wx.Panel): class MasterList(List): class ModList(List): class ModDetails(wx.Window): class ModPanel(NotebookPanel): class SaveList(List): class SaveDetails(wx.Window): class SavePanel(NotebookPanel): class ReplacersList(List): class ReplacersPanel(NotebookPanel): class ScreensList(List): class ScreensPanel(NotebookPanel): class MessageList(List): class MessagePanel(NotebookPanel): class PeoplePanel(SashTankPanel): class ModBasePanel(SashTankPanel): class BuildPanel(SashTankPanel): class BashNotebook(wx.Notebook): class BashStatusBar(wx.StatusBar): class BashFrame(wx.Frame): class DocBrowser(wx.Frame): class ModChecker(wx.Frame): class BashApp(wx.App): # Misc Dialogs ---------------------------------------------------------------- class ImportFaceDialog(wx.Dialog): # Patchers 00 ------------------------------------------------------------------ {{Patcher stuff starts here. These are core classes.}} class PatchDialog(wx.Dialog): class Patcher: class AliasesPatcher(Patcher,bosh.AliasesPatcher): class ListPatcher(Patcher): class TweakPatcher(Patcher): # Patchers 10 ------------------------------------------------------------------ {{Bunch of one line classes follow. These just merge GUI stuff with functionality from bosh.}} class PatchMerger(bosh.PatchMerger,ListPatcher): # Patchers 20 ------------------------------------------------------------------ class GraphicsPatcher(bosh.GraphicsPatcher,ListPatcher): pass class CellImporter(bosh.CellImporter,ListPatcher): pass class ImportFactions(bosh.ImportFactions,ListPatcher): pass class ImportRelations(bosh.ImportRelations,ListPatcher): pass class ImportInventory(bosh.ImportInventory,ListPatcher): pass class NamesPatcher(bosh.NamesPatcher,ListPatcher): pass class NpcFacePatcher(bosh.NpcFacePatcher,ListPatcher): pass class RacePatcher(bosh.RacePatcher,ListPatcher): class SoundPatcher(bosh.SoundPatcher,ListPatcher): pass class StatsPatcher(bosh.StatsPatcher,ListPatcher): pass # Patchers 30 ------------------------------------------------------------------ class AssortedTweaker(bosh.AssortedTweaker,TweakPatcher): pass class ClothesTweaker(bosh.ClothesTweaker,TweakPatcher): pass class GmstTweaker(bosh.GmstTweaker,TweakPatcher): pass class NamesTweaker(bosh.NamesTweaker,TweakPatcher): pass # Patchers 40 ------------------------------------------------------------------ class AlchemicalCatalogs(bosh.AlchemicalCatalogs,Patcher): pass class ListsMerger(bosh.ListsMerger,ListPatcher): class MFactMarker(bosh.MFactMarker,ListPatcher): pass class PowerExhaustion(bosh.PowerExhaustion,Patcher): pass class SEWorldEnforcer(bosh.SEWorldEnforcer,Patcher): pass {{Finalize the patcher construction.}} # Init Patchers {{Starting here are all of the menu command classes. I.e. each class encapsulates a single menu command (with a few exceptions). There are also a few extra supporting classes (windows, data collections, etc.) Things are named by the menu they belong to ("File/Files" is generic.) A lot of this stuff can be chopped. Probably main stuff to start with is just the File/Files stuff which should port pretty well.}} # Files Links ----------------------------------------------------------------- class Files_Open(Link): class Files_SortBy(Link): class Files_Unhide(Link): # File Links ------------------------------------------------------------------ class File_Delete(Link): class File_Duplicate(Link): class File_Hide(Link): class File_ListMasters(Link): class File_Redate(Link): class File_Sort(Link): class File_Snapshot(Link): class File_RevertToSnapshot(Link): class File_Backup(Link): class File_RevertToBackup: class File_Open(Link): # Mods Links ------------------------------------------------------------------ class Mods_ReplacersData: class Mod_MergedLists_Data: class Mods_LoadListData(balt.ListEditorData): class Mods_LoadList: class Mods_EsmsFirst(Link): class Mods_SelectedFirst(Link): class Mods_AutoGroup(Link): class Mods_Deprint(Link): class Mods_FullBalo(Link): class Mods_DumpTranslator(Link): class Mods_IniTweaks(Link): class Mods_ListMods(Link): class Mods_LockTimes(Link): class Mods_OblivionIni(Link): class Mods_OblivionVersion(Link): class Mods_UpdateInvalidator(Link): # Mod Links ------------------------------------------------------------------- class Mod_ActorLevels_Export(Link): class Mod_ActorLevels_Import(Link): class Mod_AddMaster(Link): class Mod_BaloGroups_Edit(wx.Dialog): class Mod_BaloGroups: class Mod_CleanMod(Link): class Mod_CreateBlank(Link): class Mod_FactionRelations_Export(Link): class Mod_Factions_Export(Link): class Mod_MarkLevelers(Link): class Mod_MarkMergeable(Link): class Mod_CopyToEsmp(Link): class Mod_Face_Import(Link): class Mod_FlipMasters(Link): class Mod_FlipSelf(Link): class Mod_LabelsData(balt.ListEditorData): class Mod_Labels: class Mod_Groups(Mod_Labels): class Mod_Groups_Export(Link): class Mod_Groups_Import(Link): class Mod_EditorIds_Export(Link): class Mod_EditorIds_Import(Link): class Mod_Filter(Link): class Mod_Formids_Replace(Link): class Mod_FullNames_Export(Link): class Mod_FullNames_Import(Link): class Mod_Patch_Update(Link): class Mod_Ratings(Mod_Labels): class Mod_SetVersion(Link): class Mod_Details(Link): class Mod_RemoveWorldOrphans(Link): class Mod_ShowReadme(Link): class Mod_Stats_Export(Link): class Mod_Stats_Import(Link): class Mod_UndeleteRefs(Link): # Saves Links ------------------------------------------------------------------ class Saves_ProfilesData(balt.ListEditorData): class Saves_Profiles: class Save_LoadMasters(Link): class Save_ImportFace(Link): class Save_DiffMasters(Link): class Save_EditCreatedData(balt.ListEditorData): class Save_EditCreated(Link): class Save_EditPCSpellsData(balt.ListEditorData): class Save_EditPCSpells(Link): class Save_Move: class Save_RepairAbomb(Link): class Save_RepairFactions(Link): class Save_RepairHair(Link): class Save_ReweighPotions(Link): class Save_Stats(Link): class Save_Unbloat(Link): class Save_UpdateNPCLevels(Link): # Screen Links ------------------------------------------------------------------ class Screens_NextScreenShot(Link): class Screen_ConvertToJpg(Link): class Screen_Rename(Link): # Messages Links ------------------------------------------------------------------ class Messages_Archive_Import(Link): class Message_Delete(Link): # People Links ------------------------------------------------------------------ class People_AddNew(Link): class People_Export(Link): class People_Import(Link): class People_Karma(Link): # Masters Links --------------------------------------------------------------- # Master Links ---------------------------------------------------------------- class Master_ChangeTo(Link): class Master_Disable(Link): # App Links ------------------------------------------------------------------- {{These are classes for application buttons that appear at the bottom of the main window.}} class App_Button(Link): class Oblivion_Button(App_Button): class Obse_Button(Link): class AutoQuit_Button(Link): class App_Help(Link): class App_DocBrowser(Link): class App_ModChecker(Link): class App_BashMon(Link): # Initialization -------------------------------------------------------------- {{Initialization, aka final assembly. Init settings, define images, and then assign all of the menuitems into the various menus. If you want to leave something OUT of the interface, this is the easiest place to do it. I.e. useful for effectively chopping stuff out without actually discarding any of the code.}} def InitSettings(): def InitImages(): def InitStatusBar(): def InitMasterLinks(): def InitModLinks(): def InitReplacerLinks(): def InitSaveLinks(): def InitScreenLinks(): def InitMessageLinks(): def InitPeopleLinks(): def InitLinks(): # Main ------------------------------------------------------------------------