-
Notifications
You must be signed in to change notification settings - Fork 68
Save File Formats
BYTE ScreenBuffer[64000];
BYTE Palette[768];
GAMESTATE GameState; // 3559 bytes
WORD GameLevel[0xC000];
GAMESTATE
BYTE junk;
BYTE WeatherFlags; // 0x80 precipitation, 1 rain, 2 snow
BYTE PlayerFloor;
BYTE OldFloor;
BYTE junk[388];
WORD LightsCount;
LIGHT Lights[256]; // autogenerated lights, 6 bytes
DWORD junk;
char LevelName[33];
char InfName[13];
BYTE junk[128];
MIFHEADER LevelHeader; // 61 byte; same as in MIF files
char MifName[13];
DWORD junk;
WORD WalkSpeed;
WORD TurnSpeed;
DWORD junk;
WORD Flags5;
WORD Flags4;
BYTE junk[48];
WORD LevelEntryX, LevelEntryY;
WORD PlayerX, PlayerZ, PlayerY;
DWORD unknown1;
WORD PlayerAngle;
BYTE junk[6];
WORD LightRadius;
BYTE junk[576];
LOCK Locks[64]; // 3 bytes
BYTE LockCount;
TARGET Targets[64]; // 2 bytes
BYTE TargetCount;
TRIGGER Triggers[64]; // 4 bytes
BYTE TriggerCount;
BYTE junk[136];
Flags4
Value | Comment |
---|---|
0x200 |
Player is in the wilderness |
The first 2 structures in SAVEENGN
are scrambled in one pass.
scramble(data, length) <-
buffer <- length
i <- 0
while i < length
key <- ( ror16(buffer, buffer&0xF) & 0xFF
data[i] <- data[i] xor key
i <- i + 1
buffer <- buffer - 1
NPCDATA Player; // 1054 bytes
PLAYERDATA PlayerData; // 2609 bytes
// End of the scrambled part
NPCDATA Enemies[8];
LOOTITEM Loot[200]; // 28 bytes
INTERNALSTATE GameState2; // 288 bytes
NPCDATA
DWORD RandomSeed;
BYTE Race;
BYTE Class;
BYTE Level;
BYTE IsFemale;
BYTE HomeCityId;
union {
char Name[32];
CREATUREDATA CrData; // 32 bytes
}
BYTE CurrentAttrs[8];
BYTE BaseAttrs[8];
WORD ActorValues[16];
WORD HP, MaxHP, Stamina;
BYTE misc[7];
WORD Mana, MaxMana;
BYTE misc2;
BYTE face; // face index in appropriate CIF file
BYTE misc3[2];
INVENTORYITEM Inventory[40];
BYTE KnownSpellCount;
BYTE KnownSpellIDs[160];
WORD Gold;
DWORD Experience; // NPC only
WORD StatusFlags;
BYTE StatusCounters[10];
BYTE EncumbranceMod;
WORD ActiveEffects;
WORD ShieldValue;
NPC attributes are stored in the internal format, where 255 corresponds to a displayed value of 100. ActorValues
are internally multiplied by 5.
Actor values
0 Damage bonus
1 Carry limit
5 Magic defense
7 Defense bonus
8 Attack bonus
9 Bonus AC
11 Bonus health
13 Bonus healing
14 Charisma
15 Bonus luck
Status flags
0x01 - diseased
0x02 - poisoned
0x04 - cursed
0x08 - blessed
0x10 - fortified
0x20 - drunk
0x40 - paralyzed
0x80 - regenerating
0x100 - dead
0x200 - being drained
0x1000 - is a creature
0x4000 - is the player (?)
Active effects
0x01 - invisible
0x02 - a non-target
0x04 - resistant to fire
0x08 - resistant to cold
0x10 - resistant to shock
0x20 - resistant to acid
0x40 - resistant to poison
0x80 - levitating
0x0100 - Probably the Light effect
0x0200 - silenced
0x0400 - able to absorb spells
0x0800 - able to reflect spells
0x1000 - resistant to spells
(to be continued)
PLAYERDATA
DWORD PCGold;
WORD Blessing;
WORD Flags2;
WORD GameOptions;
DWORD GameTime;
WORD DateTime[5]; // YMDHm
DWORD junk;
WORD MovementAngle;
WORD Flags6;
BYTE junk;
BYTE JumpPhase;
BYTE Flags7; // 1 if in the starting dungeon
BYTE junk[23];
DWORD RandomDungeonSeed;
WORD WildernessX, WildernessY;
BYTE WildernessBlocks[4];
WORD WildernessBlockX, WildernessBlockY;
WORD ReturnBlockX, ReturnBlockY;
WORD ReturnX, ReturnY;
WORD ReturnAngle;
DWORD WildernessSeed;
BYTE junk[8];
DYNTRIGGER DynamicTriggers[8]; // 6 bytes
BYTE Keyring[14];
BUFF PlayerBuffs[16]; // 60 bytes
BYTE junk[2];
CITYGENDATA CurrentCity; // 56 bytes
BYTE Weather[36];
BYTE junk[53];
NPCSPRITE NPCs[15];
NPCSPRITE Enemies[8]; // 28 bytes
BYTE junk[147];
BASEQUEST CityQuests[4]; // 41 bytes
EXTQUEST PalaceQuests[4]; // 52 bytes
EXTQUEST RumorsBuffer;
DWORD unk1;
MQDATA MainQuest; // 10 bytes
BYTE junk2[8];
WORD PickedLocks[32];
BYTE junk3[6];
WORD WorldX, WorldY;
WORD junk4;
BYTE Portrait[9];
AQDATA ArtifactQuest; // 19 bytes
BYTE junk1[2];
BYTE LootRecordCount;
Flags2
Value | Comment |
---|---|
0x800 | Player is in a dungeon |
0x2000 | Draw compass |
0x4000 | Dungeon entered from wilderness, need to return to the wilderness pos |
Flags6
Value | Comment |
---|---|
0x10 | Player's weapon/fists are readied |
0x100 | Have shown holiday text. Cleared on day change. |
0x2000 | Player is moving |
LOOTITEM
WORD unknown1;
WORD ContainerPosition;
BYTE Floor;
WORD GoldValue;
WORD unknown2;
INVENTORYITEM Item; // 19 bytes
INVENTORYITEM
BYTE SlotID;
WORD Weight;
BYTE Hands; // Also # of charges left? (could be wrong because there is `Health` field)
BYTE Param1;
BYTE Param2;
WORD Health; // Also amount of money left for King Orgnum's coffer
WORD MaxHealth; // Also # of max charges
DWORD Price; // Repair cost * Repair time is related to this field.
BYTE Flags;
BYTE X;
BYTE Material;
BYTE Y;
BYTE Attribute;
Interpretation of SlotID, Param1, Param2, Flags, X
and Y
depends on the item type.
Flag byte: evttccnm
-
e
Equipped -
v
This item record is in use -
t
Subtype -
c
Class -
n
Not identified -
m
Has a material
c t X Y Param1 Param2 Weight
0 Armor Plt/Chn/Lthr Enchantment Spell AC - Weight
1 Weapon - Enchantment Spell MinDmg MaxDmg Weight
2 Trinket Spell/AttrBonus/AC (see below)
3 Potion - - Spell - - Stack size
For spell-casting trinkets, 'Material' shows its classification: offensive, defensive or misc, and 'Hands' stores the charges left.
For attribute-enchancing trinkets, Y
contains the decimal bonus value.
CITYGENDATA
char CityName[20];
char MIFName[13];
BYTE CitySize; // 4..6
WORD BlockOffset;
BYTE Province;
BYTE CityType;
WORD LocalX, LocalY;
BYTE CityID;
BYTE unk1;
WORD AbsLatitude;
BYTE TerrainType;
BYTE Quarter;
DWORD RulerSeed;
DWORD CitySeed;
BASEQUEST
DWORD QuestSeed;
WORD Location1;
WORD Item1;
DWORD StartDate;
DWORD DueDate;
DWORD TavernData;
DWORD DestinationData;
DWORD Reward;
WORD Portrait;
DWORD QuestGiverSeed;
WORD OpponentFaction;
BYTE DestCityID;
BYTE QuestCityID;
BYTE unk1;
BYTE Relationship;
BYTE EscorteeIsFemale;
EXTQUEST
BASEQUEST q;
BYTE unk1[5];
BYTE Faction;
BYTE NPCRace;
BYTE MonsterRace;
BYTE LocationName;
BYTE LocNameTmpl;
AQDATA
BYTE CurrentArtifact;
DWORD TavernLocation;
BYTE MapDungeonId;
BYTE MapProvince;
BYTE ArtifactDungeonId;
BYTE ArtifactProvince;
BYTE ArtifactQuestFlags;
WORD ArtifactBitmask;
DWORD ArtifactQuestStarted;
BYTE ArtifactDays;
WORD ArtPriceOrOffset;
MQDATA
BYTE CanHaveVision;
BYTE NextStep;
BYTE DungeonLocationKnown;
BYTE AcceptedKeyQuest;
BYTE HadVision;
BYTE TalkedToRuler;
BYTE StepDone;
BYTE HasKeyItem;
BYTE HasStaffPiece;
BYTE ShowKey;
NPCSPRITE
This is an extremely versatile structure, and it is used in many different ways.
WORD X, Z, Y;
PVOID pDynamicLight;
WORD Speed;
WORD Angle;
BYTE Flat;
BYTE Frame;
BYTE Param1;
WORD Flags;
BYTE Param2;
WORD Data;
WORD Param3;
WORD unk1;
WORD hasPlayedBodyFallSound;
WORD Param4;
If Flags
have 0x4000
bit set, the structure is not used.
For town NPCs, Data
is a random value used for color transformation. For enemies, this is a pointer to a NPCDATA
structure.
If Flat
has two highest bits set, the (lower bits + 1) specify the animation index, 1 to 5. Param2
specifies the slot the animation was loaded into. There are many preloaded animations, and NPCs use only the slots 52
and 57
. Apparently, only a single type of creature can be shown at one time by Arena.
(more structs to come)
State of locks and triggers for the MQ dungeons only.
struct {
BYTE TriggerCount;
TRIGGER Triggers[64]; // 4 bytes
BYTE LockCount;
LOCK Locks[64];
} LevelHashTable[64];
id <- (Province*2 + DungeonID)*4 + Level
SPELLDATA Spells[N]; // 85 bytes
N
= 32 for SPELLS
(custom spells) and 128 for SPELLSG
(standard spells).
Indices of custom spells have the highest bit set.
SPELLDATA
WORD Param0[3]; // Spell damage range start (chance)?
WORD Param1[3]; // Spell damage range end (chance)?
WORD Param2[3]; // Spell damage increase range start?
WORD Param3[3]; // Spell damage increase range end?
WORD Param4[3]; // Number of levels to spell damage increase (chance)?
WORD Param5[3]; // Number of uses to spell damage increase?
BYTE TargetType;
BYTE unk;
BYTE Element;
WORD Flags;
BYTE Effects[3]; // e.g. 'Fortify'; 0xFF = no effect
BYTE SubEffects[3]; // e.g. 'Attribute:'
BYTE AffectedAttr[3]; // e.g. 'Strength'
WORD Cost;
char Name[33];
BUFF (Fortify spells, drunkenness, poison, disease, etc. that affect attributes)
WORD RemainingDuration2 (Not sure, is 1 higher than RemainingDuration and counts down with it)
WORD RemainingDuration (Remaining duration in game minutes before the effect starts wearing off)
WORD
SBYTE STRModifyAmount
SBYTE INTModifyAmount
SBYTE WILModifyAmount
SBYTE AGIModifyAmount
SBYTE SPDModifyAmount
SBYTE ENDModifyAmount
SBYTE PERModifyAmount
SBYTE LUCModifyAmount
Unknown (6 bytes)
WORD
WORD
WORD
Unknown (14 bytes)
DWORD npcDataPtr (Points to the NPCDATA of the affected entity)
WORD
WORD
WORD
Unknown (6 bytes)
BYTE
BYTE
BYTE Index (0xFF when this BUFF slot is unused. 0-based index of this status effect (see the order in "Status Flags"))
BYTE ReleasedAmount (0xFF when this BUFF slot is unused. "1" while the effect is active, then starts increasing as the effect wears off, counting the number of points that the affected attribute has returned from its modified value to the original value. When the count reaches the full amount that the effect was modified while active, the effect has completely worn off and the BUFF slot is cleared to an unused state.)
The parameters likely depend on the spell type.
Inn room reservation.
WORD HoursLeft;
DWORD TimeLimit;
n
is the third and following digits of the tavern identifier:
- For cities, door coordinates:
(Y<<8)+X
. - For the wilderness, block coordinates:
(wY<<16)+wX
Items being repaired.
REPAIRJOB jobs[5];
REPAIRJOB
BYTE Valid;
DWORD DueTo;
INVENTORYITEM item;
FoW caches.
struct {
DWORD lvlhash;
struct {
WORD x, y;
char text[60];
} notes[64];
BYTE bitmap[4096]; // 2 bits per block
} cache[16];
If x
has the 0x8000
bit set, then it is absolute pixel coordinates. Otherwise, it is the automap block coordinates.
Bitmap values range from 0 - hidden to 3 - completely visible.
The hash
value is 0xC0000000
for the starting dungeon, and the following 32-bit value otherwise:
tttpppff cityid Y X
where X and Y are the wilderness block coordinates if it is a wilderness dungeon, cityid
is a dungeon or city id, t
is the dungeon type (3), p
the province, and f
is the floor.
The log file consists of records terminated by " *"
. Each record starts with a header, which starts with &
and ends with \r\n
.