Skip to content

Record Building Blocks

Lojack edited this page Apr 25, 2014 · 2 revisions

The goal of this page is to describe the various classes used for describing record elements (subrecords). How they behave, and long-term goals in relation to these classes.

Classes

  • StringRecord - Handles a subrecord that contains a null-terminated string
  • NonNullStringRecord - Handles a subrecord that contains a non-null-terminated string
  • UnorderedPackedStrings - Handles a subrecord with multiple null-terminated strings
  • RawRecord - Handles a subrecord with unknown data. Data is read in as raw bytes
  • SimpleSubRecord<type, default=0> - Use with simple data types that initialize to zero (int, etc). Use this version when it's unknown if the subrecord is required or optional.
  • SimpleFloatSubRecord<default=0.0> - Specialization of SimpleSubRecord for floats
  • ReqSimpleSubRecord<type, default=0> - Specialization of SimpleSubRecord that will always write the subrecord no matter the value.
  • ReqSimpleFloatSubRecord<default=0.0> - Specialization of ReqSimpleSubRecord for floats.
  • OptSimpleSubRecord<type, default=0> - Specialization of SimpleSubRecord, that will only write the subrecord if it is not the default value.
  • OptZeroSubRecord<type> - like OptSimpleSubRecord, comments say it will still be considered Unloaded if they are equal to their default. Not entirely sure how these are different then the previous types though...
  • OptSimpleFloatSubRecord<default=0.0> - Specialization of SimpleFloatSubRecord that will only write the subrecord if it is not the default value.
  • SemiOptSimpleSubRecord<type, default=0> - almost eactly like SimpleSubRecord, except that once it has been loaded, it will always write out its applicable subrecord (as opposed to SimpleSubRecord, which will no write it after being loaded if it is equal to the default).
  • SemiOptSimpleFloatSubRecord<default=0.0 - specialization of SemiOptSimpleSubRecord for floats.
  • SubRecord<type> - like SimpleSubRecord, but for non-basic types. Use this kind of element when dealing with structs that directly map to the memory layout on disk.
  • ReqSubRecord<type> - specialization of SubRecord that will always write the subrecord
  • OptSubRecord<type> - specialization of SubRecord, where the subrecord will only be written if it is not equal to the default value, where the default is determined by the default constructor for type.
  • SemiOptSubRecord<type> - specialization of SubRecord, where the subrecord is only written if it was read in.
  • OrderedPackedArray<type, cmp=std::less> - Single subrecord with repeating elements of type, sorted. If type does not have a less-than operator defined, then you must supply a custom function to do the comparisons.
  • UnorderedPackedArray<type> - Single subrecord with repeating elements of type.
  • OrderedSparseArray<type, cmp=std::less> - Multiple subrecords of the same type, each containing a single element of type, sorted.
  • UnorderedSparseArray - Multiple subrecords of the same type, each containing a single element of type.
  • ReqCounted<type, countType, countRecord> - Used to wrap around one of the ***Array classes. Used when one of the ***Array type subrecords is supposed to be preceeded by another subrecord that holds the count of items. countType is the type of int to hold this (to correlate with the required size of the subrecord: ie UINT8 is the subrecord is supposed to be 1 byte, etc). countRecord is the REV32 of the counting record. Lika other Req*** subrecords, this one will always be written (the preceeding counting subrecord). An example from Skyrim: a record has keywords attached via multiple KWDA subrecord. The KWDAs are preceeded by a, 1-byte KSIZ subrecord denoting how many KWDA subrecords there are, define it like: ReqCounted<UnorderedSparseArray<FORMID>, UINT8, REV32(KSIZ)>.
  • OptCounted<type, countType, countRecord> - Like ReqCounted, but the counting subrecord will only be written if there are elements in the array.

Oblivion

  • OBMEEFIXSubRecord - hacky support for OBME EFIX subrecord. Like an OptSubRecord, except if value->efixOverride == 0, then it gets unloaded

Skyrim

  • LStringRecord - Handles translatable strings in Skyrim. If the TES4 record has the flag set for translation strings, then this subrecord is read in as an Id, then the string is looked up from the applicable strings files. Otherwise, it is treated the same as a StringRecord.
  • VMADRecord - Reads in the data from VMAD subrecords.

Other

DefaultSingleton<type> - This is a singleton creator to create default values for ***SubRecord type elements (ie: when it's defined using a struct). Used internally by many of the above types, however it is useful to use this as well in the record API for deleting elements.

Goals

Standardize the interface to each of these classes:

  1. As it is currently, to get at the actual data contained in the record, for some types you use ->, and for others you use .. I'll probably standardize this to ->
  2. StringRecord/LStringRecord - overload == to do equals, so the only time you need to call a function for comparison is if you're doing case-insensitive comparison.
  3. More overloaded assignment operators. As an example, currently for a SimpleSubRecord<int>, so assign the value you would have to do subrecord.value = newValue;. I want to provide a second overloaded = to allow direct assignment without having to access .value.
  4. Indexing operator overloading for ***Array types. Currently it's clunky in that you have to do subrecord.value[index]
  5. Expose commonly used std::vector functions in the ***Array types. Similar to the above, it's currently clunky to call things like subrecord.value.size(), etc.
  6. Come up with a good way to return the address of value, etc. This is mostly for the GetAttribute API function. Most cases you end up with code like: &DNAM.value and FULL.value. Basically I want to take away the need to use .value at all in the code. This one is tricky in that I don't want to overload the & reference operator, as that can end up giving very unintuitive results in places. Perhaps do it in the form of a functions (addr()), or overload the () operator, or perhaps the * operator.
  7. I also want to combine the ***SimpleSubRecord and ***SimpleFloatRecord types, via template specialization. Less names to remember that way.
  8. Possibly do away with the Req**** and Opt**** types, and instead implement wrapper templates (similar to the ReqCounted and OptCounted templates) that will wrap around the applicable type. Once again, less names to remember that way.
Clone this wiki locally