/// <summary> /// Calculate the size (in bytes) of a certain field depending on certain factors /// </summary> /// <param name="f">Field instance</param> /// <param name="game">Game the field is for</param> /// <param name="cache">Is the field in a cache file?</param> /// <param name="flags"><see cref="IO.ITagStreamFlags"/></param> /// <returns>Size in bytes of the field instance</returns> public static int Sizeof(Field f, BlamVersion game, bool cache, uint flags) { switch (f.FieldType) { case FieldType.String: return (f as String).GetFieldSize(); #region StringId case FieldType.OldStringId: if (Util.Flags.Test(flags, IO.ITagStreamFlags.Halo2OldFormat_StringId)) { return 32; // 28 bytes are preview string, 4 bytes for the string_id } goto case FieldType.StringId; case FieldType.StringId: if ((game & BlamVersion.Halo1) != 0 || (game & BlamVersion.Stubbs) != 0) { return 20; // we treat string ids as tag data in these games } else if (game == BlamVersion.Halo2_Alpha) { return 32; // 28 bytes are preview string, 4 bytes for the string_id } else if ((game & BlamVersion.Halo2) != 0 || // even echo used this format (game & BlamVersion.Halo3) != 0) { return 4; } throw new Debug.ExceptionLog("Failed to handle a string id: {0}", game); #endregion #region sizeof(byte) case FieldType.ByteInteger: case FieldType.ByteEnum: case FieldType.ByteFlags: case FieldType.ByteBlockIndex: return 1; #endregion #region sizeof(word) case FieldType.ShortInteger: case FieldType.Enum: case FieldType.WordFlags: case FieldType.ShortBlockIndex: return 2; #endregion #region sizeof(dword) case FieldType.LongInteger: case FieldType.Angle: case FieldType.Tag: case FieldType.LongEnum: case FieldType.Point2D: case FieldType.LongFlags: case FieldType.RgbColor: case FieldType.ArgbColor: case FieldType.Real: case FieldType.RealFraction: case FieldType.ShortBounds: case FieldType.LongBlockIndex: return 4; #endregion #region sizeof(qword) case FieldType.Rectangle2D: case FieldType.RealPoint2D: case FieldType.RealVector2D: case FieldType.RealEulerAngles2D: case FieldType.AngleBounds: case FieldType.RealBounds: case FieldType.RealFractionBounds: return 4 + 4; #endregion #region sizeof(real_vector3d) case FieldType.RealPoint3D: case FieldType.RealVector3D: case FieldType.RealPlane2D: case FieldType.RealRgbColor: case FieldType.RealEulerAngles3D: return 4 + 4 + 4; #endregion #region sizeof(real_vector4d) case FieldType.RealQuaternion: case FieldType.RealPlane3D: case FieldType.RealArgbColor: return 4 + 4 + 4 + 4; #endregion #region TagReference case FieldType.TagReference: if ((game & (BlamVersion.Halo1 | BlamVersion.Stubbs)) != 0) { return 16; } else if (game == BlamVersion.Halo2_Alpha || game == BlamVersion.Halo2_Epsilon) { return 16; } else if ((game & BlamVersion.Halo2) != 0) { if (cache) return 8; else return 16; } else if ((game & (BlamVersion.Halo3 | BlamVersion.HaloOdst)) != 0) { return 16; } else if ((game & BlamVersion.HaloReach) != 0) { return 16; // TODO: update upon release } throw new Debug.ExceptionLog("Failed to handle a tag reference: {0}", game); #endregion #region TagBlock case FieldType.Block: if ((game & (BlamVersion.Halo1 | BlamVersion.Stubbs)) != 0) { return 12; } else if (game == BlamVersion.Halo2_Alpha || game == BlamVersion.Halo2_Epsilon) { return 12; } else if ((game & BlamVersion.Halo2) != 0) { if (cache) return 8; else return 12; } else if ((game & (BlamVersion.Halo3 | BlamVersion.HaloOdst)) != 0) { return 12; } else if ((game & BlamVersion.HaloReach) != 0) { return 12; // TODO: update upon release } throw new Debug.ExceptionLog("Failed to handle a tag block: {0}", game); #endregion #region TagData case FieldType.Data: if ((game & (BlamVersion.Halo1 | BlamVersion.Stubbs)) != 0) { return 20; } else if (game == BlamVersion.Halo2_Alpha || game == BlamVersion.Halo2_Epsilon) { return 20; } else if ((game & BlamVersion.Halo2) != 0) { if (cache) return 8; else return 20; } else if ((game & (BlamVersion.Halo3 | BlamVersion.HaloOdst)) != 0) { return 20; } else if ((game & BlamVersion.HaloReach) != 0) { return 20; // TODO: update upon release } throw new Debug.ExceptionLog("Failed to handle tag data: {0}", game); #endregion case FieldType.VertexBuffer: if ((game & BlamVersion.Halo2) != 0) return 32; else return 0; case FieldType.Struct: if (cache && ((game & BlamVersion.Halo2) != 0)) return (f as IStruct).GetDefinition().RuntimeSizes.RuntimeSize; return (f as IStruct).GetDefinition().Attribute.SizeOf; case FieldType.StructReference: return 12; #region Padding\Skip case FieldType.Pad: return (f as Pad).Value; case FieldType.UnknownPad: return (f as UnknownPad).Value; case FieldType.UselessPad: if ((game & (BlamVersion.Halo1 | BlamVersion.Stubbs)) != 0 || game == BlamVersion.Halo2_Alpha || game == BlamVersion.Halo2_Epsilon) { return (f as UselessPad).Value; } else if ((game & (BlamVersion.Halo2 | BlamVersion.Halo3 | BlamVersion.HaloOdst | BlamVersion.HaloReach)) != 0) { if(Util.Flags.Test(flags, IO.ITagStreamFlags.Halo2OldFormat_UselessPadding)) return (f as UselessPad).Value; return 0; } throw new Debug.ExceptionLog("Failed to handle useless pad: {0}", game); case FieldType.Skip: return (f as Skip).Value; #endregion #region CustomData case FieldType.CustomData: return (f as CustomDataField).Sizeof(game, cache); #endregion #region Zero case FieldType.ArrayStart: case FieldType.ArrayEnd: case FieldType.Custom: case FieldType.Terminator: return 0; #endregion case FieldType.None: throw new Debug.ExceptionLog("Found a field that says it has no type: {0}", f.GetType()); default: throw new Debug.ExceptionLog("Sizeof failed to handle a field type: {0:X}", (int)f.FieldType); } }