mirror of
https://github.com/XorTroll/uLaunch
synced 2025-02-16 12:38:29 +00:00
Alternate USB mode for >=10.0.0, small fixes
This commit is contained in:
parent
fbeeccb331
commit
45ed6bf530
8 changed files with 170 additions and 80 deletions
6
Makefile
6
Makefile
|
@ -9,20 +9,16 @@ export UL_DEV := 0
|
|||
export UL_DEFS := -DUL_DEV=$(UL_DEV) -DUL_MAJOR=$(UL_MAJOR) -DUL_MINOR=$(UL_MINOR) -DUL_MICRO=$(UL_MICRO) -DUL_VERSION=\"$(UL_VERSION)\"
|
||||
|
||||
export UL_COMMON_SOURCES := ../uLaunch/source ../uLaunch/source/am ../uLaunch/source/cfg ../uLaunch/source/db ../uLaunch/source/fs ../uLaunch/source/net ../uLaunch/source/os ../uLaunch/source/util
|
||||
export UL_COMMON_INCLUDES := ../uLaunch/include ../master-libnx/nx/external/bsd/include
|
||||
export UL_COMMON_INCLUDES := ../uLaunch/include
|
||||
|
||||
export UL_CXXFLAGS := -fno-rtti -fexceptions -std=gnu++17
|
||||
|
||||
# TODO: remove this and libnx master submodule when libnx releases
|
||||
export LIBNX := $(CURDIR)/master-libnx/nx
|
||||
|
||||
.PHONY: all base make_hbtarget hbtarget make_daemon daemon make_menu menu clean
|
||||
|
||||
all: hbtarget daemon menu
|
||||
|
||||
base:
|
||||
@mkdir -p SdOut/
|
||||
@$(MAKE) -C master-libnx/
|
||||
|
||||
make_hbtarget:
|
||||
@$(MAKE) -C uHbTarget/
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit c1c71b1d96467f328d34e911781ffcdc35c6c077
|
||||
Subproject commit a3c31497a7f5c7f9136fb612a9e1970500b3e8f6
|
|
@ -1 +1 @@
|
|||
Subproject commit 30f3e4c33d2767126c36a51b9259051c6c42d6b5
|
||||
Subproject commit da6eac986d7f67ca3dcc3a8df0c191ffbea4686f
|
|
@ -35,6 +35,13 @@ namespace ams
|
|||
}
|
||||
}
|
||||
|
||||
enum class USBMode : u32
|
||||
{
|
||||
Invalid,
|
||||
RawRGBA,
|
||||
JPEG
|
||||
};
|
||||
|
||||
AccountUid selected_uid = {};
|
||||
hb::HbTargetParams hblaunch_flag = {};
|
||||
hb::HbTargetParams hbapplaunch_flag = {};
|
||||
|
@ -45,10 +52,14 @@ bool album_flag = false;
|
|||
bool menu_restart_flag = false;
|
||||
bool app_opened_hb = false;
|
||||
AppletOperationMode console_mode;
|
||||
u8 *usbbuf = nullptr;
|
||||
u8 *usb_buf = nullptr;
|
||||
u8 *usb_buf_read = nullptr;
|
||||
cfg::Config config = {};
|
||||
Thread ipc_thr;
|
||||
Thread usb_thr;
|
||||
USBMode usb_mode = USBMode::Invalid;
|
||||
|
||||
static constexpr size_t USBPacketSize = RawRGBAScreenBufferSize + sizeof(u32);
|
||||
|
||||
// This way the menu isn't loaded, even if nothing but the home menu is present, which outside of the debug menu is considered an invalid state
|
||||
bool debug_menu = false;
|
||||
|
@ -371,6 +382,18 @@ namespace
|
|||
ams::sf::hipc::ServerManager<NumServers, ServerOptions, MaxSessions> daemon_ipc_manager;
|
||||
}
|
||||
|
||||
static inline Result capsscCommand1204(void *buf, size_t buf_size, u64 *out_jpeg_size)
|
||||
{
|
||||
struct {
|
||||
u32 a;
|
||||
u64 b;
|
||||
} in = {0, 10000000000};
|
||||
return serviceDispatchInOut(capsscGetServiceSession(), 1204, in, *out_jpeg_size,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapTransferAllowsNonSecure | SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
.buffers = { { buf, buf_size } },
|
||||
);
|
||||
}
|
||||
|
||||
namespace impl
|
||||
{
|
||||
void IPCManagerThread(void *arg)
|
||||
|
@ -380,18 +403,44 @@ namespace impl
|
|||
daemon_ipc_manager.LoopProcess();
|
||||
}
|
||||
|
||||
void USBViewerThread(void *arg)
|
||||
void USBViewerRGBAThread(void *arg)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
bool flag;
|
||||
appletGetLastForegroundCaptureImageEx(usbbuf, RawRGBAScreenBufferSize, &flag);
|
||||
bool tmp_flag;
|
||||
appletGetLastForegroundCaptureImageEx(usb_buf_read, RawRGBAScreenBufferSize, &tmp_flag);
|
||||
appletUpdateLastForegroundCaptureImage();
|
||||
usbCommsWrite(usbbuf, RawRGBAScreenBufferSize);
|
||||
usbCommsWrite(usb_buf, USBPacketSize);
|
||||
svcSleepThread(10'000'000l);
|
||||
}
|
||||
}
|
||||
|
||||
void USBViewerJPEGThread(void *arg)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
u64 tmp_size;
|
||||
capsscCommand1204(usb_buf_read, RawRGBAScreenBufferSize, &tmp_size);
|
||||
usbCommsWrite(usb_buf, USBPacketSize);
|
||||
svcSleepThread(10'000'000l);
|
||||
}
|
||||
}
|
||||
|
||||
void PrepareUSBViewer()
|
||||
{
|
||||
usb_buf = new (std::align_val_t(0x1000)) u8[USBPacketSize]();
|
||||
usb_buf_read = usb_buf + sizeof(u32);
|
||||
u64 tmp_size;
|
||||
auto rc = capsscCommand1204(usb_buf_read, RawRGBAScreenBufferSize, &tmp_size);
|
||||
if(R_SUCCEEDED(rc)) usb_mode = USBMode::JPEG;
|
||||
else
|
||||
{
|
||||
usb_mode = USBMode::RawRGBA;
|
||||
capsscExit();
|
||||
}
|
||||
*(u32*)usb_buf = static_cast<u32>(usb_mode);
|
||||
}
|
||||
|
||||
void LoopUpdate()
|
||||
{
|
||||
HandleGeneralChannel();
|
||||
|
@ -486,14 +535,24 @@ namespace impl
|
|||
|
||||
Result LaunchIPCManagerThread()
|
||||
{
|
||||
R_TRY(threadCreate(&ipc_thr, &IPCManagerThread, NULL, NULL, 0x4000, 0x2b, -2));
|
||||
R_TRY(threadCreate(&ipc_thr, &IPCManagerThread, nullptr, nullptr, 0x4000, 0x2b, -2));
|
||||
R_TRY(threadStart(&ipc_thr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result LaunchUSBViewerThread()
|
||||
{
|
||||
R_TRY(threadCreate(&usb_thr, &USBViewerThread, NULL, NULL, 0x4000, 0x2b, -2));
|
||||
switch(usb_mode)
|
||||
{
|
||||
case USBMode::RawRGBA:
|
||||
R_TRY(threadCreate(&usb_thr, &USBViewerRGBAThread, nullptr, nullptr, 0x4000, 0x2b, -2));
|
||||
break;
|
||||
case USBMode::JPEG:
|
||||
R_TRY(threadCreate(&usb_thr, &USBViewerJPEGThread, nullptr, nullptr, 0x4000, 0x2b, -2));
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
R_TRY(threadStart(&usb_thr));
|
||||
return 0;
|
||||
}
|
||||
|
@ -531,7 +590,8 @@ namespace impl
|
|||
if(config.viewer_usb_enabled)
|
||||
{
|
||||
UL_ASSERT(usbCommsInitialize());
|
||||
usbbuf = new (std::align_val_t(0x1000)) u8[RawRGBAScreenBufferSize]();
|
||||
UL_ASSERT(capsscInitialize());
|
||||
PrepareUSBViewer();
|
||||
UL_ASSERT(LaunchUSBViewerThread());
|
||||
}
|
||||
|
||||
|
@ -541,7 +601,7 @@ namespace impl
|
|||
// Debug testing mode
|
||||
debug_menu = true;
|
||||
|
||||
consoleInit(NULL);
|
||||
consoleInit(nullptr);
|
||||
CONSOLE_FMT("Welcome to uDaemon -> debug mode menu")
|
||||
CONSOLE_FMT("")
|
||||
CONSOLE_FMT("(A) -> Dump system save data to sd:/ulaunch/save_dump")
|
||||
|
@ -595,7 +655,8 @@ namespace impl
|
|||
if(config.viewer_usb_enabled)
|
||||
{
|
||||
usbCommsExit();
|
||||
operator delete[](usbbuf, std::align_val_t(0x1000));
|
||||
if(usb_mode == USBMode::JPEG) capsscExit();
|
||||
operator delete[](usb_buf, std::align_val_t(0x1000));
|
||||
}
|
||||
|
||||
nsExit();
|
||||
|
|
|
@ -45,7 +45,7 @@ LIBS := -lnx
|
|||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(CURDIR)/../master-libnx/nx
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
|
|
|
@ -45,7 +45,7 @@ LIBS := -lnx -lpu -lfreetype -lSDL2_mixer -lopusfile -lopus -lmodplug -lmpg123 -
|
|||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(CURDIR)/../master-libnx/nx $(CURDIR)/../Plutonium/Plutonium/Output
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/../Plutonium/Plutonium/Output
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
|
|
|
@ -37,18 +37,18 @@ namespace uViewer
|
|||
|
||||
public void RefreshBackups()
|
||||
{
|
||||
Array.Copy(Main.CaptureBlocks[5], CaptureBackups[4], CaptureBackups[4].Length);
|
||||
Array.Copy(Main.CaptureBlocks[4], CaptureBackups[3], CaptureBackups[3].Length);
|
||||
Array.Copy(Main.CaptureBlocks[3], CaptureBackups[2], CaptureBackups[2].Length);
|
||||
Array.Copy(Main.CaptureBlocks[2], CaptureBackups[1], CaptureBackups[1].Length);
|
||||
Array.Copy(Main.CaptureBlocks[1], CaptureBackups[0], CaptureBackups[0].Length);
|
||||
Buffer.BlockCopy(Main.CaptureBlocks[5], 0, CaptureBackups[4], 0, CaptureBackups[4].Length);
|
||||
Buffer.BlockCopy(Main.CaptureBlocks[4], 0, CaptureBackups[3], 0, CaptureBackups[3].Length);
|
||||
Buffer.BlockCopy(Main.CaptureBlocks[3], 0, CaptureBackups[2], 0, CaptureBackups[2].Length);
|
||||
Buffer.BlockCopy(Main.CaptureBlocks[2], 0, CaptureBackups[1], 0, CaptureBackups[1].Length);
|
||||
Buffer.BlockCopy(Main.CaptureBlocks[1], 0, CaptureBackups[0], 0, CaptureBackups[0].Length);
|
||||
}
|
||||
|
||||
private void ScreenshotList_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (ScreenshotList.SelectedIndex >= 0)
|
||||
if(ScreenshotList.SelectedIndex >= 0)
|
||||
{
|
||||
ViewerMainForm.ApplyRGBAToPictureBox(ScreenshotBox, CaptureBackups[ScreenshotList.SelectedIndex]);
|
||||
ViewerMainForm.ApplyModeDelegate(ScreenshotBox, CaptureBackups[ScreenshotList.SelectedIndex]);
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ namespace uViewer
|
|||
{
|
||||
RefreshBackups();
|
||||
ScreenshotList.SelectedIndex = 0;
|
||||
ViewerMainForm.ApplyRGBAToPictureBox(ScreenshotBox, CaptureBackups[ScreenshotList.SelectedIndex]);
|
||||
ViewerMainForm.ApplyModeDelegate(ScreenshotBox, CaptureBackups[ScreenshotList.SelectedIndex]);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,33 +1,76 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using libusbK;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using libusbK;
|
||||
|
||||
namespace uViewer
|
||||
{
|
||||
public enum USBMode : uint
|
||||
{
|
||||
Invalid,
|
||||
RawRGBA,
|
||||
JPEG,
|
||||
}
|
||||
|
||||
public partial class ViewerMainForm : Form
|
||||
{
|
||||
private UsbK USB = null;
|
||||
private Thread USBThread;
|
||||
private Thread USBThread = null;
|
||||
|
||||
private ToolboxForm Toolbox;
|
||||
private ToolboxForm Toolbox = null;
|
||||
private static MemoryStream BaseStream = new MemoryStream((int)RawRGBAScreenBufferSize);
|
||||
private static USBMode Mode = USBMode.Invalid;
|
||||
|
||||
public delegate void ApplyTypeImplDelegate(PictureBox Box, byte[] Data);
|
||||
public static ApplyTypeImplDelegate ApplyModeDelegate = null;
|
||||
|
||||
public delegate void ApplyDelegate(byte[] data);
|
||||
|
||||
private static unsafe void ApplyRGBA(PictureBox Box, byte[] RGBA)
|
||||
{
|
||||
var bmp = Box.Image as Bitmap;
|
||||
BitmapData img = bmp.LockBits(new Rectangle(0, 0, 1280, 720), ImageLockMode.ReadWrite, bmp.PixelFormat);
|
||||
|
||||
fixed (byte* raw_data = RGBA) unchecked
|
||||
{
|
||||
uint* ptr = (uint*)raw_data;
|
||||
ptr++;
|
||||
uint* image = (uint*)img.Scan0.ToPointer();
|
||||
uint* ptr_end = ptr + 720 * 1280;
|
||||
while (ptr != ptr_end)
|
||||
{
|
||||
uint argb = *ptr << 8;
|
||||
*image = ((argb & 0x0000FF00) << 8) | ((argb & 0x00FF0000) >> 8) | ((argb & 0xFF000000) >> 24) | 0xFF000000;
|
||||
image++;
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
bmp.UnlockBits(img);
|
||||
}
|
||||
|
||||
private static void ApplyJPEG(PictureBox Box, byte[] JPEG)
|
||||
{
|
||||
BaseStream.Position = 0;
|
||||
BaseStream.Write(JPEG, 4, (int)RawRGBAScreenBufferSize);
|
||||
Box.Image = Image.FromStream(BaseStream);
|
||||
}
|
||||
|
||||
public const long RawRGBAScreenBufferSize = 1280 * 720 * 4;
|
||||
public const long USBPacketSize = RawRGBAScreenBufferSize + 4;
|
||||
|
||||
public byte[][] CaptureBlocks = new byte[][]
|
||||
{
|
||||
new byte[RawRGBAScreenBufferSize], // Current block
|
||||
new byte[RawRGBAScreenBufferSize], // Temporary blocks (5)
|
||||
new byte[RawRGBAScreenBufferSize],
|
||||
new byte[RawRGBAScreenBufferSize],
|
||||
new byte[RawRGBAScreenBufferSize],
|
||||
new byte[RawRGBAScreenBufferSize],
|
||||
new byte[USBPacketSize], // Current block
|
||||
new byte[USBPacketSize], // Temporary blocks (5)
|
||||
new byte[USBPacketSize],
|
||||
new byte[USBPacketSize],
|
||||
new byte[USBPacketSize],
|
||||
new byte[USBPacketSize],
|
||||
};
|
||||
|
||||
public ViewerMainForm(UsbK USB)
|
||||
|
@ -56,7 +99,7 @@ namespace uViewer
|
|||
if(USB == null)
|
||||
{
|
||||
MessageBox.Show("Unable to connect to uLaunch via USB-C cable.", "Unable to connect");
|
||||
Environment.Exit(Environment.ExitCode);
|
||||
Environment.Exit(1);
|
||||
}
|
||||
base.OnShown(e);
|
||||
}
|
||||
|
@ -70,13 +113,29 @@ namespace uViewer
|
|||
{
|
||||
try
|
||||
{
|
||||
USB.ReadPipe(0x81, CaptureBlocks[0], CaptureBlocks[0].Length, out _, IntPtr.Zero);
|
||||
Array.Copy(CaptureBlocks[4], CaptureBlocks[5], CaptureBlocks[4].Length);
|
||||
Array.Copy(CaptureBlocks[3], CaptureBlocks[4], CaptureBlocks[3].Length);
|
||||
Array.Copy(CaptureBlocks[2], CaptureBlocks[3], CaptureBlocks[2].Length);
|
||||
Array.Copy(CaptureBlocks[1], CaptureBlocks[2], CaptureBlocks[1].Length);
|
||||
Array.Copy(CaptureBlocks[0], CaptureBlocks[1], CaptureBlocks[0].Length);
|
||||
ApplyRGBAInternal(CaptureBlocks[0]);
|
||||
USB.ReadPipe(0x81, CaptureBlocks[0], (int)USBPacketSize, out _, IntPtr.Zero);
|
||||
if(Mode == USBMode.Invalid)
|
||||
{
|
||||
var mode_raw = BitConverter.ToUInt32(CaptureBlocks[0], 0);
|
||||
Mode = (USBMode)mode_raw;
|
||||
switch(Mode)
|
||||
{
|
||||
case USBMode.RawRGBA:
|
||||
ApplyModeDelegate = ApplyRGBA;
|
||||
break;
|
||||
case USBMode.JPEG:
|
||||
ApplyModeDelegate = ApplyJPEG;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Buffer.BlockCopy(CaptureBlocks[4], 0, CaptureBlocks[5], 0, (int)USBPacketSize);
|
||||
Buffer.BlockCopy(CaptureBlocks[3], 0, CaptureBlocks[4], 0, (int)USBPacketSize);
|
||||
Buffer.BlockCopy(CaptureBlocks[2], 0, CaptureBlocks[3], 0, (int)USBPacketSize);
|
||||
Buffer.BlockCopy(CaptureBlocks[1], 0, CaptureBlocks[2], 0, (int)USBPacketSize);
|
||||
Buffer.BlockCopy(CaptureBlocks[0], 0, CaptureBlocks[1], 0, (int)USBPacketSize);
|
||||
ApplyDataImpl(CaptureBlocks[0]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -87,45 +146,19 @@ namespace uViewer
|
|||
|
||||
public static void InitializePictureBox(PictureBox Box)
|
||||
{
|
||||
int w = 1280;
|
||||
int h = 720;
|
||||
|
||||
Box.Image = new Bitmap(w, h, PixelFormat.Format32bppArgb);
|
||||
Box.Image = new Bitmap(1280, 720, PixelFormat.Format32bppArgb);
|
||||
}
|
||||
|
||||
public static unsafe void ApplyRGBAToPictureBox(PictureBox Box, byte[] RGBA)
|
||||
public void ApplyDataImpl(byte[] data)
|
||||
{
|
||||
var bmp = Box.Image as Bitmap;
|
||||
BitmapData img = bmp.LockBits(new Rectangle(0, 0, 1280, 720), ImageLockMode.ReadWrite, bmp.PixelFormat);
|
||||
|
||||
fixed (byte* rawData = RGBA) unchecked
|
||||
if(CaptureBox.InvokeRequired)
|
||||
{
|
||||
uint* ptr = (uint*)rawData;
|
||||
uint* image = (uint*)img.Scan0.ToPointer();
|
||||
uint* ptrEnd = ptr + 720 * 1280;
|
||||
while (ptr != ptrEnd)
|
||||
{
|
||||
uint argb = *ptr << 8;
|
||||
*image = ((argb & 0x0000FF00) << 8) | ((argb & 0x00FF0000) >> 8) | ((argb & 0xFF000000) >> 24) | 0xFF000000;
|
||||
image++; ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
bmp.UnlockBits(img);
|
||||
}
|
||||
|
||||
public delegate void ApplyDelegate(byte[] data);
|
||||
|
||||
public void ApplyRGBAInternal(byte[] data)
|
||||
{
|
||||
if (CaptureBox.InvokeRequired)
|
||||
{
|
||||
var d = new ApplyDelegate(ApplyRGBAInternal);
|
||||
var d = new ApplyDelegate(ApplyDataImpl);
|
||||
CaptureBox.Invoke(d, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyRGBAToPictureBox(CaptureBox, data);
|
||||
ApplyModeDelegate(CaptureBox, data);
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue