New: Updated MediaInfo to 21.09, Sqlite to 3.32.1.0 and added support for mac osx arm64 arch. Deprecated osx x86.

This commit is contained in:
Taloth Saldono 2021-11-03 22:27:31 +01:00
parent 3a8bd451a9
commit 201004113e
26 changed files with 586 additions and 13578 deletions

View File

@ -12,8 +12,11 @@ sourceFolder='./src'
slnFile=$sourceFolder/Sonarr.sln slnFile=$sourceFolder/Sonarr.sln
updateSubFolder=Sonarr.Update updateSubFolder=Sonarr.Update
sqlitePackageDir="$HOME/.nuget/packages/system.data.sqlite.core.servarr/1.0.115.5-18"
nuget='tools/nuget/nuget.exe'; nuget='tools/nuget/nuget.exe';
vswhere='tools/vswhere/vswhere.exe'; vswhere='tools/vswhere/vswhere.exe';
macho='tools/macho/MachOConverter.exe';
. ./version.sh . ./version.sh
@ -135,6 +138,9 @@ Build()
CleanFolder $outputFolder false CleanFolder $outputFolder false
echo "Removing Sonarr.Update/sqlite3.dll"
rm $outputFolder/Sonarr.Update/sqlite3.dll
echo "Removing Mono.Posix.dll" echo "Removing Mono.Posix.dll"
rm $outputFolder/Mono.Posix.dll rm $outputFolder/Mono.Posix.dll
@ -234,6 +240,7 @@ PackageMono()
echo "Removing native windows binaries Sqlite, MediaInfo" echo "Removing native windows binaries Sqlite, MediaInfo"
rm -f $outputFolderLinux/sqlite3.* rm -f $outputFolderLinux/sqlite3.*
rm -f $outputFolderLinux/Sonarr.Update/sqlite3.*
rm -f $outputFolderLinux/MediaInfo.* rm -f $outputFolderLinux/MediaInfo.*
PatchMono $outputFolderLinux PatchMono $outputFolderLinux
@ -278,17 +285,18 @@ PackageMacOS()
chmod +x $outputFolderMacOS/Sonarr chmod +x $outputFolderMacOS/Sonarr
echo "Adding Sonarr.Update Launcher" echo "Adding Sonarr.Update Launcher"
cp ./distribution/osx/Launcher/dist/Launcher $outputFolderMacOS/Sonarr.Update/ CheckExitCode cp ./distribution/osx/Launcher/dist/Launcher $outputFolderMacOS/Sonarr.Update/
mv $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe.bak CheckExitCode mv $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe.bak
mv $outputFolderMacOS/Sonarr.Update/Launcher $outputFolderMacOS/Sonarr.Update/Sonarr.Update CheckExitCode mv $outputFolderMacOS/Sonarr.Update/Launcher $outputFolderMacOS/Sonarr.Update/Sonarr.Update
mv $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe.bak $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe CheckExitCode mv $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe.bak $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe
chmod +x $outputFolderMacOS/Sonarr.Update/Sonarr.Update chmod +x $outputFolderMacOS/Sonarr.Update/Sonarr.Update
echo "Adding sqlite dylibs" echo "Adding sqlite dylib"
cp $sourceFolder/Libraries/Sqlite/*.dylib $outputFolderMacOS CheckExitCode cp "$sqlitePackageDir/runtimes/osx-x64/native/net46"/*.config $outputFolderMacOS/
CheckExitCode $macho merge $outputFolderMacOS "$sqlitePackageDir/runtimes/osx-x64/native/net46"/*.dylib "$sqlitePackageDir/runtimes/osx-arm64/native/net46"/*.dylib
echo "Adding MediaInfo dylib" echo "Adding MediaInfo dylib"
cp $sourceFolder/Libraries/MediaInfo/*.dylib $outputFolderMacOS CheckExitCode cp $sourceFolder/Libraries/MediaInfo/x64/*.dylib $outputFolderMacOS/
ProgressEnd 'Creating MacOS Package' ProgressEnd 'Creating MacOS Package'
} }
@ -297,29 +305,32 @@ PackageMacOSApp()
{ {
ProgressStart 'Creating macOS App Package' ProgressStart 'Creating macOS App Package'
outputFolderMacOSAppBase=$outputFolderMacOSApp/Sonarr.app/Contents/MacOS
outputFolderMacOSAppBin=$outputFolderMacOSAppBase/bin
rm -rf $outputFolderMacOSApp rm -rf $outputFolderMacOSApp
mkdir $outputFolderMacOSApp mkdir $outputFolderMacOSApp
cp -r ./distribution/osx/Sonarr.app $outputFolderMacOSApp cp -r ./distribution/osx/Sonarr.app $outputFolderMacOSApp
mkdir -p $outputFolderMacOSApp/Sonarr.app/Contents/MacOS mkdir -p $outputFolderMacOSAppBase
echo "Adding Sonarr Launcher" echo "Adding Sonarr Launcher"
cp ./distribution/osx/Launcher/dist/Launcher $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/ CheckExitCode cp ./distribution/osx/Launcher/dist/Launcher $outputFolderMacOSAppBase/
mv $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/Launcher $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/Sonarr CheckExitCode mv $outputFolderMacOSAppBase/Launcher $outputFolderMacOSAppBase/Sonarr
chmod +x $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/Sonarr chmod +x $outputFolderMacOSAppBase/Sonarr
echo "Copying Binaries" echo "Copying Binaries"
mkdir -p $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/bin mkdir -p $outputFolderMacOSAppBin
cp -r $outputFolderLinux/* $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/bin/ CheckExitCode cp -r $outputFolderLinux/* $outputFolderMacOSAppBin
echo "Adding sqlite dylibs" echo "Adding sqlite dylib"
cp $sourceFolder/Libraries/Sqlite/*.dylib $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/bin/ CheckExitCode $macho merge $outputFolderMacOSAppBin "$sqlitePackageDir/runtimes/osx-x64/native/net46" "$sqlitePackageDir/runtimes/osx-arm64/native/net46"
echo "Adding MediaInfo dylib" echo "Adding MediaInfo dylib"
cp $sourceFolder/Libraries/MediaInfo/*.dylib $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/bin/ CheckExitCode cp $sourceFolder/Libraries/MediaInfo/x64/*.dylib $outputFolderMacOSAppBin/
echo "Removing Update Folder" echo "Removing Update Folder"
rm -r $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/bin/Sonarr.Update rm -r $outputFolderMacOSAppBin/Sonarr.Update
echo "# Do Not Edit\nPackageVersion=${BUILD_NUMBER}\nPackageAuthor=[Team Sonarr](https://sonarr.tv)\nReleaseVersion=${BUILD_NUMBER}\nUpdateMethod=$PackageUpdater\nBranch=${Branch:-master}" > $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/package_info echo "# Do Not Edit\nPackageVersion=${BUILD_NUMBER}\nPackageAuthor=[Team Sonarr](https://sonarr.tv)\nReleaseVersion=${BUILD_NUMBER}\nUpdateMethod=$PackageUpdater\nBranch=${Branch:-master}" > $outputFolderMacOSAppBase/package_info
ProgressEnd 'Creating macOS App Package' ProgressEnd 'Creating macOS App Package'
} }

View File

@ -3,6 +3,9 @@
<PropertyGroup> <PropertyGroup>
<SonarrRootDir>$(MSBuildThisFileDirectory)..\</SonarrRootDir> <SonarrRootDir>$(MSBuildThisFileDirectory)..\</SonarrRootDir>
<!-- Whether we want TargetFramework switching in Visual Studio -->
<TargetMultiple>false</TargetMultiple>
<!-- Specifies the type of output --> <!-- Specifies the type of output -->
<SonarrOutputType>Library</SonarrOutputType> <SonarrOutputType>Library</SonarrOutputType>
<SonarrOutputType Condition="$(MSBuildProjectName.Contains('.Test'))">Test</SonarrOutputType> <SonarrOutputType Condition="$(MSBuildProjectName.Contains('.Test'))">Test</SonarrOutputType>
@ -19,17 +22,34 @@
<SonarrProject Condition="$(MSBuildProjectName.StartsWith('ServiceUninstall'))">true</SonarrProject> <SonarrProject Condition="$(MSBuildProjectName.StartsWith('ServiceUninstall'))">true</SonarrProject>
</PropertyGroup> </PropertyGroup>
<!-- Pick default RuntimeIdentifier for our compilation environment -->
<PropertyGroup Condition=" '$(TargetMultiple)'!='true' ">
<RuntimeIdentifiers></RuntimeIdentifiers>
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
<TargetFrameworks></TargetFrameworks>
<TargetFramework>net462</TargetFramework>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<Configuration Condition="'$(Configuration)'==''">Release</Configuration> <Configuration Condition="'$(Configuration)'==''">Release</Configuration>
<TargetFrameworkPath>$(TargetFramework)\</TargetFrameworkPath>
<TargetFrameworkPath Condition="'$(Platform)'!='x64'">$(TargetFrameworkPath)$(Platform)\</TargetFrameworkPath>
<TargetFrameworkPath>$(TargetFrameworkPath)$(Configuration)\</TargetFrameworkPath>
<!-- Centralize intermediate and default outputs --> <!-- Centralize intermediate and default outputs -->
<BaseIntermediateOutputPath>$(SonarrRootDir)_temp\obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath> <BaseIntermediateOutputPath>$(SonarrRootDir)_temp\obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(SonarrRootDir)_temp\obj\$(MSBuildProjectName)\$(Configuration)\</IntermediateOutputPath> <IntermediateOutputPath>$(SonarrRootDir)_temp\obj\$(MSBuildProjectName)\$(TargetFrameworkPath)</IntermediateOutputPath>
<OutputPath>$(SonarrRootDir)_temp\bin\$(Configuration)\$(MSBuildProjectName)\</OutputPath> <OutputPath>$(SonarrRootDir)_temp\bin\$(TargetFrameworkPath)$(MSBuildProjectName)\</OutputPath>
<!-- Flatten the 'real' output paths for backward compat -->
<OutputTargetFrameworkPath>$(TargetFrameworkPath)</OutputTargetFrameworkPath>
<OutputTargetFrameworkPath Condition=" '$(TargetMultiple)'!='true' "></OutputTargetFrameworkPath>
<!-- Output to _output and _tests respectively --> <!-- Output to _output and _tests respectively -->
<OutputPath Condition="'$(SonarrProject)'=='true'">$(SonarrRootDir)_output\</OutputPath> <OutputPath Condition="'$(SonarrProject)'=='true'">$(SonarrRootDir)_output\$(OutputTargetFrameworkPath)</OutputPath>
<OutputPath Condition="'$(SonarrOutputType)'=='Test'">$(SonarrRootDir)_tests\</OutputPath> <OutputPath Condition="'$(SonarrOutputType)'=='Test'">$(SonarrRootDir)_tests\$(OutputTargetFrameworkPath)</OutputPath>
<OutputPath Condition="'$(SonarrOutputType)'=='Update'">$(SonarrRootDir)_output\Sonarr.Update\</OutputPath> <OutputPath Condition="'$(SonarrOutputType)'=='Update'">$(OutputPath)Sonarr.Update\</OutputPath>
<!-- Paths relative to project file for better readability --> <!-- Paths relative to project file for better readability -->
<BaseIntermediateOutputPath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(BaseIntermediateOutputPath)'))</BaseIntermediateOutputPath> <BaseIntermediateOutputPath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(BaseIntermediateOutputPath)'))</BaseIntermediateOutputPath>
@ -73,6 +93,7 @@
<PropertyGroup> <PropertyGroup>
<!-- We don't want separate framework directories till we go dotnet core --> <!-- We don't want separate framework directories till we go dotnet core -->
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<!-- For now keep the NzbDrone namespace --> <!-- For now keep the NzbDrone namespace -->
<RootNamespace Condition="'$(SonarrProject)'=='true'">$(MSBuildProjectName.Replace('Sonarr','NzbDrone'))</RootNamespace> <RootNamespace Condition="'$(SonarrProject)'=='true'">$(MSBuildProjectName.Replace('Sonarr','NzbDrone'))</RootNamespace>

Binary file not shown.

View File

@ -0,0 +1,3 @@
File OS Arch Version
libmediainfo.0.dylib Mac x64,arm64 21.09

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,4 @@
File OS Arch Version
libmediainfo.0.dylib Mac x64,arm64 21.09
MediaInfo.dll Win x64 21.09

Binary file not shown.

View File

@ -0,0 +1,4 @@
File OS Arch Version
libmediainfo.0.dylib Mac x86,x64 21.03
MediaInfo.dll Win x86 21.09

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
libsqlite3.0.dylib

Binary file not shown.

6
src/NuGet.config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="Lidarr SQLite" value="https://pkgs.dev.azure.com/Servarr/Servarr/_packaging/SQLite/nuget/v3/index.json" />
</packageSources>
</configuration>

View File

@ -9,6 +9,7 @@
<PackageReference Include="NLog" Version="4.6.6" /> <PackageReference Include="NLog" Version="4.6.6" />
<PackageReference Include="Sentry" Version="1.2.0" /> <PackageReference Include="Sentry" Version="1.2.0" />
<PackageReference Include="SharpZipLib" Version="1.2.0" /> <PackageReference Include="SharpZipLib" Version="1.2.0" />
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> <PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -16,9 +17,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System.Configuration.Install" /> <Reference Include="System.Configuration.Install" />
<Reference Include="System.Data.SQLite">
<HintPath>..\Libraries\Sqlite\System.Data.SQLite.dll</HintPath>
</Reference>
<Reference Include="System.ServiceProcess" /> <Reference Include="System.ServiceProcess" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -15,9 +15,6 @@
<Link>Files\1024.png</Link> <Link>Files\1024.png</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<Reference Include="System.Data.SQLite">
<HintPath>..\Libraries\Sqlite\System.Data.SQLite.dll</HintPath>
</Reference>
<None Update="Files\**\*.*"> <None Update="Files\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>

View File

@ -22,9 +22,6 @@
<ProjectReference Include="..\NzbDrone.Common\Sonarr.Common.csproj" /> <ProjectReference Include="..\NzbDrone.Common\Sonarr.Common.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System.Data.SQLite">
<HintPath>..\Libraries\Sqlite\System.Data.SQLite.dll</HintPath>
</Reference>
<Reference Include="System.Web" /> <Reference Include="System.Web" />
<Reference Include="System.Web.Extensions" /> <Reference Include="System.Web.Extensions" />
</ItemGroup> </ItemGroup>
@ -32,10 +29,10 @@
<EmbeddedResource Include="..\..\Logo\64.png"> <EmbeddedResource Include="..\..\Logo\64.png">
<Link>Resources\Logo\64.png</Link> <Link>Resources\Logo\64.png</Link>
</EmbeddedResource> </EmbeddedResource>
<None Include="..\Libraries\Sqlite\sqlite3.dll"> <None Include="..\Libraries\MediaInfo\$(Platform)\MediaInfo.dll" Condition="$(RuntimeIdentifier.StartsWith('win')) or '$(RuntimeIdentifier)'==''">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="..\Libraries\MediaInfo\MediaInfo.dll"> <None Include="..\Libraries\MediaInfo\$(Platform)\libmediainfo.0.dylib" Condition="$(RuntimeIdentifier.StartsWith('osx'))">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>

View File

@ -4,4 +4,8 @@
<TargetFramework>net462</TargetFramework> <TargetFramework>net462</TargetFramework>
<Platforms>x86</Platforms> <Platforms>x86</Platforms>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<!-- Workaround to ensure sqlite3.dll is copied -->
<ProjectReference Include="..\NzbDrone.Common\Sonarr.Common.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -16,8 +16,5 @@
<ProjectReference Include="..\NzbDrone.SignalR\Sonarr.SignalR.csproj" /> <ProjectReference Include="..\NzbDrone.SignalR\Sonarr.SignalR.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System.Data.SQLite">
<HintPath>..\Libraries\Sqlite\System.Data.SQLite.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -53,6 +53,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sonarr.Common", "NzbDrone.C
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{1E6B3CBE-1578-41C1-9BF9-78D818740BE9}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{1E6B3CBE-1578-41C1-9BF9-78D818740BE9}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
NuGet.config = NuGet.config
.nuget\NuGet.exe = .nuget\NuGet.exe .nuget\NuGet.exe = .nuget\NuGet.exe
EndProjectSection EndProjectSection
EndProject EndProject

View File

@ -0,0 +1,497 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace MachOConverter
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 1)
{
PrintUsage();
return;
}
if (args[0] == "info" && args.Length >= 2)
{
for (var i = 1; i < args.Length; i++)
{
PrintInfo(args[i]);
}
}
else if (args[0] == "split" && args.Length >= 2)
{
for (var i = 1; i < args.Length; i++)
{
SplitFile(args[i]);
}
}
else if (args[0] == "merge" && args.Length >= 4)
{
var sources = args.Skip(2).ToList();
if (sources.Any(Directory.Exists))
MergeDir(args[1], args.Skip(2).ToList());
else
MergeFile(args[1], args.Skip(2).ToList());
}
else
{
PrintUsage();
}
}
static void PrintUsage()
{
var path = Path.GetFileName(Assembly.GetExecutingAssembly().Location);
Console.WriteLine($"Usage: {path} info [path]");
Console.WriteLine($" {path} split [source]");
Console.WriteLine($" {path} merge [target] [source1] [source2]");
}
static void PrintInfo(string path)
{
var file = new MachOFile(path);
}
static void SplitFile(string path)
{
var file = new MachOFile(path);
foreach (var entry in file.FatEntries)
{
var newPath = Path.ChangeExtension(path, "." + entry.cputype.ToString() + Path.GetExtension(path));
using (var src = new FileStream(path, FileMode.Open, FileAccess.Read))
using (var dst = new FileStream(newPath, FileMode.Create, FileAccess.Write))
{
src.Seek(entry.offset, SeekOrigin.Begin);
var remaining = (int)entry.size;
var buf = new byte[64 * 1024];
while (remaining != 0)
{
var size = Math.Min(remaining, buf.Length);
src.Read(buf, 0, size);
dst.Write(buf, 0, size);
remaining -= size;
}
}
Console.WriteLine($"Wrote {entry.cputype} to {newPath}");
}
}
static void MergeDir(string outPath, List<string> sources)
{
if (!Directory.Exists(outPath))
Directory.CreateDirectory(outPath);
var subdirs = sources.SelectMany(Directory.GetDirectories).Select(Path.GetFileName).Distinct().ToList();
var files = sources.SelectMany(Directory.GetFiles).Select(Path.GetFileName).Distinct().ToList();
foreach (var subdir in subdirs)
{
MergeDir(Path.Combine(outPath, subdir), sources.ConvertAll(v => Path.Combine(v, subdir)).Where(Directory.Exists).ToList());
}
foreach (var file in files)
{
MergeFile(Path.Combine(outPath, file), sources.ConvertAll(v => Path.Combine(v, file)).Where(File.Exists).ToList());
}
}
static void MergeFile(string outPath, List<string> sources)
{
if (Directory.Exists(outPath))
{
outPath = Path.Combine(outPath, Path.GetFileName(sources[0]));
}
if (!MachOFile.IsValidFile(sources[0]))
{
File.Copy(sources[0], outPath);
return;
}
var sourceItems = sources.ConvertAll(v => new MachOFile(v));
var outFile = new MachOFile(outPath, true);
sourceItems.ForEach(outFile.AppendFile);
outFile.Write();
}
}
class MachOFile
{
[Flags]
public enum MachOCpuType : uint
{
VAX = 1,
ROMP = 2,
NS32032 = 4,
NS32332 = 5,
MC680x0 = 6,
I386 = 7,
X86 = 7,
X86_64 = X86 | ABI64,
MIPS = 8,
NS32532 = 9,
HPPA = 11,
ARM = 12,
MC88000 = 13,
SPARC = 14,
I860 = 15, // big-endian
I860_LITTLE = 16, // little-endian
RS6000 = 17,
MC98000 = 18,
POWERPC = 18,
ABI64 = 0x1000000,
ABI64_32 = 0x2000000,
MASK = 0xff000000,
POWERPC64 = POWERPC | ABI64,
VEO = 255,
ARM64 = ARM | ABI64,
ARM64_32 = ARM | ABI64_32
}
public enum MachOCpuSubType : uint
{
}
public class MachOArchEntry
{
public MachOCpuType cputype;
public MachOCpuSubType cpusubtype;
public uint filetype;
public uint ncmds;
public uint sizeofcmds;
public uint flags;
public uint reserved;
}
public class MachOFatEntry
{
public MachOCpuType cputype;
public MachOCpuSubType cpusubtype;
public uint offset;
public uint size;
public uint align;
public string path;
public MachOFatEntry srcentry;
public MachOArchEntry archentry;
}
class BinaryReaderBigEndian : BinaryReader
{
public BinaryReaderBigEndian(Stream stream) : base(stream)
{
}
public new int ReadInt32()
{
var data = base.ReadBytes(4);
if (BitConverter.IsLittleEndian)
Array.Reverse(data);
return BitConverter.ToInt32(data, 0);
}
public new short ReadInt16()
{
var data = base.ReadBytes(2);
if (BitConverter.IsLittleEndian)
Array.Reverse(data);
return BitConverter.ToInt16(data, 0);
}
public new long ReadInt64()
{
var data = base.ReadBytes(8);
if (BitConverter.IsLittleEndian)
Array.Reverse(data);
return BitConverter.ToInt64(data, 0);
}
public new uint ReadUInt32()
{
var data = base.ReadBytes(4);
if (BitConverter.IsLittleEndian)
Array.Reverse(data);
return BitConverter.ToUInt32(data, 0);
}
}
class BinaryWriterBigEndian : BinaryWriter
{
public BinaryWriterBigEndian(Stream stream) : base(stream, Encoding.UTF8, true)
{
}
public override void Write(int value)
{
var data = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian)
Array.Reverse(data);
base.Write(data);
}
public override void Write(uint value)
{
var data = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian)
Array.Reverse(data);
base.Write(data);
}
}
private string _path;
private int _size;
private MachOArchEntry _entry;
private List<MachOFatEntry> _fatEntries = new List<MachOFatEntry>();
public MachOArchEntry Entry => _entry;
public List<MachOFatEntry> FatEntries => _fatEntries;
public MachOFile(string path, bool create = false)
{
_path = path;
if (File.Exists(_path) && !create)
{
_size = (int)new FileInfo(_path).Length;
using (var stream = new FileStream(_path, FileMode.Open, FileAccess.Read))
using (var reader = new BinaryReaderBigEndian(stream))
{
ReadFile(reader);
}
}
}
public static bool IsValidFile(string path)
{
if (!File.Exists(path))
return false;
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
using (var reader = new BinaryReaderBigEndian(stream))
{
var magic = reader.ReadUInt32();
if (magic == 0xCAFEBABE || magic == 0xCEFAEDFE || magic == 0xCFFAEDFE)
{
return true;
}
return false;
}
}
private void ReadFile(BinaryReaderBigEndian reader)
{
var magic = reader.ReadUInt32();
if (magic == 0xCAFEBABE)
{
ReadFatFile(reader);
foreach (var entry in _fatEntries)
{
Console.WriteLine($"Details for {entry.cputype}");
reader.BaseStream.Seek(entry.offset, SeekOrigin.Begin);
ReadFile(reader);
entry.path = _path;
entry.archentry = _entry;
_entry = null;
}
}
else if (magic == 0xCEFAEDFE)
{
ReadFileArch32(reader);
}
else if (magic == 0xCFFAEDFE)
{
ReadFileArch64(reader);
}
else
{
throw new ApplicationException($"File {_path} contains unknown Mach-O header");
}
}
private void ReadFileArch32(BinaryReader reader)
{
_entry = new MachOArchEntry
{
cputype = (MachOCpuType)reader.ReadUInt32(),
cpusubtype = (MachOCpuSubType)reader.ReadUInt32(),
filetype = reader.ReadUInt32(),
ncmds = reader.ReadUInt32(),
sizeofcmds = reader.ReadUInt32(),
flags = reader.ReadUInt32()
};
Console.WriteLine($"Found {_entry.cputype} filetype {_entry.filetype} flags {_entry.flags}");
}
private void ReadFileArch64(BinaryReader reader)
{
_entry = new MachOArchEntry
{
cputype = (MachOCpuType)reader.ReadUInt32(),
cpusubtype = (MachOCpuSubType)reader.ReadUInt32(),
filetype = reader.ReadUInt32(),
ncmds = reader.ReadUInt32(),
sizeofcmds = reader.ReadUInt32(),
flags = reader.ReadUInt32(),
reserved = reader.ReadUInt32()
};
Console.WriteLine($"Found {_entry.cputype} filetype {_entry.filetype} flags {_entry.flags}");
}
private void ReadFatFile(BinaryReaderBigEndian reader)
{
var numArchs = reader.ReadUInt32();
Console.WriteLine($"Found Mach-O Universal with {numArchs} items");
for (var i = 0; i < numArchs; i++)
{
var entry = new MachOFatEntry
{
cputype = (MachOCpuType)reader.ReadUInt32(),
cpusubtype = (MachOCpuSubType)reader.ReadUInt32(),
offset = reader.ReadUInt32(),
size = reader.ReadUInt32(),
align = reader.ReadUInt32()
};
Console.WriteLine($" - {entry.cputype} at offset {entry.offset} size {entry.size}");
_fatEntries.Add(entry);
}
}
static int Align(int offset, int align)
{
offset += (1 << align) - 1;
offset -= offset % (1 << align);
return offset;
}
public void Write()
{
var align = 14;
var offset = Align(4 + FatEntries.Count * 5 * 4, align);
// Determine offsets
foreach (var entry in FatEntries)
{
entry.offset = (uint)offset;
entry.align = (uint)align;
offset = Align(offset + (int)entry.size, align);
}
if (FatEntries.Count == 0)
{
}
else if (FatEntries.Count == 1)
{
Console.WriteLine($"Writing {_path} {FatEntries[0].cputype} from {FatEntries[0].srcentry.path}");
File.Copy(FatEntries[0].srcentry.path, _path);
}
else
{
Console.WriteLine($"Writing {_path}:");
using (var dst = new FileStream(_path, FileMode.Create, FileAccess.Write))
{
// Write Header
using (var writer = new BinaryWriterBigEndian(dst))
{
writer.Write(0xCAFEBABE);
writer.Write(FatEntries.Count);
foreach (var entry in FatEntries)
{
writer.Write((uint)entry.cputype);
writer.Write((uint)entry.cpusubtype);
writer.Write(entry.offset);
writer.Write(entry.size);
writer.Write(entry.align);
}
}
foreach (var entry in FatEntries)
{
Console.WriteLine($" - {entry.cputype} from {entry.srcentry.path}");
using (var src = new FileStream(entry.srcentry.path, FileMode.Open, FileAccess.Read))
{
dst.Seek(entry.offset, SeekOrigin.Begin);
src.Seek(entry.srcentry.offset, SeekOrigin.Begin);
var remaining = (int)entry.size;
var buf = new byte[64 * 1024];
while (remaining != 0)
{
var size = Math.Min(remaining, buf.Length);
src.Read(buf, 0, size);
dst.Write(buf, 0, size);
remaining -= size;
}
}
}
}
}
}
public void AppendEntry(MachOFatEntry entry)
{
if (!FatEntries.Any(v => v.cputype == entry.cputype))
{
FatEntries.Add(new MachOFatEntry()
{
cputype = entry.cputype,
cpusubtype = entry.cpusubtype,
offset = 0,
size = entry.size,
align = entry.align,
srcentry = entry,
archentry = entry.archentry
});
}
}
public void AppendFile(MachOFile file)
{
if (file.Entry != null)
{
AppendEntry(new MachOFatEntry
{
cputype = file.Entry.cputype,
cpusubtype = file.Entry.cpusubtype,
offset = 0,
size = (uint)file._size,
align = 0,
path = file._path,
archentry = file.Entry
});
}
else
{
file.FatEntries.ForEach(AppendEntry);
}
}
}
}

View File

@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net462</TargetFramework>
</PropertyGroup>
</Project>

Binary file not shown.