mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
Move all files into project subdirectory
This commit is contained in:
1342
Aaru.Helpers/.editorconfig
Normal file
1342
Aaru.Helpers/.editorconfig
Normal file
File diff suppressed because it is too large
Load Diff
595
Aaru.Helpers/.gitignore
vendored
Normal file
595
Aaru.Helpers/.gitignore
vendored
Normal 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
|
||||
60
Aaru.Helpers/Aaru.Helpers.csproj
Normal file
60
Aaru.Helpers/Aaru.Helpers.csproj
Normal 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 <claunia@claunia.com></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>
|
||||
5
Aaru.Helpers/Aaru.Helpers.csproj.DotSettings
Normal file
5
Aaru.Helpers/Aaru.Helpers.csproj.DotSettings
Normal 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
79
Aaru.Helpers/ArrayFill.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
49
Aaru.Helpers/ArrayIsEmpty.cs
Normal file
49
Aaru.Helpers/ArrayIsEmpty.cs
Normal 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;
|
||||
}
|
||||
323
Aaru.Helpers/BigEndianBitConverter.cs
Normal file
323
Aaru.Helpers/BigEndianBitConverter.cs
Normal 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
50
Aaru.Helpers/BitEndian.cs
Normal 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
49
Aaru.Helpers/CHS.cs
Normal 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;
|
||||
}
|
||||
72
Aaru.Helpers/CompareBytes.cs
Normal file
72
Aaru.Helpers/CompareBytes.cs
Normal 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
48
Aaru.Helpers/CountBits.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
386
Aaru.Helpers/DateHandlers.cs
Normal file
386
Aaru.Helpers/DateHandlers.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
72
Aaru.Helpers/Extensions.cs
Normal file
72
Aaru.Helpers/Extensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
257
Aaru.Helpers/IO/ForcedSeekStream.cs
Normal file
257
Aaru.Helpers/IO/ForcedSeekStream.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
78
Aaru.Helpers/IO/NonClosableStream.cs
Normal file
78
Aaru.Helpers/IO/NonClosableStream.cs
Normal 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();
|
||||
}
|
||||
680
Aaru.Helpers/IO/OffsetStream.cs
Normal file
680
Aaru.Helpers/IO/OffsetStream.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
372
Aaru.Helpers/IO/SplitJoinStream.cs
Normal file
372
Aaru.Helpers/IO/SplitJoinStream.cs
Normal 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);
|
||||
}
|
||||
197
Aaru.Helpers/Localization/Localization.Designer.cs
generated
Normal file
197
Aaru.Helpers/Localization/Localization.Designer.cs
generated
Normal 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'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'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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
64
Aaru.Helpers/Localization/Localization.es.resx
Normal file
64
Aaru.Helpers/Localization/Localization.es.resx
Normal 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>
|
||||
71
Aaru.Helpers/Localization/Localization.resx
Normal file
71
Aaru.Helpers/Localization/Localization.resx
Normal 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
492
Aaru.Helpers/Marshal.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
62
Aaru.Helpers/MarshallingPropertiesAttribute.cs
Normal file
62
Aaru.Helpers/MarshallingPropertiesAttribute.cs
Normal 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
139
Aaru.Helpers/PrintHex.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
187
Aaru.Helpers/StringHandlers.cs
Normal file
187
Aaru.Helpers/StringHandlers.cs
Normal 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
113
Aaru.Helpers/Swapping.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user