private uint GetExtraDataSize(UtfHeader header, out UtfFieldImage[] orderedDataFieldImages)
        {
            var  baseOffset = header.ExtraDataOffset;
            uint totalSize  = 0;

            var fieldImagesInNewOrder = new List <UtfFieldImage>();
            var row0 = Rows[0];
            var firstTableDataFieldIndex = -1;

            foreach (var fieldImage in row0)
            {
                if (fieldImage.Type == ColumnType.Data && (fieldImage.Storage == ColumnStorage.Constant || fieldImage.Storage == ColumnStorage.Constant2))
                {
                    AddToNewOrdererdDataFieldList(fieldImagesInNewOrder, fieldImage, ref firstTableDataFieldIndex);
                }
            }

            foreach (var row in Rows)
            {
                foreach (var fieldImage in row)
                {
                    if (fieldImage.Type == ColumnType.Data && fieldImage.Storage == ColumnStorage.PerRow)
                    {
                        AddToNewOrdererdDataFieldList(fieldImagesInNewOrder, fieldImage, ref firstTableDataFieldIndex);
                    }
                }
            }

            // If the first field is a table, rebase the whole data field.
            if (firstTableDataFieldIndex == 0)
            {
                baseOffset             = SerializationHelper.RoundUpAsTable(baseOffset, Alignment);
                header.ExtraDataOffset = baseOffset;
            }

            orderedDataFieldImages = fieldImagesInNewOrder.ToArray();

            foreach (var fieldImage in orderedDataFieldImages)
            {
                var rawOffset = baseOffset;

                // Tables' starting locations are rounded by Alignment (usually 0x20).
                if (fieldImage.IsTable && fieldImage.DataValue.Length > 0)
                {
                    baseOffset = SerializationHelper.RoundUpAsTable(baseOffset, Alignment);
                }

                baseOffset += (uint)fieldImage.DataValue.Length;

                // Tables' ending locations are rounded by 4.
                if (fieldImage.IsTable && fieldImage.DataValue.Length > 0)
                {
                    baseOffset = AcbHelper.RoundUpToAlignment(baseOffset, 4);
                }

                totalSize += (baseOffset - rawOffset);
            }

            return(totalSize);
        }
예제 #2
0
        public static byte[] RoundUpTo(this byte[] data, int alignment)
        {
            var newLength = AcbHelper.RoundUpToAlignment(data.Length, alignment);
            var buffer    = new byte[newLength];

            data.CopyTo(buffer, 0);
            return(buffer);
        }
예제 #3
0
        private static void WriteAfs2ArchiveToStream(ReadOnlyCollection <byte[]> files, Stream stream, uint alignment)
        {
            var fileCount = (uint)files.Count;

            if (files.Count >= ushort.MaxValue)
            {
                throw new IndexOutOfRangeException($"File count {fileCount} exceeds maximum possible value (65535).");
            }
            if (files.Count != 1)
            {
                throw new NotSupportedException("Currently DereTore does not support more than one file.");
            }
            stream.WriteBytes(Afs2Archive.Afs2Signature);
            const uint version = 0x00020401;

            stream.WriteUInt32LE(version);
            stream.WriteUInt32LE(fileCount);
            stream.WriteUInt32LE(alignment);
            const uint offsetFieldSize = (version >> 8) & 0xff; // version[1], always 4? See Afs2Archive.Initialize().

            // Prepare the fields.
            var afs2HeaderSegmentSize = 0x10 +                        // General data
                                        2 * fileCount +               // Cue IDs
                                        offsetFieldSize * fileCount + // File offsets
                                        sizeof(uint);                 // Size of last file (U32)
            // Assuming the music file always has ID 0 in Waveform table and Cue table.
            var records = new List <Afs2FileRecord>();
            var currentFileRawOffset = afs2HeaderSegmentSize;

            for (ushort i = 0; i < fileCount; ++i)
            {
                var record = new Afs2FileRecord {
                    // TODO: Use the Cue table.
                    CueId             = i,
                    FileOffsetRaw     = currentFileRawOffset,
                    FileOffsetAligned = AcbHelper.RoundUpToAlignment(currentFileRawOffset, alignment)
                };
                records.Add(record);
                currentFileRawOffset = (uint)(record.FileOffsetAligned + files[i].Length);
            }

            var lastFileEndOffset = currentFileRawOffset;

            for (var i = 0; i < files.Count; ++i)
            {
                stream.WriteUInt16LE(records[i].CueId);
            }
            for (var i = 0; i < files.Count; ++i)
            {
                stream.WriteUInt32LE((uint)records[i].FileOffsetRaw);
            }
            // TODO: Dynamically judge it. See Afs2Archive.Initialize().
            stream.WriteUInt32LE(lastFileEndOffset);
            for (var i = 0; i < files.Count; ++i)
            {
                stream.SeekAndWriteBytes(files[i], records[i].FileOffsetAligned);
            }
        }
예제 #4
0
 public static uint RoundUpAsTable(uint value, uint alignment)
 {
     // This action seems weird. But it does exist (see Cue table in CGSS song_1001[oneshin]), I don't know why.
     value = AcbHelper.RoundUpToAlignment(value, 4);
     if (value % alignment == 0)
     {
         value += alignment;
     }
     return(AcbHelper.RoundUpToAlignment(value, alignment));
 }
예제 #5
0
        /// <summary>
        /// Some fields are not filled.
        /// </summary>
        /// <returns></returns>
        public UtfHeader GetHeader(out UtfFieldImage[] orderedDataFieldImages)
        {
            // Basic information
            if (Rows.Count < 1)
            {
                throw new InvalidOperationException("Rows should not be empty.");
            }

            var fieldCount = Rows[0].Count;

            for (var i = 1; i < Rows.Count; ++i)
            {
                if (Rows[i].Count != fieldCount)
                {
                    throw new InvalidOperationException("Number of fields in each row do not match.");
                }
            }

            var singleRowDataSize = GetSingleRowDataSize();
            var rowDescriptorSize = GetRowDescriptorSize();

            var header = new UtfHeader {
                TableName        = TableName,
                RowCount         = (uint)Rows.Count,
                FieldCount       = (ushort)fieldCount,
                Unknown1         = 1,
                RowSize          = singleRowDataSize,
                PerRowDataOffset = rowDescriptorSize + SchemaOffset // "per row" data offset, actually
            };

            var perRowDataSize = header.RowCount * header.RowSize;

            header.StringTableOffset = header.PerRowDataOffset + perRowDataSize;
            header.TableNameOffset   = 0;

            var stringTableSize = GetStringTableSize();

            header.ExtraDataOffset = header.StringTableOffset + stringTableSize;

            var extraDataSize = GetExtraDataSize(header, out orderedDataFieldImages);

            header.TableSize = header.ExtraDataOffset + extraDataSize;

            // ???
            header.TableSize = AcbHelper.RoundUpToAlignment(header.TableSize, 4);

            return(header);
        }
예제 #6
0
        private void SetExtraDataFieldRelocations(UtfHeader header, UtfFieldImage[] orderedFieldImages)
        {
            // This value is already calibrated in GetExtraDataSize(). It will move to a mod16 position if the first item is a table.
            var baseOffset    = header.ExtraDataOffset;
            var currentOffset = baseOffset;

            foreach (var fieldImage in orderedFieldImages)
            {
                if (fieldImage.DataValue.Length > 0)
                {
                    // For priority, see AddToNewOrderedDataFieldList().
                    if (fieldImage.IsTable)
                    {
                        currentOffset = SerializationHelper.RoundUpAsTable(currentOffset, Alignment);
                    }

                    fieldImage.DataOffset = currentOffset - baseOffset;

                    currentOffset += (uint)fieldImage.DataValue.Length;

                    // Extra 4-byte alignment at the end of tables.
                    if (fieldImage.IsTable)
                    {
                        currentOffset = AcbHelper.RoundUpToAlignment(currentOffset, Alignment);
                    }
                }
                else
                {
                    fieldImage.DataOffset = 0;
                }
            }

            var row0 = Rows[0];

            for (var i = 1; i < Rows.Count; ++i)
            {
                for (var j = 0; j < Rows[i].Count; ++j)
                {
                    var fieldImage = Rows[i][j];

                    if (fieldImage.Type == ColumnType.Data && (fieldImage.Storage == ColumnStorage.Constant || fieldImage.Storage == ColumnStorage.Constant2))
                    {
                        fieldImage.DataOffset = row0[j].DataOffset;
                    }
                }
            }
        }
예제 #7
0
        public void WriteValueTo(Stream stream)
        {
            switch (Type)
            {
            case ColumnType.Byte:
                stream.WriteByte(NumericValue.U8);
                break;

            case ColumnType.SByte:
                stream.WriteSByte(NumericValue.S8);
                break;

            case ColumnType.UInt16:
                stream.WriteUInt16BE(NumericValue.U16);
                break;

            case ColumnType.Int16:
                stream.WriteInt16BE(NumericValue.S16);
                break;

            case ColumnType.UInt32:
                stream.WriteUInt32BE(NumericValue.U32);
                break;

            case ColumnType.Int32:
                stream.WriteInt32BE(NumericValue.S32);
                break;

            case ColumnType.UInt64:
                stream.WriteUInt64BE(NumericValue.U64);
                break;

            case ColumnType.Int64:
                stream.WriteInt64BE(NumericValue.S64);
                break;

            case ColumnType.Single:
                stream.WriteSingleBE(NumericValue.R32);
                break;

            case ColumnType.Double:
                stream.WriteDoubleBE(NumericValue.R64);
                break;

            case ColumnType.String:
                stream.WriteUInt32BE(StringOffset);
                break;

            case ColumnType.Data:
                stream.WriteUInt32BE(DataOffset);

                // Tables ending locations, 4-byte alignment.
                if (IsTable)
                {
                    stream.WriteUInt32BE(AcbHelper.RoundUpToAlignment((uint)DataValue.Length, 4));
                }
                else
                {
                    stream.WriteUInt32BE((uint)DataValue.Length);
                }
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(Type));
            }
        }