using System; using System.Linq; using System.IO; using FSO.Files.Utils; namespace FSO.Files.Formats.IFF.Chunks { /// /// Type of OBJD. /// [Serializable()] public enum OBJDType { Unknown = 0, //Character or NPC Person = 2, //Buyable objects Normal = 4, //Roaches, Stoves2, TrClownGen, AnimTester, HelpSystem, JobFinder, NPCController, Stoves, //Tutorial, VisitGenerator, phonecall, unsnacker, CCPhonePlugin, EStove SimType = 7, //Stairs, doors, pool diving board & ladder, windows(?) Portal = 8, Cursor = 9, PrizeToken = 10, //Temporary location for drop or shoo Internal = 11, //these are mysteriously set as global sometimes. GiftToken = 12, Food = 34 } /// /// This is an object definition, the main chunk for an object and the first loaded by the VM. /// There can be multiple master OBJDs in an IFF, meaning that one IFF file can define multiple objects. /// public class OBJD : IffChunk { public uint Version; public static string[] VERSION_142_Fields = new string[] { "StackSize", "BaseGraphicID", "NumGraphics", "BHAV_MainID", "BHAV_GardeningID", "TreeTableID", "InteractionGroupID", "ObjectType", "MasterID", "SubIndex", "BHAV_WashHandsID", "AnimationTableID", "GUID1", "GUID2", "Disabled", "BHAV_Portal", "Price", "BodyStringID", "SlotID", "BHAV_AllowIntersectionID", "UsesFnTable", "BitField1", "BHAV_PrepareFoodID", "BHAV_CookFoodID", "BHAV_PlaceSurfaceID", "BHAV_DisposeID", "BHAV_EatID", "BHAV_PickupFromSlotID", "BHAV_WashDishID", "BHAV_EatSurfaceID", "BHAV_SitID", "BHAV_StandID", "SalePrice", "InitialDepreciation", "DailyDepreciation", "SelfDepreciating", "DepreciationLimit", "RoomFlags", "FunctionFlags", "CatalogStringsID", "Global", "BHAV_Init", "BHAV_Place", "BHAV_UserPickup", "WallStyle", "BHAV_Load", "BHAV_UserPlace", "ObjectVersion", "BHAV_RoomChange", "MotiveEffectsID", "BHAV_Cleanup", "BHAV_LevelInfo", "CatalogID", "BHAV_ServingSurface", "LevelOffset", "Shadow", "NumAttributes", "BHAV_Clean", "BHAV_QueueSkipped", "FrontDirection", "BHAV_WallAdjacencyChanged", "MyLeadObject", "DynamicSpriteBaseId", "NumDynamicSprites", "ChairEntryFlags", "TileWidth", "LotCategories", "BuildModeType", "OriginalGUID1", "OriginalGUID2", "SuitGUID1", "SuitGUID2", "BHAV_Pickup", "ThumbnailGraphic", "ShadowFlags", "FootprintMask", "BHAV_DynamicMultiTileUpdate", "ShadowBrightness", "BHAV_Repair", "WallStyleSpriteID", "RatingHunger", "RatingComfort", "RatingHygiene", "RatingBladder", "RatingEnergy", "RatingFun", "RatingRoom", "RatingSkillFlags", "NumTypeAttributes", "MiscFlags", "TypeAttrGUID1", "TypeAttrGUID2" }; public static string[] VERSION_138b_Extra_Fields = new string[] { "FunctionSubsort", "DTSubsort", "KeepBuying", "VacationSubsort", "ResetLotAction", "CommunitySubsort", "DreamFlags", "RenderFlags", "VitaboyFlags", "STSubsort", "MTSubsort" }; public ushort GUID1 { get { return (ushort)(GUID); } set { GUID = (GUID & 0xFFFF0000) | value; } } public ushort GUID2 { get { return (ushort)(GUID>>16); } set { GUID = (GUID & 0x0000FFFF) | ((uint)value<<16); } } public ushort StackSize { get; set; } public ushort BaseGraphicID { get; set; } public ushort NumGraphics { get; set; } public ushort TreeTableID { get; set; } public short InteractionGroupID { get; set; } public OBJDType ObjectType { get; set; } public ushort MasterID { get; set; } public short SubIndex { get; set; } public ushort AnimationTableID { get; set; } public uint GUID { get; set; } public ushort Disabled { get; set; } public ushort BHAV_Portal { get; set; } public ushort Price { get; set; } public ushort BodyStringID { get; set; } public ushort SlotID { get; set; } public ushort SalePrice { get; set; } public ushort InitialDepreciation { get; set; } public ushort DailyDepreciation { get; set; } public ushort SelfDepreciating { get; set; } public ushort DepreciationLimit { get; set; } public ushort RoomFlags { get; set; } public ushort FunctionFlags { get; set; } public ushort CatalogStringsID { get; set; } public ushort BHAV_MainID { get; set; } public ushort BHAV_GardeningID { get; set; } public ushort BHAV_WashHandsID { get; set; } public ushort BHAV_AllowIntersectionID { get; set; } public ushort UsesFnTable { get; set; } public ushort BitField1 { get; set; } public ushort BHAV_PrepareFoodID { get; set; } public ushort BHAV_CookFoodID { get; set; } public ushort BHAV_PlaceSurfaceID { get; set; } public ushort BHAV_DisposeID { get; set; } public ushort BHAV_EatID { get; set; } public ushort BHAV_PickupFromSlotID { get; set; } public ushort BHAV_WashDishID { get; set; } public ushort BHAV_EatSurfaceID { get; set; } public ushort BHAV_SitID { get; set; } public ushort BHAV_StandID { get; set; } public ushort Global { get; set; } public ushort BHAV_Init { get; set; } public ushort BHAV_Place { get; set; } public ushort BHAV_UserPickup { get; set; } public ushort WallStyle { get; set; } public ushort BHAV_Load { get; set; } public ushort BHAV_UserPlace { get; set; } public ushort ObjectVersion { get; set; } public ushort BHAV_RoomChange { get; set; } public ushort MotiveEffectsID { get; set; } public ushort BHAV_Cleanup { get; set; } public ushort BHAV_LevelInfo { get; set; } public ushort CatalogID { get; set; } public ushort BHAV_ServingSurface { get; set; } public ushort LevelOffset { get; set; } public ushort Shadow { get; set; } public ushort NumAttributes { get; set; } public ushort BHAV_Clean { get; set; } public ushort BHAV_QueueSkipped { get; set; } public ushort FrontDirection { get; set; } public ushort BHAV_WallAdjacencyChanged { get; set; } public ushort MyLeadObject { get; set; } public ushort DynamicSpriteBaseId { get; set; } public ushort NumDynamicSprites { get; set; } public ushort ChairEntryFlags { get; set; } public ushort TileWidth { get; set; } public ushort LotCategories { get; set; } public ushort BuildModeType { get; set; } public ushort OriginalGUID1 { get; set; } public ushort OriginalGUID2 { get; set; } public ushort SuitGUID1 { get; set; } public ushort SuitGUID2 { get; set; } public ushort BHAV_Pickup { get; set; } public ushort ThumbnailGraphic { get; set; } public ushort ShadowFlags { get; set; } public ushort FootprintMask { get; set; } public ushort BHAV_DynamicMultiTileUpdate { get; set; } public ushort ShadowBrightness { get; set; } public ushort BHAV_Repair { get; set; } public ushort WallStyleSpriteID { get; set; } public short RatingHunger { get; set; } public short RatingComfort { get; set; } public short RatingHygiene { get; set; } public short RatingBladder { get; set; } public short RatingEnergy { get; set; } public short RatingFun { get; set; } public short RatingRoom { get; set; } public ushort RatingSkillFlags { get; set; } public ushort[] RawData; public ushort NumTypeAttributes { get; set; } public ushort MiscFlags { get; set; } public uint TypeAttrGUID; public ushort FunctionSubsort { get; set; } public ushort DTSubsort { get; set; } public ushort KeepBuying { get; set; } public ushort VacationSubsort { get; set; } public ushort ResetLotAction { get; set; } public ushort CommunitySubsort { get; set; } public ushort DreamFlags { get; set; } public ushort RenderFlags { get; set; } public ushort VitaboyFlags { get; set; } public ushort STSubsort { get; set; } public ushort MTSubsort { get; set; } public ushort FootprintNorth { get { return (ushort)(FootprintMask & 0xF); } set { FootprintMask &= 0xFFF0; FootprintMask |= (ushort)(value & 0xF); } } public ushort FootprintEast { get { return (ushort)((FootprintMask >> 4) & 0xF); } set { FootprintMask &= 0xFF0F; FootprintMask |= (ushort)((value & 0xF) << 4); } } public ushort FootprintSouth { get { return (ushort)((FootprintMask >> 8) & 0xF); } set { FootprintMask &= 0xF0FF; FootprintMask |= (ushort)((value & 0xF) << 8); } } public ushort FootprintWest { get { return (ushort)((FootprintMask >> 12) & 0xF); } set { FootprintMask &= 0x0FFF; FootprintMask |= (ushort)((value & 0xF) << 12); } } public ushort TypeAttrGUID1 { get { return (ushort)(TypeAttrGUID); } set { TypeAttrGUID = (TypeAttrGUID & 0xFFFF0000) | value; } } public ushort TypeAttrGUID2 { get { return (ushort)(TypeAttrGUID >> 16); } set { TypeAttrGUID = (TypeAttrGUID & 0x0000FFFF) | ((uint)value << 16); } } public bool IsMaster { get { return SubIndex == -1; } } public bool IsMultiTile { get { return MasterID != 0; } } public T GetPropertyByName(string name) { Type me = typeof(OBJD); var prop = me.GetProperty(name); return (T)Convert.ChangeType(prop.GetValue(this, null), typeof(T)); } public void SetPropertyByName(string name, object value) { Type me = typeof(OBJD); var prop = me.GetProperty(name); try { value = Convert.ChangeType(value, prop.PropertyType); } catch { value = Enum.Parse(prop.PropertyType, value.ToString()); } prop.SetValue(this, value, null); } public override void Read(IffFile iff, Stream stream) { using (var io = IoBuffer.FromStream(stream, ByteOrder.LITTLE_ENDIAN)) { this.Version = io.ReadUInt32(); /**136 (80 fields) 138a (95 fields) - Used for The Sims 1 base game objects? 138b (108 fields) - Used for The Sims 1 expansion objects? 139 (96 fields) 140 (97 fields) 141 (97 fields) 142 (105 fields)**/ var numFields = 80; if (Version == 138) { numFields = 95; } else if (Version == 139) { numFields = 96; } else if (Version == 140) { numFields = 97; } else if (Version == 141) { numFields = 97; } else if (Version == 142) { numFields = 105; } numFields -= 2; RawData = new ushort[numFields]; io.Mark(); for (var i = 0; i < numFields; i++) { RawData[i] = io.ReadUInt16(); } io.SeekFromMark(0); this.StackSize = io.ReadUInt16(); this.BaseGraphicID = io.ReadUInt16(); this.NumGraphics = io.ReadUInt16(); this.BHAV_MainID = io.ReadUInt16(); this.BHAV_GardeningID = io.ReadUInt16(); this.TreeTableID = io.ReadUInt16(); this.InteractionGroupID = io.ReadInt16(); this.ObjectType = (OBJDType)io.ReadUInt16(); this.MasterID = io.ReadUInt16(); this.SubIndex = io.ReadInt16(); this.BHAV_WashHandsID = io.ReadUInt16(); this.AnimationTableID = io.ReadUInt16(); this.GUID = io.ReadUInt32(); this.Disabled = io.ReadUInt16(); this.BHAV_Portal = io.ReadUInt16(); this.Price = io.ReadUInt16(); this.BodyStringID = io.ReadUInt16(); this.SlotID = io.ReadUInt16(); this.BHAV_AllowIntersectionID = io.ReadUInt16(); this.UsesFnTable = io.ReadUInt16(); this.BitField1 = io.ReadUInt16(); this.BHAV_PrepareFoodID = io.ReadUInt16(); this.BHAV_CookFoodID = io.ReadUInt16(); this.BHAV_PlaceSurfaceID = io.ReadUInt16(); this.BHAV_DisposeID = io.ReadUInt16(); this.BHAV_EatID = io.ReadUInt16(); this.BHAV_PickupFromSlotID = io.ReadUInt16(); this.BHAV_WashDishID = io.ReadUInt16(); this.BHAV_EatSurfaceID = io.ReadUInt16(); this.BHAV_SitID = io.ReadUInt16(); this.BHAV_StandID = io.ReadUInt16(); this.SalePrice = io.ReadUInt16(); this.InitialDepreciation = io.ReadUInt16(); this.DailyDepreciation = io.ReadUInt16(); this.SelfDepreciating = io.ReadUInt16(); this.DepreciationLimit = io.ReadUInt16(); this.RoomFlags = io.ReadUInt16(); this.FunctionFlags = io.ReadUInt16(); this.CatalogStringsID = io.ReadUInt16(); this.Global = io.ReadUInt16(); this.BHAV_Init = io.ReadUInt16(); this.BHAV_Place = io.ReadUInt16(); this.BHAV_UserPickup = io.ReadUInt16(); this.WallStyle = io.ReadUInt16(); this.BHAV_Load = io.ReadUInt16(); this.BHAV_UserPlace = io.ReadUInt16(); this.ObjectVersion = io.ReadUInt16(); this.BHAV_RoomChange = io.ReadUInt16(); this.MotiveEffectsID = io.ReadUInt16(); this.BHAV_Cleanup = io.ReadUInt16(); this.BHAV_LevelInfo = io.ReadUInt16(); this.CatalogID = io.ReadUInt16(); this.BHAV_ServingSurface = io.ReadUInt16(); this.LevelOffset = io.ReadUInt16(); this.Shadow = io.ReadUInt16(); this.NumAttributes = io.ReadUInt16(); this.BHAV_Clean = io.ReadUInt16(); this.BHAV_QueueSkipped = io.ReadUInt16(); this.FrontDirection = io.ReadUInt16(); this.BHAV_WallAdjacencyChanged = io.ReadUInt16(); this.MyLeadObject = io.ReadUInt16(); this.DynamicSpriteBaseId = io.ReadUInt16(); this.NumDynamicSprites = io.ReadUInt16(); this.ChairEntryFlags = io.ReadUInt16(); this.TileWidth = io.ReadUInt16(); this.LotCategories = io.ReadUInt16(); this.BuildModeType = io.ReadUInt16(); this.OriginalGUID1 = io.ReadUInt16(); this.OriginalGUID2 = io.ReadUInt16(); this.SuitGUID1 = io.ReadUInt16(); this.SuitGUID2 = io.ReadUInt16(); this.BHAV_Pickup = io.ReadUInt16(); this.ThumbnailGraphic = io.ReadUInt16(); this.ShadowFlags = io.ReadUInt16(); this.FootprintMask = io.ReadUInt16(); this.BHAV_DynamicMultiTileUpdate = io.ReadUInt16(); this.ShadowBrightness = io.ReadUInt16(); if (numFields > 78) { this.BHAV_Repair = io.ReadUInt16(); this.WallStyleSpriteID = io.ReadUInt16(); this.RatingHunger = io.ReadInt16(); this.RatingComfort = io.ReadInt16(); this.RatingHygiene = io.ReadInt16(); this.RatingBladder = io.ReadInt16(); this.RatingEnergy = io.ReadInt16(); this.RatingFun = io.ReadInt16(); this.RatingRoom = io.ReadInt16(); this.RatingSkillFlags = io.ReadUInt16(); if (numFields > 90) { this.NumTypeAttributes = io.ReadUInt16(); this.MiscFlags = io.ReadUInt16(); this.TypeAttrGUID = io.ReadUInt32(); try { this.FunctionSubsort = io.ReadUInt16(); this.DTSubsort = io.ReadUInt16(); this.KeepBuying = io.ReadUInt16(); this.VacationSubsort = io.ReadUInt16(); this.ResetLotAction = io.ReadUInt16(); this.CommunitySubsort = io.ReadUInt16(); this.DreamFlags = io.ReadUInt16(); this.RenderFlags = io.ReadUInt16(); this.VitaboyFlags = io.ReadUInt16(); this.STSubsort = io.ReadUInt16(); this.MTSubsort = io.ReadUInt16(); } catch (Exception) { //past this point if these fields are here is really a mystery } } if (this.TypeAttrGUID == 0) this.TypeAttrGUID = GUID; } } } public override bool Write(IffFile iff, Stream stream) { using (var io = IoWriter.FromStream(stream, ByteOrder.LITTLE_ENDIAN)) { if (IffFile.TargetTS1) { //version 138 io.WriteUInt32(138); var fields = VERSION_142_Fields.Concat(VERSION_138b_Extra_Fields); foreach (var prop in fields) { io.WriteUInt16((ushort)GetPropertyByName(prop)); } for (int i = fields.Count(); i < 108; i++) { io.WriteUInt16(0); } } else { //tso version 142 io.WriteUInt32(142); foreach (var prop in VERSION_142_Fields) { io.WriteUInt16((ushort)GetPropertyByName(prop)); } for (int i = VERSION_142_Fields.Length; i < 105; i++) { io.WriteUInt16(0); } } } return true; } } }