Merge branch 'options-menu' into options-menu-and-installer

This commit is contained in:
Hyper 2024-12-03 20:17:18 +00:00
commit 7d19a7603e
19 changed files with 283 additions and 258 deletions

View file

@ -82,7 +82,7 @@ set(SWA_APU_CXX_SOURCES
if(SWA_XAUDIO2)
list(APPEND SWA_APU_CXX_SOURCES "apu/driver/xaudio_driver.cpp")
else()
list(APPEND SWA_APU_CXX_SOURCES "apu/driver/sdl2_driver.cpp")
list(APPEND SWA_APU_CXX_SOURCES "apu/driver/miniaudio_driver.cpp")
endif()
set(SWA_HID_CXX_SOURCES
@ -195,8 +195,8 @@ find_package(unofficial-concurrentqueue REQUIRED)
find_package(imgui CONFIG REQUIRED)
find_package(magic_enum CONFIG REQUIRED)
find_package(unofficial-tiny-aes-c CONFIG REQUIRED)
find_path(READERWRITERQUEUE_INCLUDE_DIRS "readerwriterqueue/atomicops.h")
find_package(nfd CONFIG REQUIRED)
find_path(MINIAUDIO_INCLUDE_DIRS "miniaudio.h")
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/D3D12)
add_custom_command(TARGET UnleashedRecomp POST_BUILD
@ -245,7 +245,7 @@ target_include_directories(UnleashedRecomp PRIVATE
${LIBMSPACK_PATH}
${Stb_INCLUDE_DIR}
${SMOLV_SOURCE_DIR}
${READERWRITERQUEUE_INCLUDE_DIRS}
${MINIAUDIO_INCLUDE_DIRS}
)
target_precompile_headers(UnleashedRecomp PUBLIC ${SWA_PRECOMPILED_HEADERS})
@ -315,6 +315,7 @@ generate_aggregate_header(
set(RESOURCES_SOURCE_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources")
set(RESOURCES_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/res")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/achievements_menu/trophy.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/achievements_menu/trophy.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_trophy")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/arrow_circle.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/arrow_circle.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_arrow_circle")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_001.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_001.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_install_001")
@ -326,6 +327,8 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/in
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_007.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_007.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_install_007")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_008.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_008.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_install_008")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/miles_electric_icon.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/miles_electric_icon.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_miles_electric_icon")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/general_window.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/general_window.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_general_window")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fade.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fade.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_select_fade")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fill.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fill.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_select_fill")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon.bmp" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_game_icon")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon_night.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon_night.bmp" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_game_icon_night")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/pause.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/pause.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_pause")

View file

@ -0,0 +1,56 @@
#include "miniaudio_driver.h"
#include <cpu/code_cache.h>
#include <cpu/guest_thread.h>
#include <cpu/guest_code.h>
#include <kernel/heap.h>
#define MINIAUDIO_IMPLEMENTATION
#include <miniaudio.h>
static PPCFunc* g_clientCallback{};
static DWORD g_clientCallbackParam{}; // pointer in guest memory
static ma_device g_audioDevice{};
static std::unique_ptr<GuestThreadContext> g_audioCtx;
static uint32_t* g_audioOutput;
static void AudioCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
if (g_audioCtx == nullptr)
g_audioCtx = std::make_unique<GuestThreadContext>(0);
g_audioCtx->ppcContext.r3.u64 = g_clientCallbackParam;
g_audioOutput = reinterpret_cast<uint32_t*>(pOutput);
(*g_clientCallback)(g_audioCtx->ppcContext, reinterpret_cast<uint8_t*>(g_memory.base));
}
void XAudioInitializeSystem()
{
ma_device_config deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.sampleRate = XAUDIO_SAMPLES_HZ;
deviceConfig.periodSizeInFrames = XAUDIO_NUM_SAMPLES;
deviceConfig.noPreSilencedOutputBuffer = true;
deviceConfig.dataCallback = AudioCallback;
deviceConfig.playback.format = ma_format_f32;
deviceConfig.playback.channels = XAUDIO_NUM_CHANNELS;
ma_device_init(nullptr, &deviceConfig, &g_audioDevice);
}
void XAudioRegisterClient(PPCFunc* callback, uint32_t param)
{
auto* pClientParam = static_cast<uint32_t*>(g_userHeap.Alloc(sizeof(param)));
ByteSwap(param);
*pClientParam = param;
g_clientCallbackParam = g_memory.MapVirtual(pClientParam);
g_clientCallback = callback;
ma_device_start(&g_audioDevice);
}
void XAudioSubmitFrame(void* samples)
{
for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++)
{
for (size_t j = 0; j < XAUDIO_NUM_CHANNELS; j++)
g_audioOutput[i * XAUDIO_NUM_CHANNELS + j] = std::byteswap(((uint32_t*)samples)[j * XAUDIO_NUM_SAMPLES + i]);
}
}

View file

@ -1,3 +1,2 @@
#pragma once
#include <SDL.h>
#include <apu/audio.h>

View file

@ -1,81 +0,0 @@
#include "sdl2_driver.h"
#include <cpu/code_cache.h>
#include <cpu/guest_thread.h>
#include <cpu/guest_code.h>
#include <kernel/heap.h>
#define SDLAUDIO_DRIVER_KEY (uint32_t)('SDLA')
static constexpr uint32_t AUDIO_FRAME_SIZE = XAUDIO_NUM_SAMPLES * XAUDIO_NUM_CHANNELS;
static std::atomic<PPCFunc*> g_clientCallback{};
static DWORD g_clientCallbackParam{}; // pointer in guest memory
static SDL_AudioDeviceID g_audioDevice{};
static moodycamel::BlockingReaderWriterCircularBuffer<std::array<uint32_t, AUDIO_FRAME_SIZE>> g_audioQueue(16);
static void SDLAudioCallback(void*, uint8_t* frames, int len)
{
std::array<uint32_t, AUDIO_FRAME_SIZE> audioFrame;
if (g_audioQueue.try_dequeue(audioFrame))
memcpy(frames, &audioFrame, sizeof(audioFrame));
else
memset(frames, 0, len);
}
static PPC_FUNC(DriverLoop)
{
GuestThread::SetThreadName(GetCurrentThreadId(), "Audio Driver");
while (true)
{
if (!g_clientCallback)
continue;
ctx.r3.u64 = g_clientCallbackParam;
GuestCode::Run((void*)g_clientCallback, &ctx);
}
}
void XAudioInitializeSystem()
{
SDL_SetHint(SDL_HINT_AUDIO_CATEGORY, "playback");
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "SWA");
auto err = SDL_InitSubSystem(SDL_INIT_AUDIO);
SDL_AudioSpec spec{};
spec.freq = XAUDIO_SAMPLES_HZ;
spec.format = AUDIO_F32SYS;
spec.channels = XAUDIO_NUM_CHANNELS;
spec.samples = XAUDIO_NUM_SAMPLES;
spec.callback = SDLAudioCallback;
g_audioDevice = SDL_OpenAudioDevice(nullptr, false, &spec, &spec, 0);
assert(g_audioDevice);
SDL_PauseAudioDevice(g_audioDevice, 0);
KeInsertHostFunction(SDLAUDIO_DRIVER_KEY, DriverLoop);
GuestThread::Start(SDLAUDIO_DRIVER_KEY, 0, 0, nullptr);
}
void XAudioRegisterClient(PPCFunc* callback, uint32_t param)
{
auto* pClientParam = static_cast<uint32_t*>(g_userHeap.Alloc(sizeof(param)));
ByteSwap(param);
*pClientParam = param;
g_clientCallbackParam = g_memory.MapVirtual(pClientParam);
g_clientCallback = callback;
}
void XAudioSubmitFrame(void* samples)
{
std::array<uint32_t, AUDIO_FRAME_SIZE> audioFrame;
for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++)
{
for (size_t j = 0; j < XAUDIO_NUM_CHANNELS; j++)
audioFrame[i * XAUDIO_NUM_CHANNELS + j] = std::byteswap(((uint32_t*)samples)[j * XAUDIO_NUM_SAMPLES + i]);
}
g_audioQueue.wait_enqueue(audioFrame);
}

View file

@ -4,7 +4,7 @@
struct GuestCode
{
inline static void Run(void* hostAddress, PPCContext* ctx, void* baseAddress, void* callStack)
inline static void Run(void* hostAddress, PPCContext* ctx, void* baseAddress)
{
ctx->fpscr.loadFromHost();
reinterpret_cast<PPCFunc*>(hostAddress)(*ctx, reinterpret_cast<uint8_t*>(baseAddress));

View file

@ -11,24 +11,15 @@ constexpr size_t PCR_SIZE = 0xAB0;
constexpr size_t TLS_SIZE = 0x100;
constexpr size_t TEB_SIZE = 0x2E0;
constexpr size_t STACK_SIZE = 0x40000;
constexpr size_t CALL_STACK_SIZE = 0x8000;
constexpr size_t TOTAL_SIZE = PCR_SIZE + TLS_SIZE + TEB_SIZE + STACK_SIZE + CALL_STACK_SIZE;
constexpr size_t TOTAL_SIZE = PCR_SIZE + TLS_SIZE + TEB_SIZE + STACK_SIZE;
constexpr size_t TEB_OFFSET = PCR_SIZE + TLS_SIZE;
DWORD GuestThread::Start(uint32_t function)
GuestThreadContext::GuestThreadContext(uint32_t cpuNumber)
{
const GuestThreadParameter parameter{ function };
return Start(parameter);
}
DWORD GuestThread::Start(const GuestThreadParameter& parameter)
{
auto* thread = (uint8_t*)g_userHeap.Alloc(TOTAL_SIZE);
const auto procMask = (uint8_t)(parameter.flags >> 24);
const auto cpuNumber = procMask == 0 ? 0 : 7 - std::countl_zero(procMask);
assert(thread == nullptr);
thread = (uint8_t*)g_userHeap.Alloc(TOTAL_SIZE);
memset(thread, 0, TOTAL_SIZE);
*(uint32_t*)thread = std::byteswap(g_memory.MapVirtual(thread + PCR_SIZE)); // tls pointer
@ -38,18 +29,36 @@ DWORD GuestThread::Start(const GuestThreadParameter& parameter)
*(uint32_t*)(thread + PCR_SIZE + 0x10) = 0xFFFFFFFF; // that one TLS entry that felt quirky
*(uint32_t*)(thread + PCR_SIZE + TLS_SIZE + 0x14C) = std::byteswap(GetCurrentThreadId()); // thread id
PPCContext ppcContext{};
ppcContext.fn = (uint8_t*)g_codeCache.bucket;
ppcContext.r1.u64 = g_memory.MapVirtual(thread + PCR_SIZE + TLS_SIZE + TEB_SIZE + STACK_SIZE); // stack pointer
ppcContext.r3.u64 = parameter.value;
ppcContext.r13.u64 = g_memory.MapVirtual(thread);
assert(GetPPCContext() == nullptr);
SetPPCContext(ppcContext);
}
GuestCode::Run(g_codeCache.Find(parameter.function), &ppcContext, g_memory.Translate(0), g_memory.Translate(ppcContext.r1.u32));
GuestThreadContext::~GuestThreadContext()
{
g_userHeap.Free(thread);
}
return (DWORD)ppcContext.r3.u64;
DWORD GuestThread::Start(uint32_t function)
{
const GuestThreadParameter parameter{ function };
return Start(parameter);
}
DWORD GuestThread::Start(const GuestThreadParameter& parameter)
{
const auto procMask = (uint8_t)(parameter.flags >> 24);
const auto cpuNumber = procMask == 0 ? 0 : 7 - std::countl_zero(procMask);
GuestThreadContext ctx(cpuNumber);
ctx.ppcContext.r3.u64 = parameter.value;
GuestCode::Run(g_codeCache.Find(parameter.function), &ctx.ppcContext, g_memory.Translate(0));
return (DWORD)ctx.ppcContext.r3.u64;
}
DWORD HostThreadStart(void* pParameter)

View file

@ -8,6 +8,15 @@ struct GuestThreadParameter
uint32_t flags;
};
struct GuestThreadContext
{
PPCContext ppcContext{};
uint8_t* thread = nullptr;
GuestThreadContext(uint32_t cpuNumber);
~GuestThreadContext();
};
struct GuestThread
{
static DWORD Start(uint32_t function);

View file

@ -4638,7 +4638,7 @@ static void EnqueueGraphicsPipelineCompilation(const PipelineState& pipelineStat
if (shouldCompile)
{
if (databaseDataHolderPair.counter == nullptr && databaseDataHolderPair.holder.databaseData.get() != nullptr)
databaseDataHolderPair.counter = std::make_unique<DatabaseDataHolder>(std::move(databaseDataHolderPair.holder));
databaseDataHolderPair.counter = std::make_shared<DatabaseDataHolder>(std::move(databaseDataHolderPair.holder));
PipelineStateQueueItem queueItem;
queueItem.pipelineHash = hash;

View file

@ -42,6 +42,7 @@ GUEST_FUNCTION_HOOK(sub_82BD4CA8, OutputDebugStringA);
GUEST_FUNCTION_HOOK(sub_82BD4AC8, QueryPerformanceCounterImpl);
GUEST_FUNCTION_HOOK(sub_831CD040, QueryPerformanceFrequencyImpl);
GUEST_FUNCTION_HOOK(sub_831CDAD0, GetTickCount);
GUEST_FUNCTION_HOOK(sub_82BD4BC0, GlobalMemoryStatusImpl);

View file

@ -62,7 +62,7 @@ bool AudioPatches::CanAttenuate()
auto version = GetPlatformVersion();
m_isAttenuationSupported = version.Major == 10 && version.Build >= 17763;
m_isAttenuationSupported = version.Major >= 10 && version.Build >= 17763;
return m_isAttenuationSupported;
#else

View file

@ -33,7 +33,6 @@
#include <wrl/client.h>
#include <smolv.h>
#include <print>
#include <readerwriterqueue/readerwritercircularbuffer.h>
using Microsoft::WRL::ComPtr;

View file

@ -8,6 +8,9 @@
#include <user/config.h>
#include <app.h>
#include <exports.h>
#include <res/images/achievements_menu/trophy.dds.h>
#include <res/images/common/general_window.dds.h>
#include <res/images/common/select_fill.dds.h>
constexpr double HEADER_CONTAINER_INTRO_MOTION_START = 0;
constexpr double HEADER_CONTAINER_INTRO_MOTION_END = 15;
@ -23,14 +26,12 @@ constexpr double CONTENT_CONTAINER_COMMON_MOTION_END = 12;
constexpr double COUNTER_INTRO_FADE_START = 15;
constexpr double COUNTER_INTRO_FADE_END = 16;
constexpr double COUNTER_SPRITE_FRAME_START = 0;
constexpr double COUNTER_SPRITE_FRAME_END = 30;
constexpr double SELECTION_CONTAINER_BREATHE = 30;
static bool g_isClosing = false;
static double g_appearTime = 0;
static double g_appearTime;
static std::vector<std::tuple<Achievement, time_t>> g_achievements;
@ -39,6 +40,8 @@ static ImFont* g_fntNewRodinDB;
static ImFont* g_fntNewRodinUB;
static std::unique_ptr<GuestTexture> g_upTrophyIcon;
static std::unique_ptr<GuestTexture> g_upSelectionCursor;
static std::unique_ptr<GuestTexture> g_upWindow;
static int g_firstVisibleRowIndex;
static int g_selectedRowIndex;
@ -63,42 +66,8 @@ static void ResetSelection()
static void DrawContainer(ImVec2 min, ImVec2 max, ImU32 gradientTop, ImU32 gradientBottom, float alpha = 1, float cornerRadius = 25)
{
auto drawList = ImGui::GetForegroundDrawList();
auto vertices = GetPauseContainerVertices(min, max, cornerRadius);
// TODO: add a drop shadow.
SetGradient(min, max, gradientTop, gradientBottom);
drawList->AddConvexPolyFilled(vertices.data(), vertices.size(), IM_COL32(255, 255, 255, 255 * alpha));
ResetGradient();
drawList->AddPolyline(vertices.data(), vertices.size(), IM_COL32(247, 247, 247, 255 * alpha), true, Scale(2.5f));
for (int i = 0; i < vertices.size(); i++)
{
vertices[i].x -= Scale(0.4f);
vertices[i].y -= Scale(0.2f);
}
auto colLineTop = IM_COL32(165, 170, 165, 230 * alpha);
auto colLineBottom = IM_COL32(190, 190, 190, 230 * alpha);
auto lineThickness = Scale(1);
// Top left corner bottom to top left corner top.
drawList->AddLine(vertices[0], vertices[1], colLineTop, lineThickness * Scale(0.5f));
// Top left corner bottom to bottom left.
drawList->AddRectFilledMultiColor
(
{ /* X */ vertices[0].x - Scale(0.2f), /* Y */ vertices[0].y },
{ /* X */ vertices[6].x + lineThickness - Scale(0.2f), /* Y */ vertices[6].y },
colLineTop,
colLineTop,
colLineBottom,
colLineBottom
);
// Top left corner top to top right.
drawList->AddLine(vertices[1], vertices[2], colLineTop, lineThickness);
DrawPauseContainer(g_upWindow, min, max, alpha);
drawList->PushClipRect({ min.x, min.y + Scale(20) }, { max.x, max.y - Scale(5) });
}
@ -106,20 +75,39 @@ static void DrawContainer(ImVec2 min, ImVec2 max, ImU32 gradientTop, ImU32 gradi
static void DrawSelectionContainer(ImVec2 min, ImVec2 max)
{
auto drawList = ImGui::GetForegroundDrawList();
auto vertices = GetPauseContainerVertices(min, max, 10);
static auto breatheStart = ImGui::GetTime();
auto alpha = Lerp(1.0f, 0.75f, (sin((ImGui::GetTime() - breatheStart) * (2.0f * M_PI / (55.0f / 60.0f))) + 1.0f) / 2.0f);
auto colour = IM_COL32(255, 255, 255, 255 * alpha);
SetGradient(min, max, IM_COL32(255, 246, 0, 129), IM_COL32(255, 194, 0, 118 * alpha));
drawList->AddConvexPolyFilled(vertices.data(), vertices.size(), IM_COL32(255, 255, 255, 255 * alpha));
ResetGradient();
auto commonWidth = Scale(11);
auto commonHeight = Scale(24);
auto tl = PIXELS_TO_UV_COORDS(64, 64, 0, 0, 11, 24);
auto tc = PIXELS_TO_UV_COORDS(64, 64, 11, 0, 8, 24);
auto tr = PIXELS_TO_UV_COORDS(64, 64, 19, 0, 11, 24);
auto cl = PIXELS_TO_UV_COORDS(64, 64, 0, 24, 11, 2);
auto cc = PIXELS_TO_UV_COORDS(64, 64, 11, 24, 8, 2);
auto cr = PIXELS_TO_UV_COORDS(64, 64, 19, 24, 11, 2);
auto bl = PIXELS_TO_UV_COORDS(64, 64, 0, 26, 11, 24);
auto bc = PIXELS_TO_UV_COORDS(64, 64, 11, 26, 8, 24);
auto br = PIXELS_TO_UV_COORDS(64, 64, 19, 26, 11, 24);
drawList->AddImage(g_upSelectionCursor.get(), min, { min.x + commonWidth, min.y + commonHeight }, GET_UV_COORDS(tl), colour);
drawList->AddImage(g_upSelectionCursor.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, min.y + commonHeight }, GET_UV_COORDS(tc), colour);
drawList->AddImage(g_upSelectionCursor.get(), { max.x - commonWidth, min.y }, { max.x, min.y + commonHeight }, GET_UV_COORDS(tr), colour);
drawList->AddImage(g_upSelectionCursor.get(), { min.x, min.y + commonHeight }, { min.x + commonWidth, max.y - commonHeight }, GET_UV_COORDS(cl), colour);
drawList->AddImage(g_upSelectionCursor.get(), { min.x + commonWidth, min.y + commonHeight }, { max.x - commonWidth, max.y - commonHeight }, GET_UV_COORDS(cc), colour);
drawList->AddImage(g_upSelectionCursor.get(), { max.x - commonWidth, min.y + commonHeight }, { max.x, max.y - commonHeight }, GET_UV_COORDS(cr), colour);
drawList->AddImage(g_upSelectionCursor.get(), { min.x, max.y - commonHeight }, { min.x + commonWidth, max.y }, GET_UV_COORDS(bl), colour);
drawList->AddImage(g_upSelectionCursor.get(), { min.x + commonWidth, max.y - commonHeight }, { max.x - commonWidth, max.y }, GET_UV_COORDS(bc), colour);
drawList->AddImage(g_upSelectionCursor.get(), { max.x - commonWidth, max.y - commonHeight }, { max.x, max.y }, GET_UV_COORDS(br), colour);
}
static void DrawHeaderContainer(const char* text)
{
auto drawList = ImGui::GetForegroundDrawList();
auto fontSize = Scale(26);
auto fontSize = Scale(24);
auto textSize = g_fntNewRodinUB->CalcTextSizeA(fontSize, FLT_MAX, 0, text);
auto cornerRadius = 23;
auto textMarginX = Scale(16) + (Scale(cornerRadius) / 2);
@ -134,26 +122,25 @@ static void DrawHeaderContainer(const char* text)
// Slide animation.
auto containerMarginX = g_isClosing
? Hermite(256, 156, containerMotion)
: Hermite(156, 256, containerMotion);
? Hermite(251, 151, containerMotion)
: Hermite(151, 251, containerMotion);
// Transparency fade animation.
auto alpha = g_isClosing
? Lerp(1, 0, colourMotion)
: Lerp(0, 1, colourMotion);
ImVec2 min = { Scale(containerMarginX), Scale(138) };
ImVec2 max = { min.x + textMarginX * 2 + textSize.x, Scale(185) };
ImVec2 min = { Scale(containerMarginX), Scale(136) };
ImVec2 max = { min.x + textMarginX * 2 + textSize.x + Scale(5), Scale(196) };
DrawContainer(min, max, IM_COL32(140, 142, 140, 201), IM_COL32(66, 65, 66, 234), alpha, cornerRadius);
drawList->PopClipRect();
DrawPauseHeaderContainer(g_upWindow, min, max, alpha);
// TODO: skew this text and apply bevel.
DrawTextWithOutline<int>
(
g_fntNewRodinUB,
fontSize,
{ /* X */ min.x + textMarginX, /* Y */ min.y + ((max.y - min.y) - textSize.y) / 2 },
{ /* X */ min.x + textMarginX, /* Y */ min.y + ((max.y - min.y) - textSize.y) / 2 - Scale(5) },
IM_COL32(255, 255, 255, 255 * alpha),
text,
3,
@ -168,9 +155,9 @@ static void DrawAchievement(int rowIndex, float yOffset, Achievement& achievemen
auto clipRectMin = drawList->GetClipRectMin();
auto clipRectMax = drawList->GetClipRectMax();
auto itemWidth = Scale(708);
auto itemWidth = Scale(700);
auto itemHeight = Scale(94);
auto itemMarginX = Scale(13);
auto itemMarginX = Scale(18);
auto imageMarginX = Scale(25);
auto imageMarginY = Scale(18);
auto imageSize = Scale(60);
@ -371,10 +358,16 @@ static void DrawAchievementTotal(ImVec2 min, ImVec2 max)
ImVec2 imageMin = { max.x - imageSize - imageMarginX, min.y - imageSize - imageMarginY };
ImVec2 imageMax = { imageMin.x + imageSize, imageMin.y + imageSize };
auto frm = int32_t(floor(ImGui::GetTime() * 30.0f)) % 30;
auto w = 256.0f / 7680.0f;
auto uv0 = ImVec2(frm * w, 0);
auto uv1 = ImVec2((frm + 1) * w, 1);
constexpr auto columns = 8;
constexpr auto rows = 4;
constexpr auto spriteSize = 256.0f;
constexpr auto textureWidth = 2048.0f;
constexpr auto textureHeight = 1024.0f;
auto frameIndex = int32_t(floor(ImGui::GetTime() * 30.0f)) % 30;
auto columnIndex = frameIndex % columns;
auto rowIndex = frameIndex / columns;
auto uv0 = ImVec2(columnIndex * spriteSize / textureWidth, rowIndex * spriteSize / textureHeight);
auto uv1 = ImVec2((columnIndex + 1) * spriteSize / textureWidth, (rowIndex + 1) * spriteSize / textureHeight);
drawList->AddImage(g_upTrophyIcon.get(), imageMin, imageMax, uv0, uv1, IM_COL32(255, 255, 255, 255 * alpha));
@ -404,20 +397,20 @@ static void DrawContentContainer()
: ComputeMotion(g_appearTime, CONTENT_CONTAINER_COMMON_MOTION_START, CONTENT_CONTAINER_COMMON_MOTION_END);
auto minX = g_isClosing
? Hermite(256, 306, motion)
: Hermite(306, 256, motion);
? Hermite(251, 301, motion)
: Hermite(301, 251, motion);
auto minY = g_isClosing
? Hermite(192, 209, motion)
: Hermite(209, 192, motion);
? Hermite(189, 206, motion)
: Hermite(206, 189, motion);
auto maxX = g_isClosing
? Hermite(1026, 973, motion)
: Hermite(973, 1026, motion);
? Hermite(1031, 978, motion)
: Hermite(978, 1031, motion);
auto maxY = g_isClosing
? Hermite(601, 569, motion)
: Hermite(569, 601, motion);
? Hermite(604, 573, motion)
: Hermite(573, 604, motion);
ImVec2 min = { Scale(minX), Scale(minY) };
ImVec2 max = { Scale(maxX), Scale(maxY) };
@ -449,8 +442,8 @@ static void DrawContentContainer()
// Draw separators.
for (int i = 1; i <= 3; i++)
{
auto lineMarginLeft = Scale(31);
auto lineMarginRight = Scale(46);
auto lineMarginLeft = Scale(35);
auto lineMarginRight = Scale(55);
auto lineMarginY = Scale(2);
ImVec2 lineMin = { clipRectMin.x + lineMarginLeft, clipRectMin.y + itemHeight * i + lineMarginY };
@ -568,34 +561,35 @@ static void DrawContentContainer()
// Draw scroll bar
if (rowCount > visibleRowCount)
{
float cornerRadius = Scale(25.0f);
float totalHeight = (clipRectMax.y - clipRectMin.y - cornerRadius) - Scale(3.0f);
float cornerRadius = Scale(25);
float totalHeight = (clipRectMax.y - clipRectMin.y - cornerRadius) - Scale(3);
float heightRatio = float(visibleRowCount) / float(rowCount);
float offsetRatio = float(g_firstVisibleRowIndex) / float(rowCount);
float offsetX = clipRectMax.x - Scale(31.0f);
float offsetY = offsetRatio * totalHeight + clipRectMin.y + Scale(4.0f);
float lineThickness = Scale(1.0f);
float innerMarginX = Scale(2.0f);
float outerMarginX = Scale(16.0f);
float offsetX = clipRectMax.x - Scale(39);
float offsetY = offsetRatio * totalHeight + clipRectMin.y + Scale(4);
float maxY = max.y - cornerRadius - Scale(3);
float lineThickness = Scale(1);
float innerMarginX = Scale(2);
float outerMarginX = Scale(24);
// Outline
drawList->AddRect
(
{ /* X */ offsetX - lineThickness, /* Y */ clipRectMin.y - lineThickness },
{ /* X */ clipRectMax.x - outerMarginX + lineThickness, /* Y */ max.y - cornerRadius + lineThickness },
{ /* X */ clipRectMax.x - outerMarginX + lineThickness, /* Y */ maxY + lineThickness },
IM_COL32(255, 255, 255, 155),
Scale(0.5f)
Scale(1)
);
// Background
drawList->AddRectFilledMultiColor
(
{ /* X */ offsetX, /* Y */ clipRectMin.y },
{ /* X */ clipRectMax.x - outerMarginX, /* Y */ max.y - cornerRadius },
IM_COL32(82, 85, 82, 186),
IM_COL32(82, 85, 82, 186),
IM_COL32(74, 73, 74, 185),
IM_COL32(74, 73, 74, 185)
{ /* X */ clipRectMax.x - outerMarginX, /* Y */ maxY },
IM_COL32(123, 125, 123, 255),
IM_COL32(123, 125, 123, 255),
IM_COL32(97, 99, 97, 255),
IM_COL32(97, 99, 97, 255)
);
// Scroll Bar Outline
@ -629,13 +623,9 @@ void AchievementMenu::Init()
g_fntNewRodinDB = io.Fonts->AddFontFromFileTTF("FOT-NewRodinPro-DB.otf", 20.0f * FONT_SCALE);
g_fntNewRodinUB = io.Fonts->AddFontFromFileTTF("FOT-NewRodinPro-UB.otf", 20.0f * FONT_SCALE);
size_t bufferSize = 0;
auto buffer = ReadAllBytes("achievements_trophy.png", bufferSize);
if (!bufferSize)
return;
g_upTrophyIcon = LoadTexture(buffer.get(), bufferSize);
g_upTrophyIcon = LoadTexture(g_trophy, sizeof(g_trophy));
g_upSelectionCursor = LoadTexture(g_select_fill, sizeof(g_select_fill));
g_upWindow = LoadTexture(g_general_window, sizeof(g_general_window));
}
void AchievementMenu::Draw()

View file

@ -8,6 +8,7 @@
#include <user/achievement_data.h>
#include <app.h>
#include <exports.h>
#include <res/images/common/general_window.dds.h>
constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0;
constexpr double OVERLAY_CONTAINER_COMMON_MOTION_END = 11;
@ -26,6 +27,8 @@ static Achievement g_achievement;
static ImFont* g_fntSeurat;
static std::unique_ptr<GuestTexture> g_upWindow;
static bool DrawContainer(ImVec2 min, ImVec2 max, float cornerRadius = 25)
{
auto drawList = ImGui::GetForegroundDrawList();
@ -51,8 +54,6 @@ static bool DrawContainer(ImVec2 min, ImVec2 max, float cornerRadius = 25)
max.y = Hermite(centreY, max.y, containerMotion);
}
auto vertices = GetPauseContainerVertices(min, max, cornerRadius);
// Transparency fade animation.
auto colourMotion = g_isClosing
? ComputeMotion(g_appearTime, OVERLAY_CONTAINER_OUTRO_FADE_START, OVERLAY_CONTAINER_OUTRO_FADE_END)
@ -62,46 +63,7 @@ static bool DrawContainer(ImVec2 min, ImVec2 max, float cornerRadius = 25)
? Hermite(1, 0, colourMotion)
: Hermite(0, 1, colourMotion);
auto colShadow = IM_COL32(0, 0, 0, 156 * alpha);
auto colGradientTop = IM_COL32(197, 194, 197, 200 * alpha);
auto colGradientBottom = IM_COL32(115, 113, 115, 236 * alpha);
// TODO: add a drop shadow.
// Draw vertices with gradient.
SetGradient(min, max, colGradientTop, colGradientBottom);
drawList->AddConvexPolyFilled(vertices.data(), vertices.size(), IM_COL32(255, 255, 255, 255 * alpha));
ResetGradient();
// Draw outline.
drawList->AddPolyline
(
vertices.data(),
vertices.size(),
IM_COL32(247, 247, 247, 255 * alpha),
true,
Scale(2.5f)
);
// Offset vertices to draw 3D effect lines.
for (int i = 0; i < vertices.size(); i++)
{
vertices[i].x -= Scale(0.4f);
vertices[i].y -= Scale(0.2f);
}
auto colLineTop = IM_COL32(165, 170, 165, 230 * alpha);
auto colLineBottom = IM_COL32(190, 190, 190, 230 * alpha);
auto lineThickness = Scale(1.0f);
// Top left corner bottom to top left corner top.
drawList->AddLine(vertices[0], vertices[1], colLineTop, lineThickness * 0.5f);
// Top left corner bottom to bottom left.
drawList->AddRectFilledMultiColor({ vertices[0].x - 0.2f, vertices[0].y }, { vertices[6].x + lineThickness - 0.2f, vertices[6].y }, colLineTop, colLineTop, colLineBottom, colLineBottom);
// Top left corner top to top right.
drawList->AddLine(vertices[1], vertices[2], colLineTop, lineThickness);
DrawPauseContainer(g_upWindow, min, max, alpha);
drawList->PushClipRect(min, max);
@ -115,6 +77,8 @@ void AchievementOverlay::Init()
constexpr float FONT_SCALE = 2.0f;
g_fntSeurat = io.Fonts->AddFontFromFileTTF("FOT-SeuratPro-M.otf", 24.0f * FONT_SCALE);
g_upWindow = LoadTexture(g_general_window, sizeof(g_general_window));
}
void AchievementOverlay::Draw()
@ -138,18 +102,18 @@ void AchievementOverlay::Draw()
auto maxSize = std::max(headerSize.x, bodySize.x);
// Calculate image margins.
auto imageMarginX = Scale(20);
auto imageMarginY = Scale(20);
auto imageMarginX = Scale(25);
auto imageMarginY = Scale(22.5f);
auto imageSize = Scale(60);
// Calculate text margins.
auto textMarginX = imageMarginX * 2 + imageSize;
auto textMarginX = imageMarginX * 2 + imageSize - Scale(5);
auto textMarginY = imageMarginY + Scale(2);
auto containerWidth = imageMarginX + textMarginX + maxSize;
ImVec2 min = { (res.x / 2) - (containerWidth / 2), Scale(50) };
ImVec2 max = { min.x + containerWidth, min.y + Scale(100) };
ImVec2 min = { (res.x / 2) - (containerWidth / 2), Scale(55) };
ImVec2 max = { min.x + containerWidth, min.y + Scale(105) };
if (DrawContainer(min, max))
{

View file

@ -1,6 +1,7 @@
#pragma once
#include <gpu/imgui_common.h>
#include <gpu/video.h>
#include <app.h>
#define PIXELS_TO_UV_COORDS(textureWidth, textureHeight, x, y, width, height) \
@ -84,17 +85,65 @@ static std::vector<ImVec2> GetPauseContainerVertices(ImVec2 min, ImVec2 max, flo
return
{
{ min.x, min.y + cornerRadius },
{ min.x + cornerRadius, min.y },
{ max.x, min.y },
{ max.x, min.y + cornerRadius },
{ max.x, max.y - cornerRadius },
{ max.x - cornerRadius, max.y },
{ min.x, max.y },
{ min.x, max.y - cornerRadius }
{ min.x, min.y + cornerRadius }, // 0 - TL Corner Bottom
{ min.x + cornerRadius, min.y }, // 1 - TL Corner Top
{ max.x, min.y }, // 2 - TR Corner Top
{ max.x, min.y + cornerRadius }, // 3 - TR Corner Bottom
{ max.x, max.y - cornerRadius }, // 4 - BR Corner Top
{ max.x - cornerRadius, max.y }, // 5 - BR Corner Bottom
{ min.x, max.y }, // 6 - BL Corner Bottom
{ min.x, max.y - cornerRadius } // 7 - BL Corner Top
};
}
static void DrawPauseContainer(std::unique_ptr<GuestTexture>& texture, ImVec2 min, ImVec2 max, float alpha = 1)
{
auto drawList = ImGui::GetForegroundDrawList();
auto commonWidth = Scale(35);
auto commonHeight = Scale(35);
auto bottomHeight = Scale(5);
auto tl = PIXELS_TO_UV_COORDS(512, 512, 0, 0, 35, 35);
auto tc = PIXELS_TO_UV_COORDS(512, 512, 51, 0, 5, 35);
auto tr = PIXELS_TO_UV_COORDS(512, 512, 70, 0, 35, 35);
auto cl = PIXELS_TO_UV_COORDS(512, 512, 0, 35, 35, 235);
auto cc = PIXELS_TO_UV_COORDS(512, 512, 51, 35, 5, 235);
auto cr = PIXELS_TO_UV_COORDS(512, 512, 70, 35, 35, 235);
auto bl = PIXELS_TO_UV_COORDS(512, 512, 0, 270, 35, 40);
auto bc = PIXELS_TO_UV_COORDS(512, 512, 51, 270, 5, 40);
auto br = PIXELS_TO_UV_COORDS(512, 512, 70, 270, 35, 40);
auto colour = IM_COL32(255, 255, 255, 255 * alpha);
drawList->AddImage(texture.get(), min, { min.x + commonWidth, min.y + commonHeight }, GET_UV_COORDS(tl), colour);
drawList->AddImage(texture.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, min.y + commonHeight }, GET_UV_COORDS(tc), colour);
drawList->AddImage(texture.get(), { max.x - commonWidth, min.y }, { max.x, min.y + commonHeight }, GET_UV_COORDS(tr), colour);
drawList->AddImage(texture.get(), { min.x, min.y + commonHeight }, { min.x + commonWidth, max.y - commonHeight }, GET_UV_COORDS(cl), colour);
drawList->AddImage(texture.get(), { min.x + commonWidth, min.y + commonHeight }, { max.x - commonWidth, max.y - commonHeight }, GET_UV_COORDS(cc), colour);
drawList->AddImage(texture.get(), { max.x - commonWidth, min.y + commonHeight }, { max.x, max.y - commonHeight }, GET_UV_COORDS(cr), colour);
drawList->AddImage(texture.get(), { min.x, max.y - commonHeight }, { min.x + commonWidth, max.y + bottomHeight }, GET_UV_COORDS(bl), colour);
drawList->AddImage(texture.get(), { min.x + commonWidth, max.y - commonHeight }, { max.x - commonWidth, max.y + bottomHeight }, GET_UV_COORDS(bc), colour);
drawList->AddImage(texture.get(), { max.x - commonWidth, max.y - commonHeight }, { max.x, max.y + bottomHeight }, GET_UV_COORDS(br), colour);
}
static void DrawPauseHeaderContainer(std::unique_ptr<GuestTexture>& texture, ImVec2 min, ImVec2 max, float alpha = 1)
{
auto drawList = ImGui::GetForegroundDrawList();
auto commonWidth = Scale(35);
auto left = PIXELS_TO_UV_COORDS(512, 512, 0, 314, 35, 60);
auto centre = PIXELS_TO_UV_COORDS(512, 512, 51, 314, 5, 60);
auto right = PIXELS_TO_UV_COORDS(512, 512, 70, 314, 35, 60);
auto colour = IM_COL32(255, 255, 255, 255 * alpha);
drawList->AddImage(texture.get(), min, { min.x + commonWidth, max.y }, GET_UV_COORDS(left), colour);
drawList->AddImage(texture.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, max.y }, GET_UV_COORDS(centre), colour);
drawList->AddImage(texture.get(), { max.x - commonWidth, min.y }, max, GET_UV_COORDS(right), colour);
}
static void DrawTextWithMarquee(const ImFont* font, float fontSize, const ImVec2& pos, const ImVec2& min, const ImVec2& max, ImU32 color, const char* text, double time, double delay, double speed)
{
auto drawList = ImGui::GetForegroundDrawList();

View file

@ -3,7 +3,7 @@
#include <api/SWA.h>
#include <gpu/video.h>
#include <exports.h>
#include <res/images/pause.dds.h>
#include <res/images/common/select_fade.dds.h>
constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0;
constexpr double OVERLAY_CONTAINER_COMMON_MOTION_END = 11;
@ -139,9 +139,9 @@ void DrawButton(int rowIndex, float yOffset, float width, float height, std::str
auto colour = IM_COL32(255, 255, 255, 255 * alpha);
auto width = Scale(11);
auto left = PIXELS_TO_UV_COORDS(128, 128, 0, 0, 11, 50);
auto centre = PIXELS_TO_UV_COORDS(128, 128, 11, 0, 8, 50);
auto right = PIXELS_TO_UV_COORDS(128, 128, 19, 0, 11, 50);
auto left = PIXELS_TO_UV_COORDS(64, 64, 0, 0, 11, 50);
auto centre = PIXELS_TO_UV_COORDS(64, 64, 11, 0, 8, 50);
auto right = PIXELS_TO_UV_COORDS(64, 64, 19, 0, 11, 50);
drawList->AddImage(g_upSelectionCursor.get(), min, { min.x + width, max.y }, GET_UV_COORDS(left), colour);
drawList->AddImage(g_upSelectionCursor.get(), { min.x + width, min.y }, { max.x - width, max.y }, GET_UV_COORDS(centre), colour);
@ -176,7 +176,7 @@ void MessageWindow::Init()
g_fntSeurat = io.Fonts->AddFontFromFileTTF("FOT-SeuratPro-M.otf", 28.0f * FONT_SCALE);
g_upSelectionCursor = LoadTexture(g_pause, sizeof(g_pause));
g_upSelectionCursor = LoadTexture(g_select_fade, sizeof(g_select_fade));
}
void MessageWindow::Draw()

View file

@ -179,6 +179,10 @@ void Window::Init()
SDL_GetWindowWMInfo(s_pWindow, &info);
s_handle = info.info.win.window;
SetDarkTitleBar(true);
SDL_ShowWindow(s_pWindow);
}
void Window::Update()

View file

@ -5,6 +5,12 @@
#include <ui/window_events.h>
#include <user/config.h>
#if _WIN32
#include <dwmapi.h>
#include <kernel/platform.h>
#pragma comment(lib, "dwmapi.lib")
#endif
#define DEFAULT_WIDTH 1280
#define DEFAULT_HEIGHT 720
@ -67,6 +73,23 @@ public:
SDL_SetWindowTitle(s_pWindow, title ? title : GetTitle());
}
static void SetDarkTitleBar(bool isEnabled)
{
#if _WIN32
auto version = GetPlatformVersion();
if (version.Major < 10 || version.Build <= 17763)
return;
auto flag = version.Build >= 18985
? DWMWA_USE_IMMERSIVE_DARK_MODE
: 19; // DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1
const DWORD useImmersiveDarkMode = isEnabled;
DwmSetWindowAttribute(s_handle, flag, &useImmersiveDarkMode, sizeof(useImmersiveDarkMode));
#endif
}
static bool IsFullscreen()
{
return SDL_GetWindowFlags(s_pWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP;
@ -144,7 +167,7 @@ public:
static uint32_t GetWindowFlags()
{
uint32_t flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
uint32_t flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE;
if (Config::WindowState == EWindowState::Maximised)
flags |= SDL_WINDOW_MAXIMIZED;

@ -1 +1 @@
Subproject commit b04e3bdcd9e76a0717cec69822f4eb01cd97d5c6
Subproject commit 5b5ad2794a2c78d50dc6a85e71954fb6b9e80ae2

View file

@ -22,7 +22,7 @@
"features": [ "sdl2-binding" ]
},
"magic-enum",
"readerwriterqueue",
"nativefiledialog-extended"
"nativefiledialog-extended",
"miniaudio"
]
}