Compare commits

..

3 Commits

8 changed files with 41 additions and 397 deletions

View File

@@ -63,6 +63,7 @@ jobs:
echo "build_version=$(gli get-next-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
echo "commit_message=$(git log -1 --pretty=%B)" >> $GITHUB_OUTPUT
shell: bash
- name: Configure for release
@@ -200,8 +201,8 @@ jobs:
- name: Publish macOS Ryujinx
run: |
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -p publish_ava/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -p publish/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_arm64.app.tar.gz
create_gitlab_release:
name: Create GitLab Release
@@ -228,11 +229,12 @@ jobs:
echo "build_version=$(gli get-next-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
echo "commit_message=$(git log -1 --pretty=%B)" >> $GITHUB_OUTPUT
shell: bash
- name: Create tag
run: |
gli create-tag -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Canary-${{ steps.version_info.outputs.build_version }} -r ${{ steps.version_info.outputs.git_short_hash }}
gli create-tag -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Canary-${{ steps.version_info.outputs.build_version }} -r ${{ steps.version_info.outputs.git_short_hash }} -c "${{ steps.version_info.outputs.commit_message }}"
- name: Create release
run: |

View File

@@ -203,7 +203,7 @@ jobs:
run: |
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -p publish/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -p publish/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_arm64.app.tar.gz
create_gitlab_release:
name: Create GitLab Release

View File

@@ -1,95 +0,0 @@
import argparse
import os
from pathlib import Path
import platform
import shutil
import subprocess
parser = argparse.ArgumentParser(
description="Construct Universal dylibs for nuget package"
)
parser.add_argument(
"arm64_input_directory", help="ARM64 Input directory containing dylibs"
)
parser.add_argument(
"x86_64_input_directory", help="x86_64 Input directory containing dylibs"
)
parser.add_argument("output_directory", help="Output directory")
parser.add_argument("rglob", help="rglob")
args = parser.parse_args()
# Use Apple LLVM on Darwin, otherwise standard LLVM.
if platform.system() == "Darwin":
LIPO = "lipo"
else:
LIPO = shutil.which("llvm-lipo")
if LIPO is None:
for llvm_ver in [17, 16, 15, 14, 13]:
lipo_path = shutil.which(f"llvm-lipo-{llvm_ver}")
if lipo_path is not None:
LIPO = lipo_path
break
if LIPO is None:
raise Exception("Cannot find a valid location for LLVM lipo!")
arm64_input_directory: Path = Path(args.arm64_input_directory)
x86_64_input_directory: Path = Path(args.x86_64_input_directory)
output_directory: Path = Path(args.output_directory)
rglob = args.rglob
def get_new_name(
input_directory: Path, output_directory: str, input_dylib_path: Path
) -> Path:
input_component = str(input_dylib_path).replace(str(input_directory), "")[1:]
return Path(os.path.join(output_directory, input_component))
def is_fat_file(dylib_path: Path) -> str:
res = subprocess.check_output([LIPO, "-info", str(dylib_path.absolute())]).decode(
"utf-8"
)
return not res.split("\n")[0].startswith("Non-fat file")
def construct_universal_dylib(
arm64_input_dylib_path: Path, x86_64_input_dylib_path: Path, output_dylib_path: Path
):
if output_dylib_path.exists() or output_dylib_path.is_symlink():
os.remove(output_dylib_path)
os.makedirs(output_dylib_path.parent, exist_ok=True)
if arm64_input_dylib_path.is_symlink():
os.symlink(
os.path.basename(arm64_input_dylib_path.resolve()), output_dylib_path
)
else:
if is_fat_file(arm64_input_dylib_path) or not x86_64_input_dylib_path.exists():
with open(output_dylib_path, "wb") as dst:
with open(arm64_input_dylib_path, "rb") as src:
dst.write(src.read())
else:
subprocess.check_call(
[
LIPO,
str(arm64_input_dylib_path.absolute()),
str(x86_64_input_dylib_path.absolute()),
"-output",
str(output_dylib_path.absolute()),
"-create",
]
)
print(rglob)
for path in arm64_input_directory.rglob("**/*.dylib"):
construct_universal_dylib(
path,
get_new_name(arm64_input_directory, x86_64_input_directory, path),
get_new_name(arm64_input_directory, output_directory, path),
)

View File

@@ -14,7 +14,7 @@ mkdir "$APP_BUNDLE_DIRECTORY/Contents/Frameworks"
mkdir "$APP_BUNDLE_DIRECTORY/Contents/MacOS"
mkdir "$APP_BUNDLE_DIRECTORY/Contents/Resources"
# Copy executable and nsure executable can be executed
# Copy executable and ensure executable can be executed
cp "$PUBLISH_DIRECTORY/Ryujinx" "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx"
chmod u+x "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx"

View File

@@ -33,69 +33,44 @@ if [[ "$(uname)" == "Darwin" ]]; then
fi
if [ "$CANARY" == "1" ]; then
RELEASE_TAR_FILE_NAME=ryujinx-canary-$VERSION-macos_universal.app.tar
RELEASE_TAR_FILE_NAME=ryujinx-canary-$VERSION-macos_arm64.app.tar
elif [ "$VERSION" == "1.1.0" ]; then
RELEASE_TAR_FILE_NAME=ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar
RELEASE_TAR_FILE_NAME=ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_arm64.app.tar
else
RELEASE_TAR_FILE_NAME=ryujinx-$VERSION-macos_universal.app.tar
RELEASE_TAR_FILE_NAME=ryujinx-$VERSION-macos_arm64.app.tar
fi
ARM64_APP_BUNDLE="$TEMP_DIRECTORY/output_arm64/Ryujinx.app"
X64_APP_BUNDLE="$TEMP_DIRECTORY/output_x64/Ryujinx.app"
UNIVERSAL_APP_BUNDLE="$OUTPUT_DIRECTORY/Ryujinx.app"
OUTPUT_APP_BUNDLE="$OUTPUT_DIRECTORY/Ryujinx.app"
EXECUTABLE_SUB_PATH=Contents/MacOS/Ryujinx
rm -rf "$TEMP_DIRECTORY"
mkdir -p "$TEMP_DIRECTORY"
DOTNET_COMMON_ARGS=(-p:DebugType=embedded -p:Version="$VERSION" -p:SourceRevisionId="$SOURCE_REVISION_ID" --self-contained true $EXTRA_ARGS)
DOTNET_COMMON_ARGS=(-p:DebugType=embedded -p:Version="$VERSION" -p:SourceRevisionId="$SOURCE_REVISION_ID" --self-contained $EXTRA_ARGS)
dotnet restore
dotnet build -c "$CONFIGURATION" src/Ryujinx
dotnet publish -c "$CONFIGURATION" -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx
dotnet publish -c "$CONFIGURATION" -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx
# Get rid of the support library for ARMeilleure for x64 (that's only for arm64)
rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib"
# Get rid of libsoundio from arm64 builds as we don't have a arm64 variant
# TODO: remove this once done
rm -rf "$TEMP_DIRECTORY/publish_arm64/libsoundio.dylib"
pushd "$BASE_DIR/distribution/macos"
./create_app_bundle.sh "$TEMP_DIRECTORY/publish_x64" "$TEMP_DIRECTORY/output_x64" "$ENTITLEMENTS_FILE_PATH"
./create_app_bundle.sh "$TEMP_DIRECTORY/publish_arm64" "$TEMP_DIRECTORY/output_arm64" "$ENTITLEMENTS_FILE_PATH"
popd
rm -rf "$UNIVERSAL_APP_BUNDLE"
rm -rf "$OUTPUT_APP_BUNDLE"
mkdir -p "$OUTPUT_DIRECTORY"
# Let's copy one of the two different app bundle and remove the executable
cp -R "$ARM64_APP_BUNDLE" "$UNIVERSAL_APP_BUNDLE"
rm "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH"
# Make its libraries universal
python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_APP_BUNDLE" "$X64_APP_BUNDLE" "$UNIVERSAL_APP_BUNDLE" "**/*.dylib"
if ! [ -x "$(command -v lipo)" ];
then
if ! [ -x "$(command -v llvm-lipo-17)" ];
then
LIPO=llvm-lipo
else
LIPO=llvm-lipo-17
fi
else
LIPO=lipo
fi
# Make the executable universal
$LIPO "$ARM64_APP_BUNDLE/$EXECUTABLE_SUB_PATH" "$X64_APP_BUNDLE/$EXECUTABLE_SUB_PATH" -output "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH" -create
# Let's copy the app bundle to the output folder so we can package it freely
cp -R "$ARM64_APP_BUNDLE" "$OUTPUT_APP_BUNDLE"
# Patch up the Info.plist to have appropriate version
sed -r -i.bck "s/\%\%RYUJINX_BUILD_VERSION\%\%/$VERSION/g;" "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist"
sed -r -i.bck "s/\%\%RYUJINX_BUILD_GIT_HASH\%\%/$SOURCE_REVISION_ID/g;" "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist"
rm "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist.bck"
sed -r -i.bck "s/\%\%RYUJINX_BUILD_VERSION\%\%/$VERSION/g;" "$OUTPUT_APP_BUNDLE/Contents/Info.plist"
sed -r -i.bck "s/\%\%RYUJINX_BUILD_GIT_HASH\%\%/$SOURCE_REVISION_ID/g;" "$OUTPUT_APP_BUNDLE/Contents/Info.plist"
rm "$OUTPUT_APP_BUNDLE/Contents/Info.plist.bck"
# Now sign it
if ! [ -x "$(command -v codesign)" ];
@@ -109,10 +84,10 @@ then
# NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes.
# cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign"
echo "Using rcodesign for ad-hoc signing"
rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$UNIVERSAL_APP_BUNDLE"
rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$OUTPUT_APP_BUNDLE"
else
echo "Using codesign for ad-hoc signing"
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f -s - "$UNIVERSAL_APP_BUNDLE"
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f -s - "$OUTPUT_APP_BUNDLE"
fi
echo "Creating archive"

View File

@@ -1,91 +1,43 @@
using Gommon;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Kernel.Memory;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace Ryujinx.HLE.Debugger
{
public partial class Debugger
{
private sealed record RcmdEntry(string[] Names, Func<Debugger, string, string> Handler, string[] HelpLines);
// Atmosphere/libraries/libmesosphere/source/kern_k_memory_block_manager.cpp
private static readonly string[] _memoryStateNames =
{
"----- Free -----",
"Io ",
"Static ",
"Code ",
"CodeData ",
"Normal ",
"Shared ",
"Alias ",
"AliasCode ",
"AliasCodeData ",
"Ipc ",
"Stack ",
"ThreadLocal ",
"Transfered ",
"SharedTransfered",
"SharedCode ",
"Inaccessible ",
"NonSecureIpc ",
"NonDeviceIpc ",
"Kernel ",
"GeneratedCode ",
"CodeOut ",
"Coverage ",
};
static Debugger()
{
_rcmdDelegates.Add(new RcmdEntry(
["help"],
(dbgr, _) => _rcmdDelegates
.Where(entry => entry.HelpLines.Length > 0)
.SelectMany(entry => entry.HelpLines)
.JoinToString('\n') + '\n',
Array.Empty<string>()));
_rcmdDelegates.Add(new RcmdEntry(["get info"], (dbgr, _) => dbgr.GetProcessInfo(), ["get info"]));
_rcmdDelegates.Add(new RcmdEntry(["backtrace", "bt"], (dbgr, _) => dbgr.GetStackTrace(), ["backtrace", "bt"]));
_rcmdDelegates.Add(new RcmdEntry(["registers", "reg"], (dbgr, _) => dbgr.GetRegisters(), ["registers", "reg"]));
_rcmdDelegates.Add(new RcmdEntry(["minidump"], (dbgr, _) => dbgr.GetMinidump(), ["minidump"]));
_rcmdDelegates.Add(new RcmdEntry(["get mappings"], (dbgr, args) => dbgr.GetMemoryMappings(args), ["get mappings", "get mappings {address}"]));
_rcmdDelegates.Add(new RcmdEntry(["get mapping"], (dbgr, args) => dbgr.GetMemoryMapping(args), ["get mapping {address}"]));
_rcmdDelegates.Add(["help"],
_ => _rcmdDelegates.Keys
.Where(x => !x[0].Equals("help"))
.Select(x => x.JoinToString('\n'))
.JoinToString('\n') + '\n'
);
_rcmdDelegates.Add(["get info"], dbgr => dbgr.GetProcessInfo());
_rcmdDelegates.Add(["backtrace", "bt"], dbgr => dbgr.GetStackTrace());
_rcmdDelegates.Add(["registers", "reg"], dbgr => dbgr.GetRegisters());
_rcmdDelegates.Add(["minidump"], dbgr => dbgr.GetMinidump());
}
private static readonly List<RcmdEntry> _rcmdDelegates = [];
private static readonly Dictionary<string[], Func<Debugger, string>> _rcmdDelegates = new();
public static string CallRcmdDelegate(Debugger debugger, string command)
public static Func<Debugger, string> FindRcmdDelegate(string command)
{
string originalCommand = command ?? string.Empty;
string trimmedCommand = originalCommand.Trim();
Func<Debugger, string> searchResult = _ => $"Unknown command: {command}\n";
foreach (RcmdEntry entry in _rcmdDelegates)
foreach ((string[] names, Func<Debugger, string> dlg) in _rcmdDelegates)
{
foreach (string name in entry.Names)
if (names.ContainsIgnoreCase(command.Trim()))
{
if (trimmedCommand.Equals(name, StringComparison.OrdinalIgnoreCase))
{
return entry.Handler(debugger, string.Empty);
}
if (trimmedCommand.Length > name.Length &&
trimmedCommand.StartsWith(name, StringComparison.OrdinalIgnoreCase) &&
char.IsWhiteSpace(trimmedCommand[name.Length]))
{
string arguments = trimmedCommand[name.Length..].TrimStart();
return entry.Handler(debugger, arguments);
}
searchResult = dlg;
break;
}
}
return $"Unknown command: {originalCommand}\n";
return searchResult;
}
public string GetStackTrace()
@@ -134,181 +86,5 @@ namespace Ryujinx.HLE.Debugger
return $"Error getting process info: {e.Message}\n";
}
}
public string GetMemoryMappings(string arguments)
{
if (Process?.MemoryManager is not { } memoryManager)
{
return "No application process found\n";
}
string trimmedArgs = arguments?.Trim() ?? string.Empty;
ulong startAddress = 0;
if (!string.IsNullOrEmpty(trimmedArgs))
{
if (!TryParseAddressArgument(trimmedArgs, out startAddress))
{
return $"Invalid address: {trimmedArgs}\n";
}
}
ulong requestedAddress = startAddress;
ulong currentAddress = Math.Max(requestedAddress, memoryManager.AddrSpaceStart);
StringBuilder sb = new();
sb.AppendLine($"Mappings (starting from 0x{requestedAddress:x10}):");
if (currentAddress >= memoryManager.AddrSpaceEnd)
{
return sb.ToString();
}
while (currentAddress < memoryManager.AddrSpaceEnd)
{
KMemoryInfo info = memoryManager.QueryMemory(currentAddress);
try
{
if (info.Size == 0 || info.Address >= memoryManager.AddrSpaceEnd)
{
break;
}
sb.AppendLine(FormatMapping(info, indent: true));
if (info.Address > ulong.MaxValue - info.Size)
{
break;
}
ulong nextAddress = info.Address + info.Size;
if (nextAddress <= currentAddress)
{
break;
}
currentAddress = nextAddress;
}
finally
{
KMemoryInfo.Pool.Release(info);
}
}
return sb.ToString();
}
public string GetMemoryMapping(string arguments)
{
if (Process?.MemoryManager is not { } memoryManager)
{
return "No application process found\n";
}
string trimmedArgs = arguments?.Trim() ?? string.Empty;
if (string.IsNullOrEmpty(trimmedArgs))
{
return "Missing address argument for `get mapping`\n";
}
if (!TryParseAddressArgument(trimmedArgs, out ulong address))
{
return $"Invalid address: {trimmedArgs}\n";
}
KMemoryInfo info = memoryManager.QueryMemory(address);
try
{
return FormatMapping(info, indent: false) + '\n';
}
finally
{
KMemoryInfo.Pool.Release(info);
}
}
private static string FormatMapping(KMemoryInfo info, bool indent)
{
ulong endAddress;
if (info.Size == 0)
{
endAddress = info.Address;
}
else if (info.Address > ulong.MaxValue - (info.Size - 1))
{
endAddress = ulong.MaxValue;
}
else
{
endAddress = info.Address + info.Size - 1;
}
string prefix = indent ? " " : string.Empty;
return $"{prefix}0x{info.Address:x10} - 0x{endAddress:x10} {GetPermissionString(info)} {GetMemoryStateName(info.State)} {GetAttributeFlags(info)} [{info.IpcRefCount}, {info.DeviceRefCount}]";
}
private static string GetPermissionString(KMemoryInfo info)
{
if ((info.State & MemoryState.UserMask) == MemoryState.Unmapped)
{
return " ";
}
return info.Permission switch
{
KMemoryPermission.ReadAndExecute => "r-x",
KMemoryPermission.Read => "r--",
KMemoryPermission.ReadAndWrite => "rw-",
_ => "---"
};
}
private static string GetMemoryStateName(MemoryState state)
{
int stateIndex = (int)(state & MemoryState.UserMask);
if ((uint)stateIndex < _memoryStateNames.Length)
{
return _memoryStateNames[stateIndex];
}
return "Unknown ";
}
private static bool TryParseAddressArgument(string text, out ulong value)
{
value = 0;
if (string.IsNullOrWhiteSpace(text))
{
return false;
}
string trimmed = text.Trim();
if (trimmed.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
trimmed = trimmed[2..];
}
if (trimmed.Length == 0)
{
return false;
}
return ulong.TryParse(trimmed, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out value);
}
private static string GetAttributeFlags(KMemoryInfo info)
{
char locked = info.Attribute.HasFlag(MemoryAttribute.Borrowed) ? 'L' : '-';
char ipc = info.Attribute.HasFlag(MemoryAttribute.IpcMapped) ? 'I' : '-';
char device = info.Attribute.HasFlag(MemoryAttribute.DeviceMapped) ? 'D' : '-';
char uncached = info.Attribute.HasFlag(MemoryAttribute.Uncached) ? 'U' : '-';
return $"{locked}{ipc}{device}{uncached}";
}
}
}

View File

@@ -404,8 +404,9 @@ namespace Ryujinx.HLE.Debugger.Gdb
string command = Helpers.FromHex(hexCommand);
Logger.Debug?.Print(LogClass.GdbStub, $"Received Rcmd: {command}");
string response = Debugger.CallRcmdDelegate(Debugger, command);
Processor.ReplyHex(response);
Func<Debugger, string> rcmd = Debugger.FindRcmdDelegate(command);
Processor.ReplyHex(rcmd(Debugger));
}
catch (Exception e)
{

View File

@@ -138,7 +138,6 @@ namespace Ryujinx.Ava.UI.Windows
Executor.ExecuteBackgroundAsync(async () =>
{
await ShowIntelMacWarningAsync();
if (CommandLineState.FirmwareToInstallPathArg.TryGet(out FilePath fwPath))
{
if (fwPath is { ExistsAsFile: true, Extension: "xci" or "zip" } || fwPath.ExistsAsDirectory)
@@ -757,20 +756,6 @@ namespace Ryujinx.Ava.UI.Windows
});
}
private static bool _intelMacWarningShown = !RunningPlatform.IsIntelMac;
public static async Task ShowIntelMacWarningAsync()
{
if (_intelMacWarningShown)
return;
await Dispatcher.UIThread.InvokeAsync(async () => await ContentDialogHelper.CreateWarningDialog(
"Intel Mac Warning",
"Intel Macs are not supported and will not work properly.\nIf you continue, do not come to our Discord asking for support;\nand do not report bugs on the GitHub. They will be closed."));
_intelMacWarningShown = true;
}
private void AppWindow_OnGotFocus(object sender, GotFocusEventArgs e)
{
if (ViewModel.AppHost is null)