Move all files into project subdirectory

This commit is contained in:
2023-12-19 13:18:14 +00:00
parent 934b86c79f
commit 22fc553971
25 changed files with 0 additions and 0 deletions

1342
Aaru.Helpers/.editorconfig Normal file

File diff suppressed because it is too large Load Diff

595
Aaru.Helpers/.gitignore vendored Normal file
View File

@@ -0,0 +1,595 @@
### VisualStudio template
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
### Linux template
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### Xcode template
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
xcuserdata/
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
### VisualStudioCode template
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
### C++ template
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
# Precompiled Headers
*.gch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
### MonoDevelop template
#User Specific
*.usertasks
#Mono Project Files
*.resources
test-results/
### GPG template
secring.*
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
### CMake template
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
### C template
# Object files
*.ko
*.elf
# Linker output
*.map
*.exp
*.so.*
# Executables
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
### Windows template
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# NuGet Packages Directory
packages/
## TODO: If the tool you use requires repositories.config uncomment the next line
#!packages/repositories.config
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
# This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented)
!packages/build/
# Others
sql/
*.Cache
# Visual Studio 2017
.vs
workspace.xml
cmake-build-debug
### macOS template
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
pkg/**/pkg
pkg/**/src
pkg/**/*.asc
pkg/**/*.sig
pkg/**/*.tar.xz
pkg/**/*.zip
pkg/**/aaru
.sonarqube

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{F8BDF57B-1571-4CD0-84B3-B422088D359A}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>Aaru.Helpers</RootNamespace>
<AssemblyName>Aaru.Helpers</AssemblyName>
<ReleaseVersion>$(Version)</ReleaseVersion>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<Version>6.0.0-alpha9</Version>
<Company>Claunia.com</Company>
<Copyright>Copyright © 2011-2023 Natalia Portillo</Copyright>
<Product>Aaru Data Preservation Suite</Product>
<Title>Aaru.Helpers</Title>
<ApplicationVersion>$(Version)</ApplicationVersion>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>12</LangVersion>
<Description>Contains helpers used by the Aaru Data Preservation Suite.</Description>
<PackageProjectUrl>https://github.com/aaru-dps/</PackageProjectUrl>
<PackageLicenseExpression>LGPL-2.1-only</PackageLicenseExpression>
<RepositoryUrl>https://github.com/aaru-dps/Aaru.Helpers</RepositoryUrl>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<NeutralLanguage>en-US</NeutralLanguage>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Authors>Natalia Portillo &lt;claunia@claunia.com&gt;</Authors>
<DisableImplicitNamespaceImports>true</DisableImplicitNamespaceImports>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<NoWarn>CS1591;CS1574</NoWarn>
</PropertyGroup>
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<InternalsVisibleTo Include="Aaru.Tests"/>
<InternalsVisibleTo Include="Aaru.Tests.Devices"/>
</ItemGroup>
<PropertyGroup>
<NrtRevisionFormat>$(Version)+{chash:8}</NrtRevisionFormat>
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
<NrtShowRevision>true</NrtShowRevision>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Aaru.Console\Aaru.Console.csproj"/>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\LICENSE.LGPL">
<Link>LICENSE.LGPL</Link>
</EmbeddedResource>
<EmbeddedResource Update="Localization\Localization.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Localization.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.5"/>
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3" PrivateAssets="all"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,5 @@
<wpf:ResourceDictionary xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xml:space="preserve">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=localization/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

79
Aaru.Helpers/ArrayFill.cs Normal file
View File

@@ -0,0 +1,79 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : ArrayFill.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Helpers.
//
// --[ Description ] ----------------------------------------------------------
//
// Fills an array with a specified value.
//
// --[ License ] --------------------------------------------------------------
//
// No license specified by creator.
//
// Published on https://github.com/mykohsu/Extensions/blob/master/ArrayExtensions.cs
//
// Assuming open source.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// Copyright(C) 2014 mykohsu
// ****************************************************************************/
using System;
using System.Text;
namespace Aaru.Helpers;
public static partial class ArrayHelpers
{
/// <summary>Fills an array with the specified value</summary>
/// <param name="destinationArray">Array</param>
/// <param name="value">Value</param>
/// <typeparam name="T">Array type</typeparam>
public static void ArrayFill<T>(T[] destinationArray, T value) => ArrayFill(destinationArray, new[]
{
value
});
/// <summary>Fills an array with the contents of the specified array</summary>
/// <param name="destinationArray">Array</param>
/// <param name="value">Value</param>
/// <typeparam name="T">Array type</typeparam>
public static void ArrayFill<T>(T[] destinationArray, T[] value)
{
ArgumentNullException.ThrowIfNull(destinationArray);
if(value.Length > destinationArray.Length)
throw new ArgumentException(Localization.Length_of_value_array_must_not_be_more_than_length_of_destination);
// set the initial array value
Array.Copy(value, destinationArray, value.Length);
int arrayToFillHalfLength = destinationArray.Length / 2;
int copyLength;
for(copyLength = value.Length; copyLength < arrayToFillHalfLength; copyLength <<= 1)
Array.Copy(destinationArray, 0, destinationArray, copyLength, copyLength);
Array.Copy(destinationArray, 0, destinationArray, copyLength, destinationArray.Length - copyLength);
}
/// <summary>Converts a byte array to its hexadecimal representation</summary>
/// <param name="array">Byte array</param>
/// <param name="upper"><c>true</c> to use uppercase</param>
/// <returns></returns>
public static string ByteArrayToHex(byte[] array, bool upper = false)
{
var sb = new StringBuilder();
for(long i = 0; i < array.LongLength; i++)
sb.Append($"{array[i]:x2}");
return upper ? sb.ToString().ToUpper() : sb.ToString();
}
}

View File

@@ -0,0 +1,49 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : ArrayIsEmpty.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Helpers.
//
// --[ Description ] ----------------------------------------------------------
//
// Methods for detecting an empty array.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
using System.Linq;
namespace Aaru.Helpers;
/// <summary>Helper operations to work with arrays</summary>
public static partial class ArrayHelpers
{
/// <summary>Checks if an array is null, filled with the NULL byte (0x00) or ASCII whitespace (0x20)</summary>
/// <param name="array">Array</param>
/// <returns>True if null or whitespace</returns>
public static bool ArrayIsNullOrWhiteSpace(byte[] array) => array?.All(b => b is 0x00 or 0x20) != false;
/// <summary>Checks if an array is null or filled with the NULL byte (0x00)</summary>
/// <param name="array">Array</param>
/// <returns>True if null</returns>
public static bool ArrayIsNullOrEmpty(byte[] array) => array?.All(b => b == 0x00) != false;
}

View File

@@ -0,0 +1,323 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : BigEndianBitConverter.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Helpers.
//
// --[ Description ] ----------------------------------------------------------
//
// Override of System.BitConverter that knows how to handle big-endian.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Aaru.Helpers;
/// <summary>
/// Converts base data types to an array of bytes, and an array of bytes to base data types. All info taken from
/// the meta data of System.BitConverter. This implementation allows for Endianness consideration.
/// </summary>
[SuppressMessage("ReSharper", "UnusedParameter.Global")]
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static class BigEndianBitConverter
{
/// <summary>Converts the specified double-precision floating point number to a 64-bit signed integer.</summary>
/// <param name="value">The number to convert.</param>
/// <returns>A 64-bit signed integer whose value is equivalent to value.</returns>
/// <exception cref="NotImplementedException">It is not currently implemented</exception>
public static long DoubleToInt64Bits(double value) => throw new NotImplementedException();
/// <summary>Returns the specified Boolean value as an array of bytes.</summary>
/// <param name="value">A Boolean value.</param>
/// <returns>An array of bytes with length 1.</returns>
public static byte[] GetBytes(bool value) => BitConverter.GetBytes(value).Reverse().ToArray();
/// <summary>Returns the specified Unicode character value as an array of bytes.</summary>
/// <param name="value">A character to convert.</param>
/// <returns>An array of bytes with length 2.</returns>
public static byte[] GetBytes(char value) => BitConverter.GetBytes(value).Reverse().ToArray();
/// <summary>Returns the specified double-precision floating point value as an array of bytes.</summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 8.</returns>
public static byte[] GetBytes(double value) => BitConverter.GetBytes(value).Reverse().ToArray();
/// <summary>Returns the specified single-precision floating point value as an array of bytes.</summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 4.</returns>
public static byte[] GetBytes(float value) => BitConverter.GetBytes(value).Reverse().ToArray();
/// <summary>Returns the specified 32-bit signed integer value as an array of bytes.</summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 4.</returns>
public static byte[] GetBytes(int value) => BitConverter.GetBytes(value).Reverse().ToArray();
/// <summary>Returns the specified 64-bit signed integer value as an array of bytes.</summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 8.</returns>
public static byte[] GetBytes(long value) => BitConverter.GetBytes(value).Reverse().ToArray();
/// <summary>Returns the specified 16-bit signed integer value as an array of bytes.</summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 2.</returns>
public static byte[] GetBytes(short value) => BitConverter.GetBytes(value).Reverse().ToArray();
/// <summary>Returns the specified 32-bit unsigned integer value as an array of bytes.</summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 4.</returns>
public static byte[] GetBytes(uint value) => BitConverter.GetBytes(value).Reverse().ToArray();
/// <summary>Returns the specified 64-bit unsigned integer value as an array of bytes.</summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 8.</returns>
public static byte[] GetBytes(ulong value) => BitConverter.GetBytes(value).Reverse().ToArray();
/// <summary>Returns the specified 16-bit unsigned integer value as an array of bytes.</summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 2.</returns>
public static byte[] GetBytes(ushort value) => BitConverter.GetBytes(value).Reverse().ToArray();
/// <summary>Converts the specified 64-bit signed integer to a double-precision floating point number.</summary>
/// <param name="value">The number to convert.</param>
/// <returns>A double-precision floating point number whose value is equivalent to value.</returns>
public static double Int64BitsToDouble(long value) => throw new NotImplementedException();
/// <summary>Returns a Boolean value converted from one byte at a specified position in a byte array.</summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>true if the byte at <see cref="startIndex" /> in value is nonzero; otherwise, false.</returns>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// <see cref="startIndex" /> is less than zero or greater than the
/// length of value minus 1.
/// </exception>
public static bool ToBoolean(byte[] value, int startIndex) => throw new NotImplementedException();
/// <summary>Returns a Unicode character converted from two bytes at a specified position in a byte array.</summary>
/// <param name="value">An array.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A character formed by two bytes beginning at <see cref="startIndex" />.</returns>
/// <exception cref="System.ArgumentException"><see cref="startIndex" /> equals the length of value minus 1.</exception>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// <see cref="startIndex" /> is less than zero or greater than the
/// length of value minus 1.
/// </exception>
public static char ToChar(byte[] value, int startIndex) => throw new NotImplementedException();
/// <summary>
/// Returns a double-precision floating point number converted from eight bytes at a specified position in a byte
/// array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A double precision floating point number formed by eight bytes beginning at <see cref="startIndex" />.</returns>
/// <exception cref="System.ArgumentException">
/// <see cref="startIndex" /> is greater than or equal to the length of value
/// minus 7, and is less than or equal to the length of value minus 1.
/// </exception>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// <see cref="startIndex" /> is less than zero or greater than the
/// length of value minus 1.
/// </exception>
public static double ToDouble(byte[] value, int startIndex) => throw new NotImplementedException();
/// <summary>Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array.</summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 16-bit signed integer formed by two bytes beginning at <see cref="startIndex" />.</returns>
/// <exception cref="System.ArgumentException"><see cref="startIndex" /> equals the length of value minus 1.</exception>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// startIndex is less than zero or greater than the length of value
/// minus 1.
/// </exception>
public static short ToInt16(byte[] value, int startIndex) =>
BitConverter.ToInt16(value.Reverse().ToArray(), value.Length - sizeof(short) - startIndex);
/// <summary>Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array.</summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 32-bit signed integer formed by four bytes beginning at <see cref="startIndex" />.</returns>
/// <exception cref="System.ArgumentException">
/// <see cref="startIndex" /> is greater than or equal to the length of value
/// minus 3, and is less than or equal to the length of value minus 1.
/// </exception>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// startIndex is less than zero or greater than the length of value
/// minus 1.
/// </exception>
public static int ToInt32(byte[] value, int startIndex) =>
BitConverter.ToInt32(value.Reverse().ToArray(), value.Length - sizeof(int) - startIndex);
/// <summary>Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array.</summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 64-bit signed integer formed by eight bytes beginning at <see cref="startIndex" />.</returns>
/// <exception cref="System.ArgumentException">
/// <see cref="startIndex" /> is greater than or equal to the length of value
/// minus 7, and is less than or equal to the length of value minus 1.
/// </exception>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// <see cref="startIndex" /> is less than zero or greater than the
/// length of value minus 1.
/// </exception>
public static long ToInt64(byte[] value, int startIndex) =>
BitConverter.ToInt64(value.Reverse().ToArray(), value.Length - sizeof(long) - startIndex);
/// <summary>
/// Returns a single-precision floating point number converted from four bytes at a specified position in a byte
/// array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A single-precision floating point number formed by four bytes beginning at <see cref="startIndex" />.</returns>
/// <exception cref="System.ArgumentException">
/// <see cref="startIndex" /> is greater than or equal to the length of value
/// minus 3, and is less than or equal to the length of value minus 1.
/// </exception>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// <see cref="startIndex" /> is less than zero or greater than the
/// length of value minus 1.
/// </exception>
public static float ToSingle(byte[] value, int startIndex) =>
BitConverter.ToSingle(value.Reverse().ToArray(), value.Length - sizeof(float) - startIndex);
/// <summary>
/// Converts the numeric value of each element of a specified array of bytes to its equivalent hexadecimal string
/// representation.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <returns>
/// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding
/// element in value; for example, "7F-2C-4A".
/// </returns>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
public static string ToString(byte[] value) => BitConverter.ToString(value.Reverse().ToArray());
/// <summary>
/// Converts the numeric value of each element of a specified subarray of bytes to its equivalent hexadecimal
/// string representation.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>
/// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding
/// element in a subarray of value; for example, "7F-2C-4A".
/// </returns>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// startIndex is less than zero or greater than the length of value
/// minus 1.
/// </exception>
public static string ToString(byte[] value, int startIndex) =>
BitConverter.ToString(value.Reverse().ToArray(), startIndex);
/// <summary>
/// Converts the numeric value of each element of a specified subarray of bytes to its equivalent hexadecimal
/// string representation.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <param name="length">The number of array elements in value to convert.</param>
/// <returns>
/// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding
/// element in a subarray of value; for example, "7F-2C-4A".
/// </returns>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// startIndex or length is less than zero. -or- startIndex is greater
/// than zero and is greater than or equal to the length of value.
/// </exception>
/// <exception cref="System.ArgumentException">
/// The combination of startIndex and length does not specify a position within
/// value; that is, the startIndex parameter is greater than the length of value minus the length parameter.
/// </exception>
public static string ToString(byte[] value, int startIndex, int length) =>
BitConverter.ToString(value.Reverse().ToArray(), startIndex, length);
/// <summary>Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array.</summary>
/// <param name="value">The array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 16-bit unsigned integer formed by two bytes beginning at startIndex.</returns>
/// <exception cref="System.ArgumentException">startIndex equals the length of value minus 1.</exception>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// startIndex is less than zero or greater than the length of value
/// minus 1.
/// </exception>
public static ushort ToUInt16(byte[] value, int startIndex) =>
BitConverter.ToUInt16(value.Reverse().ToArray(), value.Length - sizeof(ushort) - startIndex);
/// <summary>Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array.</summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 32-bit unsigned integer formed by four bytes beginning at startIndex.</returns>
/// <exception cref="System.ArgumentException">
/// startIndex is greater than or equal to the length of value minus 3, and is
/// less than or equal to the length of value minus 1.
/// </exception>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// startIndex is less than zero or greater than the length of value
/// minus 1.
/// </exception>
public static uint ToUInt32(byte[] value, int startIndex) =>
BitConverter.ToUInt32(value.Reverse().ToArray(), value.Length - sizeof(uint) - startIndex);
/// <summary>Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array.</summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 64-bit unsigned integer formed by the eight bytes beginning at startIndex.</returns>
/// <exception cref="System.ArgumentException">
/// startIndex is greater than or equal to the length of value minus 7, and is
/// less than or equal to the length of value minus 1.
/// </exception>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// startIndex is less than zero or greater than the length of value
/// minus 1.
/// </exception>
public static ulong ToUInt64(byte[] value, int startIndex) =>
BitConverter.ToUInt64(value.Reverse().ToArray(), value.Length - sizeof(ulong) - startIndex);
/// <summary>Converts a big endian byte array representation of a GUID into the .NET Guid structure</summary>
/// <param name="value">Byte array containing a GUID in big endian</param>
/// <param name="startIndex">Start of the byte array to process</param>
/// <returns>Processed Guid</returns>
public static Guid ToGuid(byte[] value, int startIndex) => new(ToUInt32(value, 0 + startIndex),
ToUInt16(value, 4 + startIndex),
ToUInt16(value, 6 + startIndex),
value[8 + startIndex + 0], value[8 + startIndex + 1],
value[8 + startIndex + 2], value[8 + startIndex + 3],
value[8 + startIndex + 5], value[8 + startIndex + 5],
value[8 + startIndex + 6],
value[8 + startIndex + 7]);
}

50
Aaru.Helpers/BitEndian.cs Normal file
View File

@@ -0,0 +1,50 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : BitEndian.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Common types.
//
// --[ Description ] ----------------------------------------------------------
//
// Defines enumerations of bit endianness.
//
// --[ License ] --------------------------------------------------------------
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
namespace Aaru.Helpers;
/// <summary>Describes the endianness of bits on a data structure</summary>
public enum BitEndian
{
/// <summary>Little-endian, or least significant bit</summary>
Little,
/// <summary>Big-endian, or most significant bit</summary>
Big,
/// <summary>PDP-11 endian, little endian except for 32-bit integers where the 16 halves are swapped between them</summary>
Pdp
}

49
Aaru.Helpers/CHS.cs Normal file
View File

@@ -0,0 +1,49 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : CHS.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Helpers.
//
// --[ Description ] ----------------------------------------------------------
//
// Helpers for CHS<->LBA conversions
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
namespace Aaru.Helpers;
/// <summary>Helper operations to work with CHS values</summary>
public static class CHS
{
/// <summary>Converts a CHS position to a LBA one</summary>
/// <param name="cyl">Cylinder</param>
/// <param name="head">Head</param>
/// <param name="sector">Sector</param>
/// <param name="maxHead">Number of heads</param>
/// <param name="maxSector">Number of sectors per track</param>
/// <returns></returns>
public static uint ToLBA(uint cyl, uint head, uint sector, uint maxHead, uint maxSector) =>
maxHead == 0 || maxSector == 0
? (cyl * 16 + head) * 63 + sector - 1
: (cyl * maxHead + head) * maxSector + sector - 1;
}

View File

@@ -0,0 +1,72 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : CompareBytes.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Helpers.
//
// --[ Description ] ----------------------------------------------------------
//
// Compares two byte arrays.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
namespace Aaru.Helpers;
public static partial class ArrayHelpers
{
/// <summary>Compares two byte arrays</summary>
/// <param name="different"><c>true</c> if they are different in any way</param>
/// <param name="sameSize"><c>true</c> if they have the same size</param>
/// <param name="compareArray1">Left array</param>
/// <param name="compareArray2">Right array</param>
public static void CompareBytes(out bool different, out bool sameSize, byte[] compareArray1, byte[] compareArray2)
{
different = false;
sameSize = true;
long leastBytes;
if(compareArray1.LongLength < compareArray2.LongLength)
{
sameSize = false;
leastBytes = compareArray1.LongLength;
}
else if(compareArray1.LongLength > compareArray2.LongLength)
{
sameSize = false;
leastBytes = compareArray2.LongLength;
}
else
leastBytes = compareArray1.LongLength;
for(long i = 0; i < leastBytes; i++)
{
if(compareArray1[i] == compareArray2[i])
continue;
different = true;
return;
}
}
}

48
Aaru.Helpers/CountBits.cs Normal file
View File

@@ -0,0 +1,48 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : CountBits.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Helpers.
//
// --[ Description ] ----------------------------------------------------------
//
// Counts bits in a number.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
namespace Aaru.Helpers;
/// <summary>Helper operations to count bits</summary>
public static class CountBits
{
/// <summary>Counts the number of bits set to <c>true</c> in a number</summary>
/// <param name="number">Number</param>
/// <returns>Bits set to <c>true</c></returns>
public static int Count(uint number)
{
number -= number >> 1 & 0x55555555;
number = (number & 0x33333333) + (number >> 2 & 0x33333333);
return (int)((number + (number >> 4) & 0x0F0F0F0F) * 0x01010101 >> 24);
}
}

View File

@@ -0,0 +1,386 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : DateHandlers.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Helpers.
//
// --[ Description ] ----------------------------------------------------------
//
// Convert several timestamp formats to C# DateTime.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
using System;
using System.Text;
using Aaru.Console;
namespace Aaru.Helpers;
/// <summary>Helper operations for timestamp management (date and time)</summary>
public static class DateHandlers
{
const string ISO9660_MODULE_NAME = "ISO9600ToDateTime handler";
const string PASCAL_MODULE_NAME = "UCSDPascalToDateTime handler";
const string DOS_MODULE_NAME = "DOSToDateTime handler";
static readonly DateTime _lisaEpoch = new(1901, 1, 1, 0, 0, 0);
static readonly DateTime _macEpoch = new(1904, 1, 1, 0, 0, 0);
static readonly DateTime _unixEpoch = new(1970, 1, 1, 0, 0, 0);
/// <summary>Day 0 of Julian Date system</summary>
static readonly DateTime _julianEpoch = new(1858, 11, 17, 0, 0, 0);
static readonly DateTime _amigaEpoch = new(1978, 1, 1, 0, 0, 0);
/// <summary>Converts a Macintosh timestamp to a .NET DateTime</summary>
/// <param name="macTimeStamp">Macintosh timestamp (seconds since 1st Jan. 1904)</param>
/// <returns>.NET DateTime</returns>
public static DateTime MacToDateTime(ulong macTimeStamp) => _macEpoch.AddTicks((long)(macTimeStamp * 10000000));
/// <summary>Converts a Lisa timestamp to a .NET DateTime</summary>
/// <param name="lisaTimeStamp">Lisa timestamp (seconds since 1st Jan. 1901)</param>
/// <returns>.NET DateTime</returns>
public static DateTime LisaToDateTime(uint lisaTimeStamp) => _lisaEpoch.AddSeconds(lisaTimeStamp);
/// <summary>Converts a UNIX timestamp to a .NET DateTime</summary>
/// <param name="unixTimeStamp">UNIX timestamp (seconds since 1st Jan. 1970)</param>
/// <returns>.NET DateTime</returns>
public static DateTime UnixToDateTime(int unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp);
/// <summary>Converts a UNIX timestamp to a .NET DateTime</summary>
/// <param name="unixTimeStamp">UNIX timestamp (seconds since 1st Jan. 1970)</param>
/// <returns>.NET DateTime</returns>
public static DateTime UnixToDateTime(long unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp);
/// <summary>Converts a UNIX timestamp to a .NET DateTime</summary>
/// <param name="unixTimeStamp">UNIX timestamp (seconds since 1st Jan. 1970)</param>
/// <returns>.NET DateTime</returns>
public static DateTime UnixUnsignedToDateTime(uint unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp);
/// <summary>Converts a UNIX timestamp to a .NET DateTime</summary>
/// <param name="seconds">Seconds since 1st Jan. 1970)</param>
/// <param name="nanoseconds">Nanoseconds</param>
/// <returns>.NET DateTime</returns>
public static DateTime UnixUnsignedToDateTime(uint seconds, uint nanoseconds) =>
_unixEpoch.AddSeconds(seconds).AddTicks((long)nanoseconds / 100);
/// <summary>Converts a UNIX timestamp to a .NET DateTime</summary>
/// <param name="unixTimeStamp">UNIX timestamp (seconds since 1st Jan. 1970)</param>
/// <returns>.NET DateTime</returns>
public static DateTime UnixUnsignedToDateTime(ulong unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp);
/// <summary>Converts a High Sierra Format timestamp to a .NET DateTime</summary>
/// <param name="vdDateTime">High Sierra Format timestamp</param>
/// <returns>.NET DateTime</returns>
public static DateTime HighSierraToDateTime(byte[] vdDateTime)
{
var isoTime = new byte[17];
Array.Copy(vdDateTime, 0, isoTime, 0, 16);
return Iso9660ToDateTime(isoTime);
}
// TODO: Timezone
/// <summary>Converts an ISO9660 timestamp to a .NET DateTime</summary>
/// <param name="vdDateTime">ISO9660 timestamp</param>
/// <returns>.NET DateTime</returns>
public static DateTime Iso9660ToDateTime(byte[] vdDateTime)
{
var twoCharValue = new byte[2];
var fourCharValue = new byte[4];
fourCharValue[0] = vdDateTime[0];
fourCharValue[1] = vdDateTime[1];
fourCharValue[2] = vdDateTime[2];
fourCharValue[3] = vdDateTime[3];
AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME, "year = \"{0}\"",
StringHandlers.CToString(fourCharValue, Encoding.ASCII));
if(!int.TryParse(StringHandlers.CToString(fourCharValue, Encoding.ASCII), out int year))
year = 0;
twoCharValue[0] = vdDateTime[4];
twoCharValue[1] = vdDateTime[5];
AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME, "month = \"{0}\"",
StringHandlers.CToString(twoCharValue, Encoding.ASCII));
if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int month))
month = 0;
twoCharValue[0] = vdDateTime[6];
twoCharValue[1] = vdDateTime[7];
AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME, "day = \"{0}\"",
StringHandlers.CToString(twoCharValue, Encoding.ASCII));
if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int day))
day = 0;
twoCharValue[0] = vdDateTime[8];
twoCharValue[1] = vdDateTime[9];
AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME, "hour = \"{0}\"",
StringHandlers.CToString(twoCharValue, Encoding.ASCII));
if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int hour))
hour = 0;
twoCharValue[0] = vdDateTime[10];
twoCharValue[1] = vdDateTime[11];
AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME, "minute = \"{0}\"",
StringHandlers.CToString(twoCharValue, Encoding.ASCII));
if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int minute))
minute = 0;
twoCharValue[0] = vdDateTime[12];
twoCharValue[1] = vdDateTime[13];
AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME, "second = \"{0}\"",
StringHandlers.CToString(twoCharValue, Encoding.ASCII));
if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int second))
second = 0;
twoCharValue[0] = vdDateTime[14];
twoCharValue[1] = vdDateTime[15];
AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME, "hundredths = \"{0}\"",
StringHandlers.CToString(twoCharValue, Encoding.ASCII));
if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int hundredths))
hundredths = 0;
AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME,
"decodedDT = new DateTime({0}, {1}, {2}, {3}, {4}, {5}, {6}, DateTimeKind.Unspecified);",
year, month, day, hour, minute, second, hundredths * 10);
var difference = (sbyte)vdDateTime[16];
var decodedDt = new DateTime(year, month, day, hour, minute, second, hundredths * 10, DateTimeKind.Utc);
return decodedDt.AddMinutes(difference * -15);
}
/// <summary>Converts a VMS timestamp to a .NET DateTime</summary>
/// <param name="vmsDate">VMS timestamp (tenths of microseconds since day 0 of the Julian Date)</param>
/// <returns>.NET DateTime</returns>
/// <remarks>C# works in UTC, VMS on Julian Date, some displacement may occur on disks created outside UTC</remarks>
public static DateTime VmsToDateTime(ulong vmsDate)
{
double delta = vmsDate * 0.0001; // Tenths of microseconds to milliseconds, will lose some detail
return _julianEpoch.AddMilliseconds(delta);
}
/// <summary>Converts an Amiga timestamp to a .NET DateTime</summary>
/// <param name="days">Days since the 1st Jan. 1978</param>
/// <param name="minutes">Minutes since o'clock</param>
/// <param name="ticks">Ticks</param>
/// <returns>.NET DateTime</returns>
public static DateTime AmigaToDateTime(uint days, uint minutes, uint ticks)
{
DateTime temp = _amigaEpoch.AddDays(days);
temp = temp.AddMinutes(minutes);
return temp.AddMilliseconds(ticks * 20);
}
/// <summary>Converts an UCSD Pascal timestamp to a .NET DateTime</summary>
/// <param name="dateRecord">UCSD Pascal timestamp</param>
/// <returns>.NET DateTime</returns>
public static DateTime UcsdPascalToDateTime(short dateRecord)
{
int year = ((dateRecord & 0xFE00) >> 9) + 1900;
int day = (dateRecord & 0x01F0) >> 4;
int month = dateRecord & 0x000F;
AaruConsole.DebugWriteLine(PASCAL_MODULE_NAME, "dateRecord = 0x{0:X4}, year = {1}, month = {2}, day = {3}",
dateRecord, year, month, day);
return new DateTime(year, month, day);
}
/// <summary>Converts a DOS timestamp to a .NET DateTime</summary>
/// <param name="date">Date</param>
/// <param name="time">Time</param>
/// <returns>.NET DateTime</returns>
public static DateTime DosToDateTime(ushort date, ushort time)
{
int year = ((date & 0xFE00) >> 9) + 1980;
int month = (date & 0x1E0) >> 5;
int day = date & 0x1F;
int hour = (time & 0xF800) >> 11;
int minute = (time & 0x7E0) >> 5;
int second = (time & 0x1F) * 2;
AaruConsole.DebugWriteLine(DOS_MODULE_NAME, "date = 0x{0:X4}, year = {1}, month = {2}, day = {3}", date, year,
month, day);
AaruConsole.DebugWriteLine(DOS_MODULE_NAME, "time = 0x{0:X4}, hour = {1}, minute = {2}, second = {3}", time,
hour, minute, second);
DateTime dosDate;
try
{
dosDate = new DateTime(year, month, day, hour, minute, second);
}
catch(ArgumentOutOfRangeException)
{
dosDate = new DateTime(1980, 1, 1, 0, 0, 0);
}
return dosDate;
}
/// <summary>Converts a CP/M timestamp to .NET DateTime</summary>
/// <param name="timestamp">CP/M timestamp</param>
/// <returns>.NET DateTime</returns>
public static DateTime CpmToDateTime(byte[] timestamp)
{
var days = BitConverter.ToUInt16(timestamp, 0);
int hours = timestamp[2];
int minutes = timestamp[3];
DateTime temp = _amigaEpoch.AddDays(days);
temp = temp.AddHours(hours);
temp = temp.AddMinutes(minutes);
return temp;
}
/// <summary>Converts an ECMA timestamp to a .NET DateTime</summary>
/// <param name="typeAndTimeZone">Timezone</param>
/// <param name="year">Year</param>
/// <param name="month">Month</param>
/// <param name="day">Day</param>
/// <param name="hour">Hour</param>
/// <param name="minute">Minute</param>
/// <param name="second">Second</param>
/// <param name="centiseconds">Centiseconds</param>
/// <param name="hundredsOfMicroseconds">Hundreds of microseconds</param>
/// <param name="microseconds">Microseconds</param>
/// <returns></returns>
public static DateTime EcmaToDateTime(ushort typeAndTimeZone, short year, byte month, byte day, byte hour,
byte minute, byte second, byte centiseconds, byte hundredsOfMicroseconds,
byte microseconds)
{
var specification = (byte)((typeAndTimeZone & 0xF000) >> 12);
long ticks = (long)centiseconds * 100000 + (long)hundredsOfMicroseconds * 1000 + (long)microseconds * 10;
if(specification == 0)
return new DateTime(year, month, day, hour, minute, second, DateTimeKind.Utc).AddTicks(ticks);
var preOffset = (ushort)(typeAndTimeZone & 0xFFF);
short offset;
if((preOffset & 0x800) == 0x800)
offset = (short)(preOffset | 0xF000);
else
offset = (short)(preOffset & 0x7FF);
switch(offset)
{
case -2047:
return new DateTime(year, month, day, hour, minute, second, DateTimeKind.Unspecified).AddTicks(ticks);
case < -1440 or > 1440:
offset = 0;
break;
}
return new DateTimeOffset(year, month, day, hour, minute, second, new TimeSpan(0, offset, 0)).AddTicks(ticks).
DateTime;
}
/// <summary>Converts a Solaris high resolution timestamp to .NET DateTime</summary>
/// <param name="hrTimeStamp">Solaris high resolution timestamp</param>
/// <returns>.NET DateTime</returns>
public static DateTime UnixHrTimeToDateTime(ulong hrTimeStamp) => _unixEpoch.AddTicks((long)(hrTimeStamp / 100));
/// <summary>Converts an OS-9 timestamp to .NET DateTime</summary>
/// <param name="date">OS-9 timestamp</param>
/// <returns>.NET DateTime</returns>
public static DateTime Os9ToDateTime(byte[] date)
{
if(date == null || date.Length != 3 && date.Length != 5)
return DateTime.MinValue;
DateTime os9Date;
try
{
os9Date = date.Length == 5
? new DateTime(1900 + date[0], date[1], date[2], date[3], date[4], 0)
: new DateTime(1900 + date[0], date[1], date[2], 0, 0, 0);
}
catch(ArgumentOutOfRangeException)
{
os9Date = new DateTime(1900, 0, 0, 0, 0, 0);
}
return os9Date;
}
/// <summary>Converts a LIF timestamp to .NET DateTime</summary>
/// <param name="date">LIF timestamp</param>
/// <returns>.NET DateTime</returns>
public static DateTime LifToDateTime(byte[] date) => date is not { Length: 6 }
? new DateTime(1970, 1, 1, 0, 0, 0)
: LifToDateTime(date[0], date[1], date[2], date[3],
date[4], date[5]);
/// <summary>Converts a LIF timestamp to .NET DateTime</summary>
/// <param name="year">Yer</param>
/// <param name="month">Month</param>
/// <param name="day">Day</param>
/// <param name="hour">Hour</param>
/// <param name="minute">Minute</param>
/// <param name="second">Second</param>
/// <returns>.NET DateTime</returns>
public static DateTime LifToDateTime(byte year, byte month, byte day, byte hour, byte minute, byte second)
{
try
{
int iyear = (year >> 4) * 10 + (year & 0xF);
int imonth = (month >> 4) * 10 + (month & 0xF);
int iday = (day >> 4) * 10 + (day & 0xF);
int iminute = (minute >> 4) * 10 + (minute & 0xF);
int ihour = (hour >> 4) * 10 + (hour & 0xF);
int isecond = (second >> 4) * 10 + (second & 0xF);
if(iyear >= 70)
iyear += 1900;
else
iyear += 2000;
return new DateTime(iyear, imonth, iday, ihour, iminute, isecond);
}
catch(ArgumentOutOfRangeException)
{
return new DateTime(1970, 1, 1, 0, 0, 0);
}
}
}

View File

@@ -0,0 +1,72 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Extensions.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Helpers.
//
// --[ Description ] ----------------------------------------------------------
//
// Provides class extensions.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
using System.IO;
namespace Aaru.Helpers;
public static class Extensions
{
/// <summary>
/// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the
/// position within the stream by the number of bytes read.<br /> Guarantees the whole count of bytes is read or EOF is
/// found
/// </summary>
/// <param name="s">Stream to extend</param>
/// <param name="buffer">
/// An array of bytes. When this method returns, the buffer contains the specified byte array with the
/// values between <see cref="offset" /> and (<see cref="offset" /> + <see cref="count" /> - 1) replaced by the bytes
/// read from the current source.
/// </param>
/// <param name="offset">
/// The zero-based byte offset in <see cref="buffer" /> at which to begin storing the data read from
/// the current stream.
/// </param>
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
/// <returns>
/// The total number of bytes read into the buffer. This can be less than the number of bytes requested if the end
/// of the stream has been reached.
/// </returns>
public static int EnsureRead(this Stream s, byte[] buffer, int offset, int count)
{
var pos = 0;
int read;
do
{
read = s.Read(buffer, pos + offset, count - pos);
pos += read;
} while(read > 0);
return pos;
}
}

View File

@@ -0,0 +1,257 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : ForcedSeekStream.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Filters.
//
// --[ Description ] ----------------------------------------------------------
//
// Provides a seekable stream from a forward-readable stream.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
using System;
using System.IO;
namespace Aaru.Helpers.IO;
/// <summary>
/// ForcedSeekStream allows to seek a forward-readable stream (like System.IO.Compression streams) by doing the
/// slow and known trick of rewinding and forward reading until arriving the desired position.
/// </summary>
/// <inheritdoc />
public sealed class ForcedSeekStream<T> : Stream where T : Stream
{
const int BUFFER_LEN = 1048576;
readonly string _backFile;
readonly FileStream _backStream;
readonly T _baseStream;
long _streamLength;
/// <summary>Initializes a new instance of the <see cref="T:Aaru.Helpers.IO.ForcedSeekStream`1" /> class.</summary>
/// <param name="length">The real (uncompressed) length of the stream.</param>
/// <param name="args">Parameters that are used to create the base stream.</param>
/// <inheritdoc />
public ForcedSeekStream(long length, params object[] args)
{
_streamLength = length;
_baseStream = (T)Activator.CreateInstance(typeof(T), args);
_backFile = Path.GetTempFileName();
_backStream = new FileStream(_backFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
if(length == 0)
CalculateLength();
}
/// <summary>Initializes a new instance of the <see cref="T:Aaru.Helpers.IO.ForcedSeekStream`1" /> class.</summary>
/// <param name="args">Parameters that are used to create the base stream.</param>
/// <inheritdoc />
public ForcedSeekStream(params object[] args)
{
_baseStream = (T)Activator.CreateInstance(typeof(T), args);
_backFile = Path.GetTempFileName();
_backStream = new FileStream(_backFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
CalculateLength();
}
/// <inheritdoc />
public override bool CanRead => _baseStream.CanRead;
/// <inheritdoc />
public override bool CanSeek => true;
/// <inheritdoc />
public override bool CanWrite => false;
/// <inheritdoc />
public override long Length => _streamLength;
/// <inheritdoc />
public override long Position
{
get => _backStream.Position;
set => SetPosition(value);
}
/// <summary>
/// Calculates the real (uncompressed) length of the stream. It basically reads (uncompresses) the whole stream to
/// memory discarding its contents, so it should be used as a last resort.
/// </summary>
/// <returns>The length.</returns>
public void CalculateLength()
{
int read;
do
{
var buffer = new byte[BUFFER_LEN];
read = _baseStream.EnsureRead(buffer, 0, BUFFER_LEN);
_backStream.Write(buffer, 0, read);
} while(read == BUFFER_LEN);
_streamLength = _backStream.Length;
_backStream.Position = 0;
}
void SetPosition(long position)
{
if(position == _backStream.Position)
return;
if(position < _backStream.Length)
{
_backStream.Position = position;
return;
}
if(position > _streamLength)
position = _streamLength;
_backStream.Position = _backStream.Length;
long toPosition = position - _backStream.Position;
var fullBufferReads = (int)(toPosition / BUFFER_LEN);
var restToRead = (int)(toPosition % BUFFER_LEN);
byte[] buffer;
int bufPos;
int left;
for(var i = 0; i < fullBufferReads; i++)
{
buffer = new byte[BUFFER_LEN];
bufPos = 0;
left = BUFFER_LEN;
while(left > 0)
{
int done = _baseStream.EnsureRead(buffer, bufPos, left);
left -= done;
bufPos += done;
}
_backStream.Write(buffer, 0, BUFFER_LEN);
}
buffer = new byte[restToRead];
bufPos = 0;
left = restToRead;
while(left > 0)
{
int done = _baseStream.EnsureRead(buffer, bufPos, left);
left -= done;
bufPos += done;
}
_backStream.Write(buffer, 0, restToRead);
}
/// <inheritdoc />
public override void Flush()
{
_baseStream.Flush();
_backStream.Flush();
}
/// <inheritdoc />
public override int Read(byte[] buffer, int offset, int count)
{
if(_backStream.Position + count > _streamLength)
count = (int)(_streamLength - _backStream.Position);
if(_backStream.Position + count <= _backStream.Length)
return _backStream.EnsureRead(buffer, offset, count);
long oldPosition = _backStream.Position;
SetPosition(_backStream.Position + count);
SetPosition(oldPosition);
return _backStream.EnsureRead(buffer, offset, count);
}
/// <inheritdoc />
public override int ReadByte()
{
if(_backStream.Position + 1 > _streamLength)
return -1;
if(_backStream.Position + 1 <= _backStream.Length)
return _backStream.ReadByte();
SetPosition(_backStream.Position + 1);
SetPosition(_backStream.Position - 1);
return _backStream.ReadByte();
}
/// <inheritdoc />
public override long Seek(long offset, SeekOrigin origin)
{
switch(origin)
{
case SeekOrigin.Begin:
if(offset < 0)
throw new IOException(Localization.Cannot_seek_before_stream_start);
SetPosition(offset);
break;
case SeekOrigin.End:
if(offset > 0)
throw new IOException(Localization.Cannot_seek_after_stream_end);
if(_streamLength == 0)
CalculateLength();
SetPosition(_streamLength + offset);
break;
default:
SetPosition(_backStream.Position + offset);
break;
}
return _backStream.Position;
}
/// <inheritdoc />
public override void SetLength(long value) => throw new NotSupportedException();
/// <inheritdoc />
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
/// <inheritdoc />
public override void Close()
{
_backStream?.Close();
File.Delete(_backFile);
}
~ForcedSeekStream()
{
_backStream?.Close();
File.Delete(_backFile);
}
}

View File

@@ -0,0 +1,78 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : NonClosableStream.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Compression.
//
// --[ Description ] ----------------------------------------------------------
//
// Overrides MemoryStream to ignore standard close requests.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
using System.Diagnostics.CodeAnalysis;
using System.IO;
namespace Aaru.Helpers.IO;
/// <inheritdoc />
/// <summary>Creates a MemoryStream that ignores close commands</summary>
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public sealed class NonClosableStream : Stream
{
readonly Stream _baseStream;
public NonClosableStream(byte[] buffer) => _baseStream = new MemoryStream(buffer);
public NonClosableStream() => _baseStream = new MemoryStream();
public NonClosableStream(Stream stream) => _baseStream = stream;
public override bool CanRead => _baseStream.CanRead;
public override bool CanSeek => _baseStream.CanSeek;
public override bool CanWrite => _baseStream.CanWrite;
public override long Length => _baseStream.Length;
public override long Position
{
get => _baseStream.Position;
set => _baseStream.Position = value;
}
public override void Flush() => _baseStream.Flush();
public override int Read(byte[] buffer, int offset, int count) => _baseStream.EnsureRead(buffer, offset, count);
public override long Seek(long offset, SeekOrigin origin) => _baseStream.Seek(offset, origin);
public override void SetLength(long value) => _baseStream.SetLength(value);
public override void Write(byte[] buffer, int offset, int count) => _baseStream.Write(buffer, offset, count);
public override void Close()
{
// Do nothing
}
public void ReallyClose() => _baseStream.Close();
}

View File

@@ -0,0 +1,680 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : OffsetStream.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Filters.
//
// --[ Description ] ----------------------------------------------------------
//
// Provides a stream that's a subset of another stream.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Microsoft.Win32.SafeHandles;
#if !NETSTANDARD2_0
#endif
namespace Aaru.Helpers.IO;
/// <summary>Creates a stream that is a subset of another stream.</summary>
/// <inheritdoc />
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public sealed class OffsetStream : Stream
{
readonly Stream _baseStream;
readonly long _streamEnd;
readonly long _streamStart;
/// <inheritdoc />
/// <summary>
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
/// specified stream, both inclusive.
/// </summary>
/// <param name="stream">Base stream</param>
/// <param name="start">Start position</param>
/// <param name="end">Last readable position</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
public OffsetStream(Stream stream, long start, long end)
{
if(start < 0)
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
if(end < 0)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
_streamStart = start;
_streamEnd = end;
_baseStream = stream;
if(end > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
_baseStream.Position = start;
}
/// <inheritdoc />
/// <summary>
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
/// specified file, both inclusive.
/// </summary>
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="T:System.IO.FileStream" /> object.
/// </param>
/// <param name="share">
/// A bitwise combination of the enumeration values that determines how the file will be shared by
/// processes.
/// </param>
/// <param name="bufferSize">
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
/// 4096.
/// </param>
/// <param name="options">A bitwise combination of the enumeration values that specifies additional file options.</param>
/// <param name="start">Start position</param>
/// <param name="end">Last readable position</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
public OffsetStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize,
FileOptions options, long start, long end)
{
if(start < 0)
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
if(end < 0)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
_streamStart = start;
_streamEnd = end;
_baseStream = new FileStream(path, mode, access, share, bufferSize, options);
if(end > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
_baseStream.Position = start;
}
/// <inheritdoc />
/// <summary>
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
/// specified file, both inclusive.
/// </summary>
/// <param name="handle">A file handle for the file that the stream will encapsulate.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="T:System.IO.FileStream" /> object.
/// </param>
/// <param name="start">Start position</param>
/// <param name="end">Last readable position</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
public OffsetStream(SafeFileHandle handle, FileAccess access, long start, long end)
{
if(start < 0)
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
if(end < 0)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
_streamStart = start;
_streamEnd = end;
_baseStream = new FileStream(handle, access);
if(end > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
_baseStream.Position = start;
}
/// <inheritdoc />
/// <summary>
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
/// specified file, both inclusive.
/// </summary>
/// <param name="handle">A file handle for the file that the stream will encapsulate.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="T:System.IO.FileStream" /> object.
/// </param>
/// <param name="bufferSize">
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
/// 4096.
/// </param>
/// <param name="start">Start position</param>
/// <param name="end">Last readable position</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
public OffsetStream(SafeFileHandle handle, FileAccess access, int bufferSize, long start, long end)
{
if(start < 0)
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
if(end < 0)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
_streamStart = start;
_streamEnd = end;
_baseStream = new FileStream(handle, access, bufferSize);
if(end > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
_baseStream.Position = start;
}
/// <inheritdoc />
/// <summary>
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
/// specified file, both inclusive.
/// </summary>
/// <param name="handle">A file handle for the file that the stream will encapsulate.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="T:System.IO.FileStream" /> object.
/// </param>
/// <param name="bufferSize">
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
/// 4096.
/// </param>
/// <param name="isAsync">Specifies whether to use asynchronous I/O or synchronous I/O.</param>
/// <param name="start">Start position</param>
/// <param name="end">Last readable position</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
public OffsetStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync, long start, long end)
{
if(start < 0)
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
if(end < 0)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
_streamStart = start;
_streamEnd = end;
_baseStream = new FileStream(handle, access, bufferSize, isAsync);
if(end > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
_baseStream.Position = start;
}
/// <inheritdoc />
/// <summary>
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
/// specified file, both inclusive.
/// </summary>
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="T:System.IO.FileStream" /> object.
/// </param>
/// <param name="share">
/// A bitwise combination of the enumeration values that determines how the file will be shared by
/// processes.
/// </param>
/// <param name="bufferSize">
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
/// 4096.
/// </param>
/// <param name="useAsync">Specifies whether to use asynchronous I/O or synchronous I/O.</param>
/// <param name="start">Start position</param>
/// <param name="end">Last readable position</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
public OffsetStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync,
long start, long end)
{
if(start < 0)
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
if(end < 0)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
_streamStart = start;
_streamEnd = end;
_baseStream = new FileStream(path, mode, access, share, bufferSize, useAsync);
if(end > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
_baseStream.Position = start;
}
/// <inheritdoc />
/// <summary>
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
/// specified file, both inclusive.
/// </summary>
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="T:System.IO.FileStream" /> object.
/// </param>
/// <param name="share">
/// A bitwise combination of the enumeration values that determines how the file will be shared by
/// processes.
/// </param>
/// <param name="bufferSize">
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
/// 4096.
/// </param>
/// <param name="start">Start position</param>
/// <param name="end">Last readable position</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
public OffsetStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, long start,
long end)
{
if(start < 0)
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
if(end < 0)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
_streamStart = start;
_streamEnd = end;
_baseStream = new FileStream(path, mode, access, share, bufferSize);
if(end > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
_baseStream.Position = start;
}
/// <inheritdoc />
/// <summary>
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
/// specified file, both inclusive.
/// </summary>
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="T:System.IO.FileStream" /> object.
/// </param>
/// <param name="share">
/// A bitwise combination of the enumeration values that determines how the file will be shared by
/// processes.
/// </param>
/// <param name="start">Start position</param>
/// <param name="end">Last readable position</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
public OffsetStream(string path, FileMode mode, FileAccess access, FileShare share, long start, long end)
{
if(start < 0)
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
if(end < 0)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
_streamStart = start;
_streamEnd = end;
_baseStream = new FileStream(path, mode, access, share);
if(end > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
_baseStream.Position = start;
}
/// <inheritdoc />
/// <summary>
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
/// specified file, both inclusive.
/// </summary>
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="T:System.IO.FileStream" /> object.
/// </param>
/// <param name="start">Start position</param>
/// <param name="end">Last readable position</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
public OffsetStream(string path, FileMode mode, FileAccess access, long start, long end)
{
if(start < 0)
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
if(end < 0)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
_streamStart = start;
_streamEnd = end;
_baseStream = new FileStream(path, mode, access);
if(end > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
_baseStream.Position = start;
}
/// <inheritdoc />
/// <summary>
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
/// specified file, both inclusive.
/// </summary>
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
/// <param name="start">Start position</param>
/// <param name="end">Last readable position</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
public OffsetStream(string path, FileMode mode, long start, long end)
{
if(start < 0)
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
if(end < 0)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
_streamStart = start;
_streamEnd = end;
_baseStream = new FileStream(path, mode);
if(end > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
_baseStream.Position = start;
}
/// <inheritdoc />
/// <summary>
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
/// specified byte array, both inclusive.
/// </summary>
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
/// <param name="index">The index into <paramref name="buffer" /> at which the stream begins.</param>
/// <param name="count">The length in bytes to add to the end of the current stream.</param>
/// <param name="writable">The setting of the CanWrite property, currently ignored.</param>
/// <param name="publiclyVisible">Currently ignored.</param>
/// <param name="start">Start position</param>
/// <param name="end">Last readable position</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
public OffsetStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible, long start, long end)
{
if(start < 0)
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
if(end < 0)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
_streamStart = start;
_streamEnd = end;
_baseStream = new MemoryStream(buffer, index, count, writable, publiclyVisible);
if(end > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
_baseStream.Position = start;
}
/// <inheritdoc />
/// <summary>
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
/// specified byte array, both inclusive.
/// </summary>
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
/// <param name="index">The index into <paramref name="buffer" /> at which the stream begins.</param>
/// <param name="count">The length in bytes to add to the end of the current stream.</param>
/// <param name="writable">The setting of the CanWrite property, currently ignored.</param>
/// <param name="start">Start position</param>
/// <param name="end">Last readable position</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
public OffsetStream(byte[] buffer, int index, int count, bool writable, long start, long end)
{
if(start < 0)
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
if(end < 0)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
_streamStart = start;
_streamEnd = end;
_baseStream = new MemoryStream(buffer, index, count, writable);
if(end > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
_baseStream.Position = start;
}
/// <inheritdoc />
/// <summary>
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
/// specified byte array, both inclusive.
/// </summary>
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
/// <param name="index">The index into <paramref name="buffer" /> at which the stream begins.</param>
/// <param name="count">The length in bytes to add to the end of the current stream.</param>
/// <param name="start">Start position</param>
/// <param name="end">Last readable position</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
public OffsetStream(byte[] buffer, int index, int count, long start, long end)
{
if(start < 0)
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
if(end < 0)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
_streamStart = start;
_streamEnd = end;
_baseStream = new MemoryStream(buffer, index, count);
if(end > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
_baseStream.Position = start;
}
/// <inheritdoc />
/// <summary>
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
/// specified byte array, both inclusive.
/// </summary>
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
/// <param name="writable">The setting of the CanWrite property, currently ignored.</param>
/// <param name="start">Start position</param>
/// <param name="end">Last readable position</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
public OffsetStream(byte[] buffer, bool writable, long start, long end)
{
if(start < 0)
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
if(end < 0)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
_streamStart = start;
_streamEnd = end;
_baseStream = new MemoryStream(buffer, writable);
if(end > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
_baseStream.Position = start;
}
/// <inheritdoc />
/// <summary>
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
/// specified byte array, both inclusive.
/// </summary>
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
/// <param name="start">Start position</param>
/// <param name="end">Last readable position</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
public OffsetStream(byte[] buffer, long start, long end)
{
if(start < 0)
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
if(end < 0)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
_streamStart = start;
_streamEnd = end;
_baseStream = new MemoryStream(buffer);
if(end > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
_baseStream.Position = start;
}
/// <inheritdoc />
public override bool CanRead => _baseStream.CanRead;
/// <inheritdoc />
public override bool CanSeek => _baseStream.CanSeek;
/// <inheritdoc />
public override bool CanWrite => _baseStream.CanWrite;
/// <inheritdoc />
public override long Length => _streamEnd - _streamStart + 1;
/// <inheritdoc />
public override long Position
{
get => _baseStream.Position - _streamStart;
set
{
if(value + _streamStart > _streamEnd)
throw new IOException(Localization.Cannot_set_position_past_stream_end);
_baseStream.Position = value + _streamStart;
}
}
~OffsetStream()
{
_baseStream.Close();
_baseStream.Dispose();
}
/// <inheritdoc />
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
if(_baseStream.Position + count > _streamEnd)
count = (int)(_streamEnd - _baseStream.Position);
return _baseStream.BeginRead(buffer, offset, count, callback, state);
}
/// <inheritdoc />
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
if(_baseStream.Position + count > _streamEnd)
throw new IOException(Localization.Cannot_write_past_stream_end);
return _baseStream.BeginWrite(buffer, offset, count, callback, state);
}
/// <inheritdoc />
public override void Close() => _baseStream.Close();
/// <inheritdoc />
public override int EndRead(IAsyncResult asyncResult) => _baseStream.EndRead(asyncResult);
/// <inheritdoc />
public override void EndWrite(IAsyncResult asyncResult) => _baseStream.EndWrite(asyncResult);
/// <inheritdoc />
public override int ReadByte() => _baseStream.Position == _streamEnd + 1 ? -1 : _baseStream.ReadByte();
/// <inheritdoc />
public override void WriteByte(byte value)
{
if(_baseStream.Position + 1 > _streamEnd)
throw new IOException(Localization.Cannot_write_past_stream_end);
_baseStream.WriteByte(value);
}
/// <inheritdoc />
public override void Flush() => _baseStream.Flush();
/// <inheritdoc />
public override int Read(byte[] buffer, int offset, int count)
{
if(_baseStream.Position + count > _streamEnd + 1)
count = (int)(_streamEnd - _baseStream.Position);
return _baseStream.EnsureRead(buffer, offset, count);
}
/// <inheritdoc />
public override long Seek(long offset, SeekOrigin origin)
{
switch(origin)
{
case SeekOrigin.Begin:
if(offset + _streamStart > _streamEnd)
throw new IOException(Localization.Cannot_seek_after_stream_end);
return _baseStream.Seek(offset + _streamStart, SeekOrigin.Begin) - _streamStart;
case SeekOrigin.End:
if(offset - (_baseStream.Length - _streamEnd) < _streamStart)
throw new IOException(Localization.Cannot_seek_before_stream_start);
return _baseStream.Seek(offset - (_baseStream.Length - _streamEnd), SeekOrigin.End) - _streamStart;
default:
if(offset + _baseStream.Position > _streamEnd)
throw new IOException(Localization.Cannot_seek_after_stream_end);
return _baseStream.Seek(offset, SeekOrigin.Current) - _streamStart;
}
}
/// <inheritdoc />
public override void SetLength(long value) =>
throw new NotSupportedException(Localization.Growing_OffsetStream_is_not_supported);
/// <inheritdoc />
public override void Write(byte[] buffer, int offset, int count)
{
if(_baseStream.Position + count > _streamEnd)
throw new IOException(Localization.Cannot_write_past_stream_end);
_baseStream.Write(buffer, offset, count);
}
}

View File

@@ -0,0 +1,372 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Microsoft.Win32.SafeHandles;
namespace Aaru.Helpers.IO;
/// <inheritdoc />
/// <summary>Implements a stream that joins two or more files (sequentially) as a single stream</summary>
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public class SplitJoinStream : Stream
{
readonly Dictionary<long, Stream> _baseStreams;
long _position;
long _streamLength;
/// <inheritdoc />
public SplitJoinStream()
{
_baseStreams = new Dictionary<long, Stream>();
_streamLength = 0;
_position = 0;
}
/// <inheritdoc />
public override bool CanRead => true;
/// <inheritdoc />
public override bool CanSeek => true;
/// <inheritdoc />
public override bool CanWrite => false;
/// <inheritdoc />
public override long Length => _streamLength;
/// <inheritdoc />
public override long Position
{
get => _position;
set
{
if(value >= _streamLength)
throw new IOException(Localization.Cannot_set_position_past_stream_end);
_position = value;
}
}
/// <summary>Adds a stream at the end of the current stream</summary>
/// <param name="stream">Stream to add</param>
/// <exception cref="ArgumentException">The specified stream is non-readable or non-seekable</exception>
public void Add(Stream stream)
{
if(!stream.CanSeek)
throw new ArgumentException(Localization.Non_seekable_streams_are_not_supported);
if(!stream.CanRead)
throw new ArgumentException(Localization.Non_readable_streams_are_not_supported);
_baseStreams[_streamLength] = stream;
_streamLength += stream.Length;
}
/// <summary>Adds the specified file to the end of the current stream</summary>
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="FileStream" /> object.
/// </param>
/// <param name="share">
/// A bitwise combination of the enumeration values that determines how the file will be shared by
/// processes.
/// </param>
/// <param name="bufferSize">
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
/// 4096.
/// </param>
/// <param name="options">A bitwise combination of the enumeration values that specifies additional file options.</param>
public void Add(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize,
FileOptions options) => Add(new FileStream(path, mode, access, share, bufferSize, options));
/// <summary>Adds the specified file to the end of the current stream</summary>
/// <param name="handle">A file handle for the file that the stream will encapsulate.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="FileStream" /> object.
/// </param>
public void Add(SafeFileHandle handle, FileAccess access) => Add(new FileStream(handle, access));
/// <summary>Adds the specified file to the end of the current stream</summary>
/// <param name="handle">A file handle for the file that the stream will encapsulate.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="FileStream" /> object.
/// </param>
/// <param name="bufferSize">
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
/// 4096.
/// </param>
public void Add(SafeFileHandle handle, FileAccess access, int bufferSize) =>
Add(new FileStream(handle, access, bufferSize));
/// <summary>Adds the specified file to the end of the current stream</summary>
/// <param name="handle">A file handle for the file that the stream will encapsulate.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="FileStream" /> object.
/// </param>
/// <param name="bufferSize">
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
/// 4096.
/// </param>
/// <param name="isAsync">Specifies whether to use asynchronous I/O or synchronous I/O.</param>
public void Add(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) =>
Add(new FileStream(handle, access, bufferSize, isAsync));
/// <summary>Adds the specified file to the end of the current stream</summary>
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="FileStream" /> object.
/// </param>
/// <param name="share">
/// A bitwise combination of the enumeration values that determines how the file will be shared by
/// processes.
/// </param>
/// <param name="bufferSize">
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
/// 4096.
/// </param>
/// <param name="useAsync">Specifies whether to use asynchronous I/O or synchronous I/O.</param>
public void Add(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) =>
Add(new FileStream(path, mode, access, share, bufferSize, useAsync));
/// <summary>Adds the specified file to the end of the current stream</summary>
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="FileStream" /> object.
/// </param>
/// <param name="share">
/// A bitwise combination of the enumeration values that determines how the file will be shared by
/// processes.
/// </param>
/// <param name="bufferSize">
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
/// 4096.
/// </param>
public void Add(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) =>
Add(new FileStream(path, mode, access, share, bufferSize));
/// <summary>Adds the specified file to the end of the current stream</summary>
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="FileStream" /> object.
/// </param>
/// <param name="share">
/// A bitwise combination of the enumeration values that determines how the file will be shared by
/// processes.
/// </param>
public void Add(string path, FileMode mode, FileAccess access, FileShare share) =>
Add(new FileStream(path, mode, access, share));
/// <summary>Adds the specified file to the end of the current stream</summary>
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="FileStream" /> object.
/// </param>
public void Add(string path, FileMode mode, FileAccess access) => Add(new FileStream(path, mode, access));
/// <summary>Adds the specified file to the end of the current stream</summary>
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
public void Add(string path, FileMode mode) => Add(new FileStream(path, mode));
/// <summary>Adds the specified byte array to the end of the current stream</summary>
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
/// <param name="index">The index into <paramref name="buffer" /> at which the stream begins.</param>
/// <param name="count">The length in bytes to add to the end of the current stream.</param>
/// <param name="writable">The setting of the CanWrite property, currently ignored.</param>
/// <param name="publiclyVisible">Currently ignored.</param>
public void Add(byte[] buffer, int index, int count, bool writable, bool publiclyVisible) =>
Add(new MemoryStream(buffer, index, count, writable, publiclyVisible));
/// <summary>Adds the specified byte array to the end of the current stream</summary>
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
/// <param name="index">The index into <paramref name="buffer" /> at which the stream begins.</param>
/// <param name="count">The length in bytes to add to the end of the current stream.</param>
/// <param name="writable">The setting of the CanWrite property, currently ignored.</param>
public void Add(byte[] buffer, int index, int count, bool writable) =>
Add(new MemoryStream(buffer, index, count, writable));
/// <summary>Adds the specified byte array to the end of the current stream</summary>
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
/// <param name="index">The index into <paramref name="buffer" /> at which the stream begins.</param>
/// <param name="count">The length in bytes to add to the end of the current stream.</param>
public void Add(byte[] buffer, int index, int count) => Add(new MemoryStream(buffer, index, count));
/// <summary>Adds the specified byte array to the end of the current stream</summary>
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
/// <param name="writable">The setting of the CanWrite property, currently ignored.</param>
public void Add(byte[] buffer, bool writable) => Add(new MemoryStream(buffer, writable));
/// <summary>Adds the specified byte array to the end of the current stream</summary>
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
public void Add(byte[] buffer) => Add(new MemoryStream(buffer));
/// <summary>Adds a range of files to the end of the current stream, alphabetically sorted</summary>
/// <param name="basePath">Base file path, directory path only</param>
/// <param name="counterFormat">Counter format, includes filename and a formatting string</param>
/// <param name="counterStart">Counter start, defaults to 0</param>
/// <param name="access">
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
/// <see cref="FileStream" /> object.
/// </param>
public void AddRange(string basePath, string counterFormat = "{0:D3}", int counterStart = 0,
FileAccess access = FileAccess.Read)
{
while(true)
{
string filePath = Path.Combine(basePath, string.Format(counterFormat, counterStart));
if(!File.Exists(filePath))
break;
Add(filePath, FileMode.Open, access);
counterStart++;
}
}
~SplitJoinStream()
{
foreach(Stream stream in _baseStreams.Values)
{
stream.Close();
stream.Dispose();
}
_baseStreams.Clear();
_position = 0;
}
/// <inheritdoc />
public override IAsyncResult
BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) =>
throw new NotSupportedException(Localization.Asynchronous_IO_is_not_supported);
/// <inheritdoc />
public override IAsyncResult
BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) =>
throw new NotSupportedException(Localization.Asynchronous_IO_is_not_supported);
/// <inheritdoc />
public override void Close()
{
foreach(Stream stream in _baseStreams.Values)
stream.Close();
_baseStreams.Clear();
_position = 0;
}
/// <inheritdoc />
public override int EndRead(IAsyncResult asyncResult) =>
throw new NotSupportedException(Localization.Asynchronous_IO_is_not_supported);
/// <inheritdoc />
public override void EndWrite(IAsyncResult asyncResult) =>
throw new NotSupportedException(Localization.Asynchronous_IO_is_not_supported);
/// <inheritdoc />
public override int ReadByte()
{
if(_position >= _streamLength)
return -1;
KeyValuePair<long, Stream> baseStream = _baseStreams.FirstOrDefault(s => s.Key >= _position);
if(baseStream.Value == null)
return -1;
baseStream.Value.Position = _position - baseStream.Key;
_position++;
return baseStream.Value.ReadByte();
}
/// <inheritdoc />
public override void WriteByte(byte value) => throw new ReadOnlyException(Localization.This_stream_is_read_only);
/// <inheritdoc />
public override void Flush() {}
/// <inheritdoc />
public override int Read(byte[] buffer, int offset, int count)
{
var read = 0;
while(count > 0)
{
KeyValuePair<long, Stream> baseStream = _baseStreams.LastOrDefault(s => s.Key <= _position);
if(baseStream.Value == null)
break;
baseStream.Value.Position = _position - baseStream.Key;
int currentCount = count;
if(baseStream.Value.Position + currentCount > baseStream.Value.Length)
currentCount = (int)(baseStream.Value.Length - baseStream.Value.Position);
read += baseStream.Value.Read(buffer, offset, currentCount);
count -= currentCount;
offset += currentCount;
}
return read;
}
/// <inheritdoc />
public override long Seek(long offset, SeekOrigin origin)
{
switch(origin)
{
case SeekOrigin.Begin:
if(offset >= _streamLength)
throw new IOException(Localization.Cannot_seek_after_stream_end);
_position = offset;
break;
case SeekOrigin.End:
if(_position - offset < 0)
throw new IOException(Localization.Cannot_seek_before_stream_start);
_position -= offset;
break;
default:
if(_position + offset >= _streamLength)
throw new IOException(Localization.Cannot_seek_after_stream_end);
_position += offset;
break;
}
return _position;
}
/// <inheritdoc />
public override void SetLength(long value) => throw new ReadOnlyException(Localization.This_stream_is_read_only);
/// <inheritdoc />
public override void Write(byte[] buffer, int offset, int count) =>
throw new ReadOnlyException(Localization.This_stream_is_read_only);
}

View File

@@ -0,0 +1,197 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Aaru.Helpers {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Localization {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Localization() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Aaru.Helpers.Localization.Localization", typeof(Localization).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Asynchronous I/O is not supported..
/// </summary>
internal static string Asynchronous_IO_is_not_supported {
get {
return ResourceManager.GetString("Asynchronous_IO_is_not_supported", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cannot read past stream end..
/// </summary>
internal static string Cannot_read_past_stream_end {
get {
return ResourceManager.GetString("Cannot_read_past_stream_end", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cannot seek after stream end..
/// </summary>
internal static string Cannot_seek_after_stream_end {
get {
return ResourceManager.GetString("Cannot_seek_after_stream_end", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cannot seek before stream start..
/// </summary>
internal static string Cannot_seek_before_stream_start {
get {
return ResourceManager.GetString("Cannot_seek_before_stream_start", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cannot set position past stream end..
/// </summary>
internal static string Cannot_set_position_past_stream_end {
get {
return ResourceManager.GetString("Cannot_set_position_past_stream_end", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cannot write past stream end..
/// </summary>
internal static string Cannot_write_past_stream_end {
get {
return ResourceManager.GetString("Cannot_write_past_stream_end", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to End can&apos;t be a negative number..
/// </summary>
internal static string End_cant_be_a_negative_number {
get {
return ResourceManager.GetString("End_cant_be_a_negative_number", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to End is after stream end..
/// </summary>
internal static string End_is_after_stream_end {
get {
return ResourceManager.GetString("End_is_after_stream_end", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Growing OffsetStream is not supported..
/// </summary>
internal static string Growing_OffsetStream_is_not_supported {
get {
return ResourceManager.GetString("Growing_OffsetStream_is_not_supported", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Length of value array must not be more than length of destination.
/// </summary>
internal static string Length_of_value_array_must_not_be_more_than_length_of_destination {
get {
return ResourceManager.GetString("Length_of_value_array_must_not_be_more_than_length_of_destination", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Non-readable streams are not supported.
/// </summary>
internal static string Non_readable_streams_are_not_supported {
get {
return ResourceManager.GetString("Non_readable_streams_are_not_supported", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Non-seekable streams are not supported.
/// </summary>
internal static string Non_seekable_streams_are_not_supported {
get {
return ResourceManager.GetString("Non_seekable_streams_are_not_supported", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Offset.
/// </summary>
internal static string Offset {
get {
return ResourceManager.GetString("Offset", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Start can&apos;t be a negative number..
/// </summary>
internal static string Start_cant_be_a_negative_number {
get {
return ResourceManager.GetString("Start_cant_be_a_negative_number", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This stream is read-only.
/// </summary>
internal static string This_stream_is_read_only {
get {
return ResourceManager.GetString("This_stream_is_read_only", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,64 @@
<root>
<!-- ReSharper disable MarkupTextTypo -->
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<data name="Offset" xml:space="preserve">
<value>Compensación</value>
</data>
<data name="Length_of_value_array_must_not_be_more_than_length_of_destination" xml:space="preserve">
<value>La longitud de una colección no puede ser mayor que la longitud del destino</value>
</data>
<data name="Non_readable_streams_are_not_supported" xml:space="preserve">
<value>Las secuencias no legíbles no están soportadas.</value>
</data>
<data name="Non_seekable_streams_are_not_supported" xml:space="preserve">
<value>Las secuencias no posicionables no están soportadas.</value>
</data>
<data name="Cannot_read_past_stream_end" xml:space="preserve">
<value>No se puede leer más allá del final de la secuencia.</value>
</data>
<data name="Cannot_seek_after_stream_end" xml:space="preserve">
<value>No se puede posicionar después del final de la secuencia.</value>
</data>
<data name="Cannot_seek_before_stream_start" xml:space="preserve">
<value>No se puede posicionar antes del comienzo de la secuencia.</value>
</data>
<data name="Cannot_set_position_past_stream_end" xml:space="preserve">
<value>No se puede establecer la posición más allá del final de la secuencia.</value>
</data>
<data name="Cannot_write_past_stream_end" xml:space="preserve">
<value>No se puede escribir después del final de la secuencia.</value>
</data>
<data name="End_cant_be_a_negative_number" xml:space="preserve">
<value>El final no puede ser un número negativo.</value>
</data>
<data name="End_is_after_stream_end" xml:space="preserve">
<value>El final está después del final de la secuencia.</value>
</data>
<data name="Growing_OffsetStream_is_not_supported" xml:space="preserve">
<value>No se puede agrandar un OffsetStream.</value>
</data>
<data name="Asynchronous_IO_is_not_supported" xml:space="preserve">
<value>E/S asíncrona no soportada.</value>
</data>
<data name="Start_cant_be_a_negative_number" xml:space="preserve">
<value>El comienzo no puede ser un número negativo.</value>
</data>
<data name="This_stream_is_read_only" xml:space="preserve">
<value>Esta secuencia es de sólo lectura.</value>
</data>
</root>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8" ?>
<root>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
id="root"
xmlns="">
<xsd:element name="root" msdata:IsDataSet="true"></xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<data name="Length_of_value_array_must_not_be_more_than_length_of_destination" xml:space="preserve">
<value>Length of value array must not be more than length of destination</value>
</data>
<data name="Offset" xml:space="preserve">
<value>Offset</value>
</data>
<data name="Start_cant_be_a_negative_number" xml:space="preserve">
<value>Start can't be a negative number.</value>
</data>
<data name="End_cant_be_a_negative_number" xml:space="preserve">
<value>End can't be a negative number.</value>
</data>
<data name="End_is_after_stream_end" xml:space="preserve">
<value>End is after stream end.</value>
</data>
<data name="Cannot_set_position_past_stream_end" xml:space="preserve">
<value>Cannot set position past stream end.</value>
</data>
<data name="Cannot_read_past_stream_end" xml:space="preserve">
<value>Cannot read past stream end.</value>
</data>
<data name="Cannot_write_past_stream_end" xml:space="preserve">
<value>Cannot write past stream end.</value>
</data>
<data name="Growing_OffsetStream_is_not_supported" xml:space="preserve">
<value>Growing OffsetStream is not supported.</value>
</data>
<data name="Cannot_seek_before_stream_start" xml:space="preserve">
<value>Cannot seek before stream start.</value>
</data>
<data name="Cannot_seek_after_stream_end" xml:space="preserve">
<value>Cannot seek after stream end.</value>
</data>
<data name="Non_seekable_streams_are_not_supported" xml:space="preserve">
<value>Non-seekable streams are not supported</value>
</data>
<data name="Non_readable_streams_are_not_supported" xml:space="preserve">
<value>Non-readable streams are not supported</value>
</data>
<data name="Asynchronous_IO_is_not_supported" xml:space="preserve">
<value>Asynchronous I/O is not supported.</value>
</data>
<data name="This_stream_is_read_only" xml:space="preserve">
<value>This stream is read-only</value>
</data>
</root>

492
Aaru.Helpers/Marshal.cs Normal file
View File

@@ -0,0 +1,492 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Marshal.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Helpers.
//
// --[ Description ] ----------------------------------------------------------
//
// Provides marshalling for binary data.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Aaru.Helpers;
/// <summary>Provides methods to marshal binary data into C# structs</summary>
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static class Marshal
{
/// <summary>Returns the size of an unmanaged type in bytes.</summary>
/// <typeparam name="T">The type whose size is to be returned.</typeparam>
/// <returns>The size, in bytes, of the type that is specified by the <see cref="T" /> generic type parameter.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int SizeOf<T>() => System.Runtime.InteropServices.Marshal.SizeOf<T>();
/// <summary>Marshal little-endian binary data to a structure</summary>
/// <param name="bytes">Byte array containing the binary data</param>
/// <typeparam name="T">Type of the structure to marshal</typeparam>
/// <returns>The binary data marshalled in a structure with the specified type</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T ByteArrayToStructureLittleEndian<T>(byte[] bytes) where T : struct
{
var ptr = GCHandle.Alloc(bytes, GCHandleType.Pinned);
var str = (T)(System.Runtime.InteropServices.Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T)) ??
default(T));
ptr.Free();
return str;
}
/// <summary>Marshal little-endian binary data to a structure</summary>
/// <param name="bytes">Byte array containing the binary data</param>
/// <param name="start">Start on the array where the structure begins</param>
/// <param name="length">Length of the structure in bytes</param>
/// <typeparam name="T">Type of the structure to marshal</typeparam>
/// <returns>The binary data marshalled in a structure with the specified type</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T ByteArrayToStructureLittleEndian<T>(byte[] bytes, int start, int length) where T : struct
{
Span<byte> span = bytes;
return ByteArrayToStructureLittleEndian<T>(span.Slice(start, length).ToArray());
}
/// <summary>Marshal big-endian binary data to a structure</summary>
/// <param name="bytes">Byte array containing the binary data</param>
/// <typeparam name="T">Type of the structure to marshal</typeparam>
/// <returns>The binary data marshalled in a structure with the specified type</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T ByteArrayToStructureBigEndian<T>(byte[] bytes) where T : struct
{
var ptr = GCHandle.Alloc(bytes, GCHandleType.Pinned);
object str = (T)(System.Runtime.InteropServices.Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T)) ??
default(T));
ptr.Free();
return (T)SwapStructureMembersEndian(str);
}
/// <summary>Marshal big-endian binary data to a structure</summary>
/// <param name="bytes">Byte array containing the binary data</param>
/// <param name="start">Start on the array where the structure begins</param>
/// <param name="length">Length of the structure in bytes</param>
/// <typeparam name="T">Type of the structure to marshal</typeparam>
/// <returns>The binary data marshalled in a structure with the specified type</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T ByteArrayToStructureBigEndian<T>(byte[] bytes, int start, int length) where T : struct
{
Span<byte> span = bytes;
return ByteArrayToStructureBigEndian<T>(span.Slice(start, length).ToArray());
}
/// <summary>Marshal PDP-11 binary data to a structure</summary>
/// <param name="bytes">Byte array containing the binary data</param>
/// <typeparam name="T">Type of the structure to marshal</typeparam>
/// <returns>The binary data marshalled in a structure with the specified type</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T ByteArrayToStructurePdpEndian<T>(byte[] bytes) where T : struct
{
{
var ptr = GCHandle.Alloc(bytes, GCHandleType.Pinned);
object str =
(T)(System.Runtime.InteropServices.Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T)) ??
default(T));
ptr.Free();
return (T)SwapStructureMembersEndianPdp(str);
}
}
/// <summary>Marshal PDP-11 binary data to a structure</summary>
/// <param name="bytes">Byte array containing the binary data</param>
/// <param name="start">Start on the array where the structure begins</param>
/// <param name="length">Length of the structure in bytes</param>
/// <typeparam name="T">Type of the structure to marshal</typeparam>
/// <returns>The binary data marshalled in a structure with the specified type</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T ByteArrayToStructurePdpEndian<T>(byte[] bytes, int start, int length) where T : struct
{
Span<byte> span = bytes;
return ByteArrayToStructurePdpEndian<T>(span.Slice(start, length).ToArray());
}
/// <summary>
/// Marshal little-endian binary data to a structure. If the structure type contains any non value type, this
/// method will crash.
/// </summary>
/// <param name="bytes">Byte array containing the binary data</param>
/// <typeparam name="T">Type of the structure to marshal</typeparam>
/// <returns>The binary data marshalled in a structure with the specified type</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T SpanToStructureLittleEndian<T>(ReadOnlySpan<byte> bytes) where T : struct =>
MemoryMarshal.Read<T>(bytes);
/// <summary>
/// Marshal little-endian binary data to a structure. If the structure type contains any non value type, this
/// method will crash.
/// </summary>
/// <param name="bytes">Byte span containing the binary data</param>
/// <param name="start">Start on the span where the structure begins</param>
/// <param name="length">Length of the structure in bytes</param>
/// <typeparam name="T">Type of the structure to marshal</typeparam>
/// <returns>The binary data marshalled in a structure with the specified type</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T SpanToStructureLittleEndian<T>(ReadOnlySpan<byte> bytes, int start, int length) where T : struct =>
MemoryMarshal.Read<T>(bytes.Slice(start, length));
/// <summary>
/// Marshal big-endian binary data to a structure. If the structure type contains any non value type, this method
/// will crash.
/// </summary>
/// <param name="bytes">Byte array containing the binary data</param>
/// <typeparam name="T">Type of the structure to marshal</typeparam>
/// <returns>The binary data marshalled in a structure with the specified type</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T SpanToStructureBigEndian<T>(ReadOnlySpan<byte> bytes) where T : struct
{
T str = SpanToStructureLittleEndian<T>(bytes);
return (T)SwapStructureMembersEndian(str);
}
/// <summary>
/// Marshal big-endian binary data to a structure. If the structure type contains any non value type, this method
/// will crash.
/// </summary>
/// <param name="bytes">Byte span containing the binary data</param>
/// <param name="start">Start on the span where the structure begins</param>
/// <param name="length">Length of the structure in bytes</param>
/// <typeparam name="T">Type of the structure to marshal</typeparam>
/// <returns>The binary data marshalled in a structure with the specified type</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T SpanToStructureBigEndian<T>(ReadOnlySpan<byte> bytes, int start, int length) where T : struct
{
T str = SpanToStructureLittleEndian<T>(bytes.Slice(start, length));
return (T)SwapStructureMembersEndian(str);
}
/// <summary>
/// Marshal PDP-11 binary data to a structure. If the structure type contains any non value type, this method will
/// crash.
/// </summary>
/// <param name="bytes">Byte array containing the binary data</param>
/// <typeparam name="T">Type of the structure to marshal</typeparam>
/// <returns>The binary data marshalled in a structure with the specified type</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T SpanToStructurePdpEndian<T>(ReadOnlySpan<byte> bytes) where T : struct
{
object str = SpanToStructureLittleEndian<T>(bytes);
return (T)SwapStructureMembersEndianPdp(str);
}
/// <summary>
/// Marshal PDP-11 binary data to a structure. If the structure type contains any non value type, this method will
/// crash.
/// </summary>
/// <param name="bytes">Byte array containing the binary data</param>
/// <param name="start">Start on the span where the structure begins</param>
/// <param name="length">Length of the structure in bytes</param>
/// <typeparam name="T">Type of the structure to marshal</typeparam>
/// <returns>The binary data marshalled in a structure with the specified type</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T SpanToStructurePdpEndian<T>(ReadOnlySpan<byte> bytes, int start, int length) where T : struct
{
object str = SpanToStructureLittleEndian<T>(bytes.Slice(start, length));
return (T)SwapStructureMembersEndianPdp(str);
}
/// <summary>
/// Marshal a structure depending on the decoration of <see cref="MarshallingPropertiesAttribute" />. If the
/// decoration is not present it will marshal as a reference type containing little endian structure.
/// </summary>
/// <param name="bytes">Byte array containing the binary data</param>
/// <typeparam name="T">Type of the structure to marshal</typeparam>
/// <returns>The binary data marshalled in a structure with the specified type</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// The <see cref="MarshallingPropertiesAttribute" /> contains an unsupported
/// endian
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T MarshalStructure<T>(byte[] bytes) where T : struct
{
if(typeof(T).GetCustomAttribute(typeof(MarshallingPropertiesAttribute)) is not MarshallingPropertiesAttribute
properties)
return ByteArrayToStructureLittleEndian<T>(bytes);
return properties.Endian switch
{
BitEndian.Little => properties.HasReferences
? ByteArrayToStructureLittleEndian<T>(bytes)
: SpanToStructureLittleEndian<T>(bytes),
BitEndian.Big => properties.HasReferences
? ByteArrayToStructureBigEndian<T>(bytes)
: SpanToStructureBigEndian<T>(bytes),
BitEndian.Pdp => properties.HasReferences
? ByteArrayToStructurePdpEndian<T>(bytes)
: SpanToStructurePdpEndian<T>(bytes),
_ => throw new ArgumentOutOfRangeException()
};
}
/// <summary>Swaps all members of a structure</summary>
/// <param name="str"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public static object SwapStructureMembersEndian(object str)
{
Type t = str.GetType();
FieldInfo[] fieldInfo = t.GetFields();
foreach(FieldInfo fi in fieldInfo)
{
if(fi.FieldType == typeof(short) ||
fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(short))
{
var x = (short)(fi.GetValue(str) ?? default(short));
fi.SetValue(str, (short)(x << 8 | x >> 8 & 0xFF));
}
else if(fi.FieldType == typeof(int) ||
fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(int))
{
var x = (int)(fi.GetValue(str) ?? default(int));
x = (int)(x << 8 & 0xFF00FF00 | (uint)x >> 8 & 0xFF00FF);
fi.SetValue(str, (int)((uint)x << 16 | (uint)x >> 16 & 0xFFFF));
}
else if(fi.FieldType == typeof(long) ||
fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(long))
{
var x = (long)(fi.GetValue(str) ?? default(long));
x = (x & 0x00000000FFFFFFFF) << 32 | (long)(((ulong)x & 0xFFFFFFFF00000000) >> 32);
x = (x & 0x0000FFFF0000FFFF) << 16 | (long)(((ulong)x & 0xFFFF0000FFFF0000) >> 16);
x = (x & 0x00FF00FF00FF00FF) << 8 | (long)(((ulong)x & 0xFF00FF00FF00FF00) >> 8);
fi.SetValue(str, x);
}
else if(fi.FieldType == typeof(ushort) ||
fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(ushort))
{
var x = (ushort)(fi.GetValue(str) ?? default(ushort));
fi.SetValue(str, (ushort)(x << 8 | x >> 8));
}
else if(fi.FieldType == typeof(uint) ||
fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(uint))
{
var x = (uint)(fi.GetValue(str) ?? default(uint));
x = x << 8 & 0xFF00FF00 | x >> 8 & 0xFF00FF;
fi.SetValue(str, x << 16 | x >> 16);
}
else if(fi.FieldType == typeof(ulong) ||
fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(ulong))
{
var x = (ulong)(fi.GetValue(str) ?? default(ulong));
x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32;
x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16;
x = (x & 0x00FF00FF00FF00FF) << 8 | (x & 0xFF00FF00FF00FF00) >> 8;
fi.SetValue(str, x);
}
else if(fi.FieldType == typeof(float))
{
var flt = (float)(fi.GetValue(str) ?? default(float));
byte[] flt_b = BitConverter.GetBytes(flt);
fi.SetValue(str, BitConverter.ToSingle(new[]
{
flt_b[3], flt_b[2], flt_b[1], flt_b[0]
}, 0));
}
else if(fi.FieldType == typeof(double))
{
var dbl = (double)(fi.GetValue(str) ?? default(double));
byte[] dbl_b = BitConverter.GetBytes(dbl);
fi.SetValue(str, BitConverter.ToDouble(new[]
{
dbl_b[7], dbl_b[6], dbl_b[5], dbl_b[4], dbl_b[3], dbl_b[2], dbl_b[1], dbl_b[0]
}, 0));
}
else if(fi.FieldType == typeof(byte) || fi.FieldType == typeof(sbyte))
{
// Do nothing, can't byteswap them!
}
else if(fi.FieldType == typeof(Guid))
{
// TODO: Swap GUID
}
// TODO: Swap arrays
else if(fi.FieldType.IsValueType && fi.FieldType is { IsEnum: false, IsArray: false })
{
object obj = fi.GetValue(str);
object strc = SwapStructureMembersEndian(obj);
fi.SetValue(str, strc);
}
}
return str;
}
/// <summary>Swaps all fields in an structure considering them to follow PDP endian conventions</summary>
/// <param name="str">Source structure</param>
/// <returns>Resulting structure</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static object SwapStructureMembersEndianPdp(object str)
{
Type t = str.GetType();
FieldInfo[] fieldInfo = t.GetFields();
foreach(FieldInfo fi in fieldInfo)
{
if(fi.FieldType == typeof(short) ||
fi.FieldType == typeof(long) ||
fi.FieldType == typeof(ushort) ||
fi.FieldType == typeof(ulong) ||
fi.FieldType == typeof(float) ||
fi.FieldType == typeof(double) ||
fi.FieldType == typeof(byte) ||
fi.FieldType == typeof(sbyte) ||
fi.FieldType == typeof(Guid))
{
// Do nothing
}
else if(fi.FieldType == typeof(int) ||
fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(int))
{
var x = (int)(fi.GetValue(str) ?? default(int));
fi.SetValue(str, (x & 0xffffu) << 16 | (x & 0xffff0000u) >> 16);
}
else if(fi.FieldType == typeof(uint) ||
fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(uint))
{
var x = (uint)(fi.GetValue(str) ?? default(uint));
fi.SetValue(str, (x & 0xffffu) << 16 | (x & 0xffff0000u) >> 16);
}
// TODO: Swap arrays
else if(fi.FieldType.IsValueType && fi.FieldType is { IsEnum: false, IsArray: false })
{
object obj = fi.GetValue(str);
object strc = SwapStructureMembersEndianPdp(obj);
fi.SetValue(str, strc);
}
}
return str;
}
/// <summary>Marshal a structure to little-endian binary data</summary>
/// <param name="str">The structure you want to marshal to binary</param>
/// <typeparam name="T">Type of the structure to marshal</typeparam>
/// <returns>The byte array representing the given structure</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte[] StructureToByteArrayLittleEndian<T>(T str) where T : struct
{
var buf = new byte[SizeOf<T>()];
var ptr = GCHandle.Alloc(buf, GCHandleType.Pinned);
System.Runtime.InteropServices.Marshal.StructureToPtr(str, ptr.AddrOfPinnedObject(), false);
ptr.Free();
return buf;
}
/// <summary>Marshal a structure to little-endian binary data</summary>
/// <param name="str">The structure you want to marshal to binary</param>
/// <typeparam name="T">Type of the structure to marshal</typeparam>
/// <returns>The byte array representing the given structure</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte[] StructureToByteArrayBigEndian<T>(T str) where T : struct =>
StructureToByteArrayLittleEndian((T)SwapStructureMembersEndian(str));
/// <summary>Converts a hexadecimal string into a byte array</summary>
/// <param name="hex">Hexadecimal string</param>
/// <param name="outBuf">Resulting byte array</param>
/// <returns>Number of output bytes processed</returns>
public static int ConvertFromHexAscii(string hex, out byte[] outBuf)
{
outBuf = null;
if(hex is null or "")
return -1;
var off = 0;
if(hex[0] == '0' && (hex[1] == 'x' || hex[1] == 'X'))
off = 2;
outBuf = new byte[(hex.Length - off) / 2];
var count = 0;
for(int i = off; i < hex.Length; i += 2)
{
char c = hex[i];
if(c is < '0' or > '9' and < 'A' or > 'F' and < 'a' or > 'f')
break;
c -= c switch
{
>= 'a' and <= 'f' => '\u0057',
>= 'A' and <= 'F' => '\u0037',
_ => '\u0030'
};
outBuf[(i - off) / 2] = (byte)(c << 4);
c = hex[i + 1];
if(c is < '0' or > '9' and < 'A' or > 'F' and < 'a' or > 'f')
break;
c -= c switch
{
>= 'a' and <= 'f' => '\u0057',
>= 'A' and <= 'F' => '\u0037',
_ => '\u0030'
};
outBuf[(i - off) / 2] += (byte)c;
count++;
}
return count;
}
}

View File

@@ -0,0 +1,62 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : MarshallingPropertiesAttribute.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Common types.
//
// --[ Description ] ----------------------------------------------------------
//
// Declares properties of structs for marshalling.
//
// --[ License ] --------------------------------------------------------------
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
using System;
namespace Aaru.Helpers;
/// <inheritdoc />
/// <summary>Defines properties to help marshalling structs from binary data</summary>
[AttributeUsage(AttributeTargets.Struct)]
public sealed class MarshallingPropertiesAttribute : Attribute
{
/// <inheritdoc />
/// <summary>Defines properties to help marshalling structs from binary data</summary>
/// <param name="endian">Defines properties to help marshalling structs from binary data</param>
public MarshallingPropertiesAttribute(BitEndian endian)
{
Endian = endian;
HasReferences = true;
}
/// <summary>c</summary>
public BitEndian Endian { get; }
/// <summary>Tells if the structure, or any nested structure, has any non-value type (e.g. arrays, strings, etc).</summary>
public bool HasReferences { get; set; }
}

139
Aaru.Helpers/PrintHex.cs Normal file
View File

@@ -0,0 +1,139 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : PrintHex.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Helpers.
//
// --[ Description ] ----------------------------------------------------------
//
// Prints a byte array as hexadecimal.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
using System.Text;
using Aaru.Console;
namespace Aaru.Helpers;
/// <summary>Helper operations to get hexadecimal representations of byte arrays</summary>
public static class PrintHex
{
/// <summary>Prints a byte array as hexadecimal values to the console</summary>
/// <param name="array">Array</param>
/// <param name="width">Width of line</param>
public static void PrintHexArray(byte[] array, int width = 16) =>
AaruConsole.WriteLine(ByteArrayToHexArrayString(array, width));
/// <summary>Prints a byte array as hexadecimal values to a string</summary>
/// <param name="array">Array</param>
/// <param name="width">Width of line</param>
/// <param name="color">Use ANSI escape colors for sections</param>
/// <returns>String containing hexadecimal values</returns>
public static string ByteArrayToHexArrayString(byte[] array, int width = 16, bool color = false)
{
if(array is null)
return null;
// TODO: Color list
// TODO: Allow to change width
string str = Localization.Offset;
int rows = array.Length / width;
int last = array.Length % width;
int offsetLength = $"{array.Length:X}".Length;
var sb = new StringBuilder();
switch(last)
{
case > 0:
rows++;
break;
case 0:
last = width;
break;
}
if(offsetLength < str.Length)
offsetLength = str.Length;
while(str.Length < offsetLength)
str += ' ';
if(color)
sb.Append("\u001b[36m");
sb.Append(str);
sb.Append(" ");
for(var i = 0; i < width; i++)
sb.Append($" {i:X2}");
if(color)
sb.Append("\u001b[0m");
sb.AppendLine();
var b = 0;
var format = $"{{0:X{offsetLength}}}";
for(var i = 0; i < rows; i++)
{
if(color)
sb.Append("\u001b[36m");
sb.AppendFormat(format, b);
if(color)
sb.Append("\u001b[0m");
sb.Append(" ");
int lastBytes = i == rows - 1 ? last : width;
int lastSpaces = width - lastBytes;
for(var j = 0; j < lastBytes; j++)
{
sb.Append($" {array[b]:X2}");
b++;
}
for(var j = 0; j < lastSpaces; j++)
sb.Append(" ");
b -= lastBytes;
sb.Append(" ");
for(var j = 0; j < lastBytes; j++)
{
int v = array[b];
sb.Append(v is > 31 and < 127 or > 159 ? (char)v : '.');
b++;
}
sb.AppendLine();
}
return sb.ToString();
}
}

View File

@@ -0,0 +1,187 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : StringHandlers.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Helpers.
//
// --[ Description ] ----------------------------------------------------------
//
// Convert byte arrays to C# strings.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace Aaru.Helpers;
/// <summary>Helper operations to work with strings</summary>
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static class StringHandlers
{
/// <summary>Converts a null-terminated (aka C string) ASCII byte array to a C# string</summary>
/// <returns>The corresponding C# string</returns>
/// <param name="cString">A null-terminated (aka C string) ASCII byte array</param>
public static string CToString(byte[] cString) => CToString(cString, Encoding.ASCII);
/// <summary>Converts a null-terminated (aka C string) byte array with the specified encoding to a C# string</summary>
/// <returns>The corresponding C# string</returns>
/// <param name="cString">A null-terminated (aka C string) byte array in the specified encoding</param>
/// <param name="encoding">Encoding.</param>
/// <param name="twoBytes">Set if encoding uses 16-bit characters.</param>
/// <param name="start">Start decoding at this position</param>
public static string CToString(byte[] cString, Encoding encoding, bool twoBytes = false, int start = 0)
{
if(cString == null)
return null;
var len = 0;
for(int i = start; i < cString.Length; i++)
{
if(cString[i] == 0)
{
if(twoBytes)
{
if(i + 1 < cString.Length && cString[i + 1] == 0)
{
len++;
break;
}
}
else
break;
}
len++;
}
if(twoBytes && len % 2 > 0)
len--;
var dest = new byte[len];
Array.Copy(cString, start, dest, 0, len);
return len == 0 ? "" : encoding.GetString(dest);
}
/// <summary>Converts a length-prefixed (aka Pascal string) ASCII byte array to a C# string</summary>
/// <returns>The corresponding C# string</returns>
/// <param name="pascalString">A length-prefixed (aka Pascal string) ASCII byte array</param>
public static string PascalToString(byte[] pascalString) => PascalToString(pascalString, Encoding.ASCII);
/// <summary>Converts a length-prefixed (aka Pascal string) ASCII byte array to a C# string</summary>
/// <returns>The corresponding C# string</returns>
/// <param name="pascalString">A length-prefixed (aka Pascal string) ASCII byte array</param>
/// <param name="encoding">Encoding.</param>
/// <param name="start">Start decoding at this position</param>
public static string PascalToString(byte[] pascalString, Encoding encoding, int start = 0)
{
if(pascalString == null)
return null;
byte length = pascalString[start];
var len = 0;
for(int i = start + 1; i < length + 1 && i < pascalString.Length; i++)
{
if(pascalString[i] == 0)
break;
len++;
}
var dest = new byte[len];
Array.Copy(pascalString, start + 1, dest, 0, len);
return len == 0 ? "" : encoding.GetString(dest);
}
/// <summary>Converts a space (' ', 0x20, ASCII SPACE) padded ASCII byte array to a C# string</summary>
/// <returns>The corresponding C# string</returns>
/// <param name="spacePaddedString">A space (' ', 0x20, ASCII SPACE) padded ASCII byte array</param>
public static string SpacePaddedToString(byte[] spacePaddedString) =>
SpacePaddedToString(spacePaddedString, Encoding.ASCII);
/// <summary>Converts a space (' ', 0x20, ASCII SPACE) padded ASCII byte array to a C# string</summary>
/// <returns>The corresponding C# string</returns>
/// <param name="spacePaddedString">A space (' ', 0x20, ASCII SPACE) padded ASCII byte array</param>
/// <param name="encoding">Encoding.</param>
/// <param name="start">Start decoding at this position</param>
public static string SpacePaddedToString(byte[] spacePaddedString, Encoding encoding, int start = 0)
{
if(spacePaddedString == null)
return null;
int len = start;
for(int i = spacePaddedString.Length; i >= start; i--)
{
if(i == start)
return "";
if(spacePaddedString[i - 1] == 0x20)
continue;
len = i;
break;
}
return len == 0 ? "" : encoding.GetString(spacePaddedString, start, len);
}
/// <summary>Converts an OSTA compressed unicode byte array to a C# string</summary>
/// <returns>The C# string.</returns>
/// <param name="dstring">OSTA compressed unicode byte array.</param>
public static string DecompressUnicode(byte[] dstring)
{
byte compId = dstring[0];
var temp = "";
if(compId != 8 && compId != 16)
return null;
for(var byteIndex = 1; byteIndex < dstring.Length;)
{
ushort unicode;
if(compId == 16)
unicode = (ushort)(dstring[byteIndex++] << 8);
else
unicode = 0;
if(byteIndex < dstring.Length)
unicode |= dstring[byteIndex++];
if(unicode == 0)
break;
temp += Encoding.Unicode.GetString(BitConverter.GetBytes(unicode));
}
return temp;
}
}

113
Aaru.Helpers/Swapping.cs Normal file
View File

@@ -0,0 +1,113 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Swapping.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Helpers.
//
// --[ Description ] ----------------------------------------------------------
//
// Byte-swapping methods.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace Aaru.Helpers;
/// <summary>Helper operations to work with swapping endians</summary>
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static class Swapping
{
/// <summary>Gets the PDP endian equivalent of the given little endian unsigned integer</summary>
/// <param name="x">Little endian unsigned integer</param>
/// <returns>PDP unsigned integer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint PDPFromLittleEndian(uint x) => (x & 0xffff) << 16 | (x & 0xffff0000) >> 16;
/// <summary>Gets the PDP endian equivalent of the given big endian unsigned integer</summary>
/// <param name="x">Big endian unsigned integer</param>
/// <returns>PDP unsigned integer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint PDPFromBigEndian(uint x) => (x & 0xff00ff) << 8 | (x & 0xff00ff00) >> 8;
/// <summary>Swaps the endian of the specified unsigned short integer</summary>
/// <param name="x">Unsigned short integer</param>
/// <returns>Swapped unsigned short integer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort Swap(ushort x) => (ushort)(x << 8 | x >> 8);
/// <summary>Swaps the endian of the specified signed short integer</summary>
/// <param name="x">Signed short integer</param>
/// <returns>Swapped signed short integer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short Swap(short x) => (short)(x << 8 | x >> 8 & 0xFF);
/// <summary>Swaps the endian of the specified unsigned integer</summary>
/// <param name="x">Unsigned integer</param>
/// <returns>Swapped unsigned integer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Swap(uint x)
{
x = x << 8 & 0xFF00FF00 | x >> 8 & 0xFF00FF;
return x << 16 | x >> 16;
}
/// <summary>Swaps the endian of the specified signed integer</summary>
/// <param name="x">Signed integer</param>
/// <returns>Swapped signed integer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Swap(int x)
{
x = (int)(x << 8 & 0xFF00FF00 | (uint)x >> 8 & 0xFF00FF);
return (int)((uint)x << 16 | (uint)x >> 16 & 0xFFFF);
}
/// <summary>Swaps the endian of the specified unsigned long integer</summary>
/// <param name="x">Unsigned long integer</param>
/// <returns>Swapped unsigned long integer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Swap(ulong x)
{
x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32;
x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16;
x = (x & 0x00FF00FF00FF00FF) << 8 | (x & 0xFF00FF00FF00FF00) >> 8;
return x;
}
/// <summary>Swaps the endian of the specified signed long integer</summary>
/// <param name="x">Signed long integer</param>
/// <returns>Swapped signed long integer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long Swap(long x)
{
x = (x & 0x00000000FFFFFFFF) << 32 | (long)(((ulong)x & 0xFFFFFFFF00000000) >> 32);
x = (x & 0x0000FFFF0000FFFF) << 16 | (long)(((ulong)x & 0xFFFF0000FFFF0000) >> 16);
x = (x & 0x00FF00FF00FF00FF) << 8 | (long)(((ulong)x & 0xFF00FF00FF00FF00) >> 8);
return x;
}
}