private void CalculateMethodParameterSize() { // Check if already computed if (MethodData.ParameterStackSize != 0) { return; } int stacksize = 0; MethodData.ParameterSizes = new List <int>(Method.Signature.Parameters.Count); MethodData.ParameterOffsets = new List <int>(Method.Signature.Parameters.Count); if (Method.HasThis) { stacksize = TypeLayout.NativePointerSize; } foreach (var parameter in Method.Signature.Parameters) { var size = parameter.ParameterType.IsValueType ? TypeLayout.GetTypeSize(parameter.ParameterType) : TypeLayout.NativePointerAlignment; MethodData.ParameterSizes.Add(size); MethodData.ParameterOffsets.Add(stacksize); stacksize += Alignment.AlignUp(size, TypeLayout.NativePointerAlignment); } MethodData.ParameterStackSize = stacksize; }
private int LayoutParameters(int offsetOfFirst) { int offset = offsetOfFirst; foreach (var operand in Parameters) { int size, alignment; Architecture.GetTypeRequirements(TypeLayout, operand.Type, out size, out alignment); operand.Offset = offset; operand.IsResolved = true; //// adjust split children //if (operand.Low != null) //{ // operand.Low.Offset = offset + (operand.Low.Offset - operand.Offset); // operand.High.Offset = offset + (operand.High.Offset - operand.Offset); //} size = Alignment.AlignUp(size, alignment); offset = offset + size; } return(offset); }
/// <summary> /// Calculates the remaining space. /// </summary> /// <param name="compiler">The compiler.</param> /// <param name="typeLayout">The type layouts.</param> /// <param name="context">The context.</param> /// <param name="method">The method.</param> /// <param name="operands">The operand stack.</param> /// <param name="space">The space.</param> /// <param name="scratch">The scratch.</param> private void PushOperands(BaseMethodCompiler compiler, MosaTypeLayout typeLayout, Context context, MosaMethod method, List <Operand> operands, int space, Operand scratch) { Debug.Assert((method.Signature.Parameters.Count + (method.HasThis ? 1 : 0) == operands.Count) || (method.DeclaringType.IsDelegate && method.Signature.Parameters.Count == operands.Count)); int offset = method.Signature.Parameters.Count - operands.Count; for (int index = operands.Count - 1; index >= 0; index--) { Operand operand = operands[index]; MosaType param = (index + offset >= 0) ? method.Signature.Parameters[index + offset].ParameterType : null; int size, alignment; if (param != null && operand.IsR8 && param.IsR4) { architecture.GetTypeRequirements(typeLayout, param, out size, out alignment); } else { architecture.GetTypeRequirements(typeLayout, operand.Type, out size, out alignment); } size = Alignment.AlignUp(size, alignment); space -= size; Push(compiler, typeLayout, context, operand, space, size, scratch); } }
/// <summary> /// Requests the calling convention to create an appropriate move instruction to populate the return /// value of a method. /// </summary> /// <param name="compiler">The compiler.</param> /// <param name="context">The context.</param> /// <param name="operand">The operand, that's holding the return value.</param> public override void SetReturnValue(BaseMethodCompiler compiler, Context context, Operand operand) { int size, alignment; architecture.GetTypeRequirements(compiler.TypeLayout, operand.Type, out size, out alignment); size = Alignment.AlignUp(size, alignment); if (operand.IsR4) { architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(operand.Type, returnFloatingPointRegister), operand); } else if (operand.IsR8) { architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(operand.Type, returnFloatingPointRegister), operand); } else if (operand.IsLong) { MosaType highType = (operand.IsI8) ? compiler.TypeSystem.BuiltIn.I4 : compiler.TypeSystem.BuiltIn.U4; architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(compiler.TypeSystem.BuiltIn.U4, return32BitRegister), operand.Low); architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(highType, return64BitRegister), operand.High); } else if (compiler.StoreOnStack(operand.Type)) { int size2 = compiler.TypeLayout.GetTypeSize(operand.Type); var OffsetOfFirstParameterOperand = Operand.CreateConstant(compiler.TypeSystem, OffsetOfFirstParameter); architecture.InsertCompoundCopy(compiler, context, compiler.StackFrame, OffsetOfFirstParameterOperand, compiler.StackFrame, operand, size2); } else { architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(operand.Type, return32BitRegister), operand); } }
private static Result CreateSaveData(FileSystemClient fs, Func <Result> createFunc, ref long requiredSize, long baseSize, long dataSize, long journalSize) { Result rc = createFunc(); if (rc.IsSuccess()) { return(Result.Success); } if (ResultFs.UsableSpaceNotEnough.Includes(rc)) { Result queryRc = fs.QuerySaveDataTotalSize(out long totalSize, dataSize, journalSize); if (queryRc.IsFailure()) { return(queryRc); } requiredSize += Alignment.AlignUp(totalSize, 0x4000) + baseSize; } else if (!ResultFs.PathAlreadyExists.Includes(rc)) { return(rc); } return(Result.Success); }
/// <summary> /// Calculates the stack size for parameters. /// </summary> /// <param name="typeLayout">The type layouts.</param> /// <param name="operands">The operands.</param> /// <param name="method">The method.</param> /// <returns></returns> protected static int CalculateStackSizeForParameters(MosaTypeLayout typeLayout, BaseArchitecture architecture, List <Operand> operands, MosaMethod method) { Debug.Assert((method.Signature.Parameters.Count + (method.HasThis ? 1 : 0) == operands.Count) || (method.DeclaringType.IsDelegate && method.Signature.Parameters.Count == operands.Count), method.FullName); int offset = method.Signature.Parameters.Count - operands.Count; int result = 0; for (int index = operands.Count - 1; index >= 0; index--) { Operand operand = operands[index]; int size, alignment; architecture.GetTypeRequirements(typeLayout, operand.Type, out size, out alignment); var param = (index + offset >= 0) ? method.Signature.Parameters[index + offset] : null; if (param != null && operand.IsR8 && param.ParameterType.IsR4) { // adjust for parameter size on stack when method parameter is R4 while the calling variable is R8 architecture.GetTypeRequirements(typeLayout, param.ParameterType, out size, out alignment); } result = (int)Alignment.AlignUp(result, (uint)alignment) + size; } return(result); }
private void ResolveSectionLayout(LinkerSection section, uint fileOffset, ulong virtualAddress) { section.VirtualAddress = virtualAddress; section.FileOffset = fileOffset; section.Size = 0; foreach (var symbol in Symbols) { if (symbol.IsReplaced) { continue; } if (symbol.SectionKind != section.SectionKind) { continue; } if (symbol.IsResolved) { continue; } //if (symbol.IsExternalSymbol) // continue; symbol.SectionOffset = section.Size; symbol.VirtualAddress = section.VirtualAddress + section.Size; section.Size += symbol.Size; } section.Size = Alignment.AlignUp(section.Size, SectionAlignment); section.IsResolved = true; }
private void LayoutObjectsAndSections() { var virtualAddress = LinkerSettings.BaseAddress; // Sort the list --- helpful for debugging Symbols.Sort((y, x) => x.Name.CompareTo(y.Name)); if (FirstSymbol != null) { Symbols.Remove(FirstSymbol); Symbols.Insert(0, FirstSymbol); } foreach (var section in SectionKinds) { var size = ResolveSymbolLocation(section, virtualAddress); var linkerSection = Sections[(int)section]; linkerSection.VirtualAddress = virtualAddress; linkerSection.Size = size; size = Alignment.AlignUp(size, SectionAlignment); virtualAddress += size; } }
/// <summary> /// Creates a new <see cref="AesXtsFile"/> using the provided key. /// </summary> /// <param name="path">The full path of the file to create.</param> /// <param name="size">The initial size of the created file.</param> /// <param name="options">Flags to control how the file is created. /// Should usually be <see cref="CreateFileOptions.None"/></param> /// <param name="key">The 256-bit key containing a 128-bit data key followed by a 128-bit tweak key.</param> public Result CreateFile(U8Span path, long size, CreateFileOptions options, byte[] key) { long containerSize = AesXtsFile.HeaderLength + Alignment.AlignUp(size, 0x10); Result rc = BaseFileSystem.CreateFile(path, containerSize, options); if (rc.IsFailure()) { return(rc); } var header = new AesXtsFileHeader(key, size, path.ToString(), KekSource, ValidationKey); rc = BaseFileSystem.OpenFile(out IFile baseFile, path, OpenMode.Write); if (rc.IsFailure()) { return(rc); } using (baseFile) { rc = baseFile.Write(0, header.ToBytes(false)); if (rc.IsFailure()) { return(rc); } } return(Result.Success); }
private void WriteSection(BinaryWriter writer, Section section) { if (section.Type == SectionType.Null) { return; } section.Offset = Math.Max(BaseFileOffset, Alignment.AlignUp((uint)writer.BaseStream.Length, SectionAlignment)); if (section.Type == SectionType.NoBits) { return; } writer.SetPosition(section.Offset); if (section.Emitter != null) { section.Stream = section.Emitter(); } if (section.Stream != null) { section.Stream.Position = 0; section.Stream.WriteTo(writer.BaseStream); } section.Size = (uint)section.Stream.Length; }
public AesXtsFile(OpenMode mode, IFile baseFile, U8String path, ReadOnlySpan <byte> kekSeed, ReadOnlySpan <byte> verificationKey, int blockSize) { Mode = mode; BaseFile = baseFile; Path = path; KekSeed = kekSeed.ToArray(); VerificationKey = verificationKey.ToArray(); BlockSize = blockSize; Header = new AesXtsFileHeader(BaseFile); baseFile.GetSize(out long fileSize).ThrowIfFailure(); if (!Header.TryDecryptHeader(Path.ToString(), KekSeed, VerificationKey)) { ThrowHelper.ThrowResult(ResultFs.AesXtsFileHeaderInvalidKeys.Value, "NAX0 key derivation failed."); } if (HeaderLength + Alignment.AlignUp(Header.Size, 0x10) > fileSize) { ThrowHelper.ThrowResult(ResultFs.AesXtsFileTooShort.Value, "NAX0 key derivation failed."); } var fileStorage = new FileStorage2(baseFile); var encStorage = new SubStorage(fileStorage, HeaderLength, fileSize - HeaderLength); encStorage.SetResizable(true); BaseStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Header.DecryptedKey1, Header.DecryptedKey2, BlockSize, true), 4, true); }
/// <summary> /// Resolves the method parameters. /// </summary> /// <param name="method">The method.</param> private MethodInfo __ResolveMethodParameters(MosaMethod method) { if (method.HasOpenGenericParams) { return(null); } if (methodData.ContainsKey(method)) { return(null); } var parameters = method.Signature.Parameters; int stacksize = 0; var offsets = new List <int>(parameters.Count + (method.HasThis ? 1 : 0)); var sizes = new List <int>(parameters.Count + (method.HasThis ? 1 : 0)); if (method.HasThis) { offsets.Add(0); sizes.Add(NativePointerSize); stacksize = NativePointerSize; // already aligned } foreach (var parameter in parameters) { var size = parameter.ParameterType.IsValueType ? GetTypeSize(parameter.ParameterType) : NativePointerAlignment; offsets.Add(stacksize); sizes.Add(size); stacksize += Alignment.AlignUp(size, NativePointerAlignment); } var returnType = method.Signature.ReturnType; int returnSize = 0; if (!returnType.IsVoid) { ResolveType(returnType); typeSizes.TryGetValue(returnType, out returnSize); } var methodInfo = new MethodInfo { ReturnSize = returnSize, ParameterOffsets = offsets, ParameterSizes = sizes, ParameterStackSize = stacksize, ReturnInRegister = !returnType.IsVoid && FitsInRegister(returnType), HasThis = method.HasThis }; methodData.Add(method, methodInfo); return(methodInfo); }
/// <summary> /// Requests the calling convention to create an appropriate move instruction to populate the return /// value of a method. /// </summary> /// <param name="compiler">The compiler.</param> /// <param name="typeLayout">The type layouts.</param> /// <param name="context">The context.</param> /// <param name="operand">The operand, that's holding the return value.</param> public override void SetReturnValue(BaseMethodCompiler compiler, MosaTypeLayout typeLayout, Context context, Operand operand) { int size, alignment; architecture.GetTypeRequirements(typeLayout, operand.Type, out size, out alignment); size = Alignment.AlignUp(size, alignment); if (operand.IsR4) { architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(operand.Type, returnFloatingPointRegister), operand); } else if (operand.IsR8) { architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(operand.Type, returnFloatingPointRegister), operand); } else if (operand.IsLong) { MosaType highType = (operand.IsI8) ? typeLayout.TypeSystem.BuiltIn.I4 : typeLayout.TypeSystem.BuiltIn.U4; architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(typeLayout.TypeSystem.BuiltIn.U4, return32BitRegister), operand.Low); architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(highType, return64BitRegister), operand.High); } else if (size == 4 || size == 2 || size == 1) { architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(operand.Type, return32BitRegister), operand); } else if (typeLayout.IsCompoundType(operand.Type)) { Operand stackBaseReg = Operand.CreateCPURegister(typeLayout.TypeSystem.BuiltIn.Pointer, architecture.StackFrameRegister); architecture.InsertCompoundMoveInstruction(compiler, context, Operand.CreateMemoryAddress(operand.Type, stackBaseReg, OffsetOfFirstParameter), operand, size); } }
private void Process(Request request) { lock (sync) { if (request.Blocks.Count != 0) { return; } requests.Remove(request); var data = new byte[request.Size]; // blocks start/end ulong start = Alignment.AlignDown(request.Address, BlockSize); ulong end = Alignment.AlignUp(request.Address + (ulong)request.Size, BlockSize); ulong requestStart = request.Address; ulong requestEnd = request.Address + request.Size; lock (sync) { for (ulong blockStart = start; blockStart < end; blockStart += BlockSize) { ulong blockEnd = blockStart + BlockSize; int blockOffset = 0; int dataOffset = 0; ulong overlapStart = Math.Max(requestStart, blockStart); ulong overlapEnd = Math.Min(requestEnd, blockEnd); int len = (int)(overlapEnd - overlapStart); if (requestStart > blockStart) { blockOffset = (int)(requestStart - blockStart); } if (blockStart > requestStart) { dataOffset = (int)(blockStart - requestStart); } var block = buffer[blockStart]; try { Array.Copy(block, blockOffset, data, dataOffset, len); } catch (Exception e) { Debug.WriteLine(e.ToString()); } } } request.OnMemoryRead(request.Address, data); } }
private bool VerifyPk11Sizes() { Assert.True(IsDecrypted); int pk11Size = Unsafe.SizeOf <Package1Pk11Header>() + GetSectionSize(Package1Section.WarmBoot) + GetSectionSize(Package1Section.Bootloader) + GetSectionSize(Package1Section.SecureMonitor); pk11Size = Alignment.AlignUp(pk11Size, 0x10); return(pk11Size == Pk11Size); }
/// <summary> /// Performs a stack layout of all local variables in the list. /// </summary> /// <param name="locals">The enumerable holding all locals.</param> /// <param name="callingConvention">The cc.</param> /// <param name="offsetOfFirst">Specifies the offset of the first stack operand in the list.</param> /// <param name="isLocalVariable">The direction.</param> /// <returns></returns> private int LayoutVariables(IList <Operand> locals, BaseCallingConvention callingConvention, int offsetOfFirst, bool isLocalVariable) { int offset = offsetOfFirst; foreach (var operand in locals) { if (!operand.IsParameter && operand.Uses.Count == 0 && operand.Definitions.Count == 0) { bool skip = false; if (operand.Low == null && operand.High == null) { skip = true; } else if (operand.Low.Uses.Count == 0 && operand.Low.Definitions.Count == 0 && operand.High.Uses.Count == 0 && operand.High.Definitions.Count == 0) { skip = true; } if (skip) { operand.Displacement = 0; continue; } } int size, alignment; Architecture.GetTypeRequirements(TypeLayout, operand.Type, out size, out alignment); if (isLocalVariable) { size = Alignment.AlignUp(size, alignment); offset = offset - size; } // adjust split children if (operand.Low != null) { operand.Low.Displacement = offset + (operand.Low.Displacement - operand.Displacement); operand.High.Displacement = offset + (operand.High.Displacement - operand.Displacement); } operand.Displacement = offset; if (!isLocalVariable) { size = Alignment.AlignUp(size, alignment); offset = offset + size; } } return(offset); }
private void RegisterStandardSections() { var previous = nullSection; foreach (var linkerSection in Linker.Sections) { if (linkerSection.Size == 0 && linkerSection.SectionKind != SectionKind.BSS) { continue; } var section = new Section() { Name = SectionNames[(int)linkerSection.SectionKind], Address = linkerSection.VirtualAddress, Size = Alignment.AlignUp(linkerSection.Size, SectionAlignment), Emitter = () => { return(WriteLinkerSection(linkerSection.SectionKind)); }, SectionKind = linkerSection.SectionKind }; switch (linkerSection.SectionKind) { case SectionKind.Text: section.Type = SectionType.ProgBits; section.Flags = SectionAttribute.AllocExecute; break; case SectionKind.Data: section.Type = SectionType.ProgBits; section.Flags = SectionAttribute.Alloc | SectionAttribute.Write; break; case SectionKind.ROData: section.Type = SectionType.ProgBits; section.Flags = SectionAttribute.Alloc; break; case SectionKind.BSS: section.Type = SectionType.NoBits; section.Flags = SectionAttribute.Alloc | SectionAttribute.Write; break; } section.AddDependency(previous); RegisterSection(section); previous = section; } }
public void FsTrim() { int virtualBlockCount = Header.MainDataBlockCount; int physicalBlockCount = virtualBlockCount + Header.JournalBlockCount; int blockMapLength = virtualBlockCount * MapEntryLength; int physicalBitmapLength = Alignment.AlignUp(physicalBlockCount, 32) / 8; int virtualBitmapLength = Alignment.AlignUp(virtualBlockCount, 32) / 8; MapStorage.Slice(blockMapLength).Fill(SaveDataFileSystem.TrimFillValue); FreeBlocks.Slice(physicalBitmapLength).Fill(SaveDataFileSystem.TrimFillValue); ModifiedPhysicalBlocks.Slice(physicalBitmapLength).Fill(SaveDataFileSystem.TrimFillValue); ModifiedVirtualBlocks.Slice(virtualBitmapLength).Fill(SaveDataFileSystem.TrimFillValue); }
protected static int CalculateStackSizeForParameters(MosaTypeLayout typeLayout, BaseArchitecture architecture, List <Operand> operands) { // first operand is the call location int result = 0; foreach (var operand in operands) { int size, alignment; architecture.GetTypeRequirements(typeLayout, operand.Type, out size, out alignment); result = Alignment.AlignUp(result, alignment) + size; } return(result); }
/// <summary> /// Calculates the remaining space. /// </summary> /// <param name="compiler">The compiler.</param> /// <param name="typeLayout">The type layouts.</param> /// <param name="context">The context.</param> /// <param name="operands">The operand stack.</param> /// <param name="space">The space.</param> /// <param name="scratch">The scratch.</param> private void PushOperands(BaseMethodCompiler compiler, MosaTypeLayout typeLayout, Context context, List <Operand> operands, int space, Operand scratch) { foreach (var operand in operands) { int size, alignment; architecture.GetTypeRequirements(typeLayout, operand.Type, out size, out alignment); size = Alignment.AlignUp(size, alignment); space -= size; Push(compiler, typeLayout, context, operand, space, size, scratch); } }
private int CalculateReturnSize(Operand result) { if (result == null) { return(0); } var returnType = result.Type; if (MosaTypeLayout.IsStoredOnStack(returnType)) { return(Alignment.AlignUp(TypeLayout.GetTypeSize(returnType), NativeAlignment)); } return(0); }
private int CalculateReturnSize(Operand result) { if (result == null) { return(0); } var returnType = result.Type; if (!MosaTypeLayout.CanFitInRegister(returnType)) { return(Alignment.AlignUp(TypeLayout.GetTypeSize(returnType), NativeAlignment)); } return(0); }
private void CalculateMethodParameterSize() { int stacksize = 0; if (Method.HasThis) { stacksize = TypeLayout.NativePointerSize; } foreach (var parameter in Method.Signature.Parameters) { var size = parameter.ParameterType.IsValueType ? TypeLayout.GetTypeSize(parameter.ParameterType) : TypeLayout.NativePointerAlignment; stacksize += Alignment.AlignUp(size, TypeLayout.NativePointerAlignment); } MethodData.ParameterStackSize = stacksize; }
/// <summary> /// Performs a stack layout of all local variables in the list. /// </summary> /// <param name="locals">The enumerable holding all locals.</param> /// <param name="callingConvention">The cc.</param> /// <param name="offsetOfFirst">The offset of first.</param> /// <returns></returns> private int LayoutVariables(IList <Operand> locals, BaseCallingConvention callingConvention, int offsetOfFirst) { int offset = offsetOfFirst; foreach (var operand in locals) { int size, alignment; Architecture.GetTypeRequirements(TypeLayout, operand.Type, out size, out alignment); size = Alignment.AlignUp(size, alignment); offset = offset - size; operand.Offset = offset; operand.IsResolved = true; } return(offset); }
/// <summary> /// Gets the size of the reference or type. /// </summary> /// <param name="type">The type.</param> /// <param name="aligned">if set to <c>true</c> [aligned].</param> /// <returns></returns> public int GetReferenceOrTypeSize(MosaType type, bool aligned) { if (type.IsValueType) { if (aligned) { return(Alignment.AlignUp(TypeLayout.GetTypeSize(type), Architecture.NativeAlignment)); } else { return(TypeLayout.GetTypeSize(type)); } } else { return(Architecture.NativeAlignment); } }
private void LayoutObjectsAndSections() { var virtualAddress = LinkerSettings.BaseAddress; foreach (var section in SectionKinds) { var size = ResolveSymbolLocation(section, virtualAddress); var linkerSection = Sections[(int)section]; linkerSection.VirtualAddress = virtualAddress; linkerSection.Size = size; size = Alignment.AlignUp(size, SectionAlignment); virtualAddress += size; } }
private void WriteProgramHeader(BinaryWriter writer) { elfheader.ProgramHeaderOffset = ElfHeader.GetEntrySize(LinkerFormatType); writer.SetPosition(elfheader.ProgramHeaderOffset); elfheader.ProgramHeaderNumber = 0; foreach (var section in sections) { if (section.SectionKind == SectionKind.Unknown) { continue; } if (section.Size == 0 && section.SectionKind != SectionKind.BSS) { continue; } if (section.Address == 0) { continue; } var programHeader = new ProgramHeader { Alignment = SectionAlignment, FileSize = Alignment.AlignUp(section.Size, SectionAlignment), MemorySize = Alignment.AlignUp(section.Size, SectionAlignment), Offset = section.Offset, VirtualAddress = section.Address, PhysicalAddress = section.Address, Type = ProgramHeaderType.Load, Flags = (section.SectionKind == SectionKind.Text) ? ProgramHeaderFlags.Read | ProgramHeaderFlags.Execute : (section.SectionKind == SectionKind.ROData) ? ProgramHeaderFlags.Read : ProgramHeaderFlags.Read | ProgramHeaderFlags.Write }; programHeader.Write(LinkerFormatType, writer); elfheader.ProgramHeaderNumber++; } }
private void ResolveSectionOffset(Section section) { if (section.Type == SectionType.NoBits || section.Type == SectionType.Null) { return; } if (section.Offset != 0) { return; } uint max = 0; foreach (var sec in sections) { max = Math.Max(max, sec.Offset + sec.Size); } section.Offset = Alignment.AlignUp(max, linker.SectionAlignment); }
internal void ResolveLayout(uint fileOffset, ulong virtualAddress) { VirtualAddress = virtualAddress; FileOffset = fileOffset; foreach (var symbol in Symbols) { if (symbol.IsResolved) { continue; } Size = Alignment.AlignUp(Size, symbol.Alignment); symbol.SectionOffset = Size; symbol.VirtualAddress = VirtualAddress + Size; Size = Size + symbol.Size; } IsResolved = true; }
/// <summary> /// Adds a file to the RomFS. /// </summary> /// <param name="path">The full path in the RomFS</param> /// <param name="file">An <see cref="IFile"/> of the file data to add.</param> public void AddFile(string path, IFile file) { var fileInfo = new RomFileInfo(); file.GetSize(out long fileSize).ThrowIfFailure(); fileInfo.Offset = CurrentOffset; fileInfo.Length = fileSize; IStorage fileStorage = file.AsStorage(); Sources.Add(fileStorage); long newOffset = CurrentOffset + fileSize; CurrentOffset = Alignment.AlignUp(newOffset, FileAlignment); var padding = new NullStorage(CurrentOffset - newOffset); Sources.Add(padding); FileTable.AddFile(path, ref fileInfo); }