@ -1,4 +1,4 @@
|
||||
version: 1.62.3-{build}
|
||||
version: 1.63-{build}
|
||||
|
||||
image: Visual Studio 2022
|
||||
|
||||
|
@ -518,10 +518,14 @@ int S9xModifyCheatGroup(uint32 num, const std::string &name, const std::string &
|
||||
if (num >= Cheat.group.size())
|
||||
return -1;
|
||||
|
||||
bool enabled = Cheat.group[num].enabled;
|
||||
S9xDisableCheatGroup(num);
|
||||
|
||||
Cheat.group[num] = S9xCreateCheatGroup(name, cheat);
|
||||
|
||||
if (enabled)
|
||||
S9xEnableCheatGroup(num);
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <map>
|
||||
#include "glsl.h"
|
||||
#include "shader_helpers.h"
|
||||
#include "../vulkan/slang_helpers.hpp"
|
||||
#include "common/video/vulkan/slang_helpers.hpp"
|
||||
#include "shader_platform.h"
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
@ -8,7 +8,7 @@
|
||||
#define __GLSL_H
|
||||
|
||||
#include "snes9x.h"
|
||||
#include "../vulkan/slang_preset_ini.hpp"
|
||||
#include "common/video/vulkan/slang_preset_ini.hpp"
|
||||
#include "shader_platform.h"
|
||||
#include <deque>
|
||||
#include <limits.h>
|
@ -8,7 +8,7 @@
|
||||
#define __WAYLAND_EGL_CONTEXT_H
|
||||
|
||||
#include "opengl_context.hpp"
|
||||
#include "wayland_surface.hpp"
|
||||
#include "common/video/wayland/wayland_surface.hpp"
|
||||
|
||||
#include "glad/egl.h"
|
||||
#include <memory>
|
@ -1,5 +1,5 @@
|
||||
#include "slang_preset.hpp"
|
||||
#include "../external/SPIRV-Cross/spirv.hpp"
|
||||
#include "external/SPIRV-Cross/spirv.hpp"
|
||||
#include "slang_helpers.hpp"
|
||||
#include "slang_preset_ini.hpp"
|
||||
|
||||
@ -12,8 +12,8 @@
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <future>
|
||||
#include "../external/SPIRV-Cross/spirv_cross.hpp"
|
||||
#include "../external/SPIRV-Cross/spirv_glsl.hpp"
|
||||
#include "external/SPIRV-Cross/spirv_cross.hpp"
|
||||
#include "external/SPIRV-Cross/spirv_glsl.hpp"
|
||||
#include "slang_shader.hpp"
|
||||
|
||||
using std::string;
|
@ -7,9 +7,9 @@
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include "../external/glslang/glslang/Public/ShaderLang.h"
|
||||
#include "../external/glslang/SPIRV/GlslangToSpv.h"
|
||||
#include "../external/glslang/glslang/Public/ResourceLimits.h"
|
||||
#include "external/glslang/glslang/Public/ShaderLang.h"
|
||||
#include "external/glslang/SPIRV/GlslangToSpv.h"
|
||||
#include "external/glslang/glslang/Public/ResourceLimits.h"
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
@ -183,69 +183,88 @@ bool Context::init_command_pool()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Context::init_device(int preferred_device)
|
||||
static bool find_extension(std::vector<vk::ExtensionProperties> &props, const char *extension_string)
|
||||
{
|
||||
const char *required_extensions[] = {
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||
// VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME
|
||||
};
|
||||
auto check_extensions = [&](vk::PhysicalDevice &device) -> bool {
|
||||
auto [retval, props] = device.enumerateDeviceExtensionProperties();
|
||||
for (const auto &extension : required_extensions) {
|
||||
auto found = std::find_if(
|
||||
props.begin(), props.end(), [&](vk::ExtensionProperties &ext) {
|
||||
return (std::string(ext.extensionName.data()) == extension);
|
||||
});
|
||||
return found != props.end();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
return std::find_if(props.begin(),
|
||||
props.end(),
|
||||
[&](vk::ExtensionProperties &ext) {
|
||||
return (std::string(ext.extensionName.data()) == extension_string);
|
||||
}) != props.end();
|
||||
};
|
||||
|
||||
auto device_list = instance->enumeratePhysicalDevices().value;
|
||||
|
||||
if (preferred_device > -1 &&
|
||||
preferred_device < device_list.size() &&
|
||||
check_extensions(device_list[preferred_device]))
|
||||
{
|
||||
physical_device = device_list[preferred_device];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto &device : device_list)
|
||||
if (check_extensions(device))
|
||||
{
|
||||
physical_device = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
physical_device.getProperties(&physical_device_props);
|
||||
|
||||
graphics_queue_family_index = UINT32_MAX;
|
||||
auto queue_props = physical_device.getQueueFamilyProperties();
|
||||
static uint32_t find_graphics_queue(vk::PhysicalDevice &device)
|
||||
{
|
||||
auto queue_props = device.getQueueFamilyProperties();
|
||||
for (size_t i = 0; i < queue_props.size(); i++)
|
||||
{
|
||||
if (queue_props[i].queueFlags & vk::QueueFlagBits::eGraphics)
|
||||
{
|
||||
graphics_queue_family_index = i;
|
||||
break;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return UINT32_MAX;
|
||||
}
|
||||
|
||||
static bool check_extensions(std::vector<const char *> &required_extensions, vk::PhysicalDevice &device)
|
||||
{
|
||||
auto props = device.enumerateDeviceExtensionProperties().value;
|
||||
for (const auto &extension : required_extensions)
|
||||
{
|
||||
if (!find_extension(props, extension))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
bool Context::init_device(int preferred_device)
|
||||
{
|
||||
std::vector<const char *> required_extensions = {
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||
};
|
||||
|
||||
auto device_list = instance->enumeratePhysicalDevices().value;
|
||||
physical_device = nullptr;
|
||||
|
||||
if (preferred_device > -1 &&
|
||||
(size_t)preferred_device < device_list.size() &&
|
||||
check_extensions(required_extensions, device_list[preferred_device]))
|
||||
{
|
||||
physical_device = device_list[preferred_device];
|
||||
}
|
||||
|
||||
if (physical_device == nullptr)
|
||||
{
|
||||
for (auto &device : device_list)
|
||||
{
|
||||
if (check_extensions(required_extensions, device))
|
||||
{
|
||||
physical_device = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto extension_properties = physical_device.enumerateDeviceExtensionProperties().value;
|
||||
physical_device.getProperties(&physical_device_props);
|
||||
|
||||
graphics_queue_family_index = find_graphics_queue(physical_device);
|
||||
if (graphics_queue_family_index == UINT32_MAX)
|
||||
return false;
|
||||
|
||||
vk::DeviceQueueCreateInfo dqci({}, graphics_queue_family_index, 1);
|
||||
vk::DeviceCreateInfo dci({}, dqci, {}, required_extensions, {});
|
||||
std::vector<float> priorities = { 1.0f };
|
||||
vk::DeviceQueueCreateInfo dqci({}, graphics_queue_family_index, priorities);
|
||||
vk::DeviceCreateInfo dci({}, dqci, {}, required_extensions);
|
||||
|
||||
device = physical_device.createDevice(dci).value;
|
||||
queue = device.getQueue(graphics_queue_family_index, 0);
|
||||
|
||||
auto surface_formats = physical_device.getSurfaceFormatsKHR(surface.get()).value;
|
||||
auto format = std::find_if(surface_formats.begin(), surface_formats.end(), [](vk::SurfaceFormatKHR &f) {
|
||||
return (f.format == vk::Format::eB8G8R8A8Unorm);
|
||||
});
|
||||
|
||||
if (format == surface_formats.end())
|
||||
if (std::find_if(surface_formats.begin(),
|
||||
surface_formats.end(),
|
||||
[](vk::SurfaceFormatKHR &f) {
|
||||
return (f.format == vk::Format::eB8G8R8A8Unorm);
|
||||
}) == surface_formats.end())
|
||||
return false;
|
||||
|
||||
return true;
|
@ -8,8 +8,8 @@
|
||||
#define WINVER 0x599
|
||||
#endif
|
||||
#include <cstdint>
|
||||
#include "vulkan/vulkan_hpp_wrapper.hpp"
|
||||
#include "../external/VulkanMemoryAllocator-Hpp/include/vk_mem_alloc.hpp"
|
||||
#include "vulkan_hpp_wrapper.hpp"
|
||||
#include "external/VulkanMemoryAllocator-Hpp/include/vk_mem_alloc.hpp"
|
||||
#include "vulkan_swapchain.hpp"
|
||||
#include <memory>
|
||||
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include "vulkan/vulkan_hpp_wrapper.hpp"
|
||||
#include "vulkan_hpp_wrapper.hpp"
|
||||
#include "slang_shader.hpp"
|
||||
#include "vulkan_context.hpp"
|
||||
#include "vulkan_pipeline_image.hpp"
|
@ -1,5 +1,4 @@
|
||||
#include "vulkan_swapchain.hpp"
|
||||
#include <thread>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
@ -19,13 +18,9 @@ Swapchain::~Swapchain()
|
||||
{
|
||||
}
|
||||
|
||||
bool Swapchain::set_vsync(bool new_setting)
|
||||
void Swapchain::set_vsync(bool new_setting)
|
||||
{
|
||||
if (new_setting == vsync)
|
||||
return false;
|
||||
|
||||
vsync = new_setting;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Swapchain::on_render_pass_end(std::function<void ()> function)
|
||||
@ -80,13 +75,40 @@ void Swapchain::create_render_pass()
|
||||
|
||||
bool Swapchain::recreate(int new_width, int new_height)
|
||||
{
|
||||
device.waitIdle();
|
||||
if (swapchain_object)
|
||||
{
|
||||
device.waitIdle();
|
||||
wait_on_frames();
|
||||
}
|
||||
|
||||
return create(num_swapchain_images, new_width, new_height);
|
||||
}
|
||||
|
||||
vk::Image Swapchain::get_image()
|
||||
{
|
||||
return imageviewfbs[current_swapchain_image].image;
|
||||
return image_data[current_swapchain_image].image;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static bool vector_find(std::vector<T> haystack, T&& needle)
|
||||
{
|
||||
for (auto &elem : haystack)
|
||||
if (elem == needle)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
vk::PresentModeKHR Swapchain::get_present_mode() {
|
||||
auto present_mode = vk::PresentModeKHR::eFifo;
|
||||
|
||||
if (!vsync) {
|
||||
if (supports_mailbox)
|
||||
present_mode = vk::PresentModeKHR::eMailbox;
|
||||
if (supports_immediate)
|
||||
present_mode = vk::PresentModeKHR::eImmediate;
|
||||
}
|
||||
|
||||
return present_mode;
|
||||
}
|
||||
|
||||
bool Swapchain::check_and_resize(int width, int height)
|
||||
@ -115,7 +137,7 @@ bool Swapchain::check_and_resize(int width, int height)
|
||||
bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width, int new_height)
|
||||
{
|
||||
frames.clear();
|
||||
imageviewfbs.clear();
|
||||
image_data.clear();
|
||||
|
||||
auto surface_capabilities = physical_device.getSurfaceCapabilitiesKHR(surface).value;
|
||||
|
||||
@ -126,7 +148,7 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width,
|
||||
|
||||
// If extents aren't reported (Wayland), we have to rely on Wayland to report
|
||||
// the size, so keep current extent.
|
||||
if (surface_capabilities.currentExtent.width != -1)
|
||||
if (surface_capabilities.currentExtent.width != UINT32_MAX)
|
||||
extents = surface_capabilities.currentExtent;
|
||||
|
||||
uint32_t graphics_queue_index = 0;
|
||||
@ -164,11 +186,12 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width,
|
||||
extents.height = surface_capabilities.minImageExtent.height;
|
||||
|
||||
auto present_modes = physical_device.getSurfacePresentModesKHR(surface).value;
|
||||
auto tearing_present_mode = vk::PresentModeKHR::eFifo;
|
||||
if (std::find(present_modes.begin(), present_modes.end(), vk::PresentModeKHR::eImmediate) != present_modes.end())
|
||||
tearing_present_mode = vk::PresentModeKHR::eImmediate;
|
||||
if (std::find(present_modes.begin(), present_modes.end(), vk::PresentModeKHR::eMailbox) != present_modes.end())
|
||||
tearing_present_mode = vk::PresentModeKHR::eMailbox;
|
||||
supports_mailbox = vector_find(present_modes, vk::PresentModeKHR::eMailbox);
|
||||
supports_immediate = vector_find(present_modes, vk::PresentModeKHR::eImmediate);
|
||||
supports_relaxed = vector_find(present_modes, vk::PresentModeKHR::eFifoRelaxed);
|
||||
|
||||
auto swapchain_maintenance_info = vk::SwapchainPresentModesCreateInfoEXT{}
|
||||
.setPresentModes(present_modes);
|
||||
|
||||
auto swapchain_create_info = vk::SwapchainCreateInfoKHR{}
|
||||
.setMinImageCount(num_swapchain_images)
|
||||
@ -179,33 +202,38 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width,
|
||||
.setImageUsage(vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc)
|
||||
.setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque)
|
||||
.setClipped(true)
|
||||
.setPresentMode(vsync ? vk::PresentModeKHR::eFifo : tearing_present_mode)
|
||||
.setPresentMode(get_present_mode())
|
||||
.setSurface(surface)
|
||||
.setPreTransform(vk::SurfaceTransformFlagBitsKHR::eIdentity)
|
||||
.setImageArrayLayers(1)
|
||||
.setQueueFamilyIndices(graphics_queue_index);
|
||||
.setQueueFamilyIndices(graphics_queue_index)
|
||||
.setPNext(&swapchain_maintenance_info);
|
||||
|
||||
if (swapchain_object)
|
||||
swapchain_create_info.setOldSwapchain(swapchain_object.get());
|
||||
|
||||
try {
|
||||
swapchain_object = device.createSwapchainKHRUnique(swapchain_create_info).value;
|
||||
} catch (std::exception &e) {
|
||||
swapchain_object.reset();
|
||||
auto resval = device.createSwapchainKHRUnique(swapchain_create_info);
|
||||
if (resval.result != vk::Result::eSuccess && resval.result != vk::Result::eSuboptimalKHR)
|
||||
{
|
||||
swapchain_object.reset();
|
||||
}
|
||||
|
||||
if (!swapchain_object)
|
||||
return false;
|
||||
}
|
||||
swapchain_object = std::move(resval.value);
|
||||
|
||||
create_resources();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Swapchain::create_resources()
|
||||
{
|
||||
auto swapchain_images = device.getSwapchainImagesKHR(swapchain_object.get()).value;
|
||||
vk::CommandBufferAllocateInfo command_buffer_allocate_info(command_pool, vk::CommandBufferLevel::ePrimary, swapchain_images.size());
|
||||
if (swapchain_images.size() > num_swapchain_images)
|
||||
num_swapchain_images = swapchain_images.size();
|
||||
|
||||
vk::CommandBufferAllocateInfo command_buffer_allocate_info(command_pool, vk::CommandBufferLevel::ePrimary, num_swapchain_images);
|
||||
auto command_buffers = device.allocateCommandBuffersUnique(command_buffer_allocate_info).value;
|
||||
|
||||
if (imageviewfbs.size() > num_swapchain_images)
|
||||
num_swapchain_images = imageviewfbs.size();
|
||||
|
||||
frames.resize(num_swapchain_images);
|
||||
imageviewfbs.resize(num_swapchain_images);
|
||||
image_data.resize(num_swapchain_images);
|
||||
|
||||
vk::FenceCreateInfo fence_create_info(vk::FenceCreateFlagBits::eSignaled);
|
||||
|
||||
@ -218,12 +246,11 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width,
|
||||
frame.acquire = device.createSemaphoreUnique({}).value;
|
||||
frame.complete = device.createSemaphoreUnique({}).value;
|
||||
}
|
||||
current_frame = 0;
|
||||
|
||||
for (unsigned int i = 0; i < num_swapchain_images; i++)
|
||||
{
|
||||
// Create resources associated with swapchain images
|
||||
auto &image = imageviewfbs[i];
|
||||
auto &image = image_data[i];
|
||||
image.image = swapchain_images[i];
|
||||
auto image_view_create_info = vk::ImageViewCreateInfo{}
|
||||
.setImage(swapchain_images[i])
|
||||
@ -240,11 +267,12 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width,
|
||||
.setLayers(1)
|
||||
.setRenderPass(render_pass.get());
|
||||
image.framebuffer = device.createFramebufferUnique(framebuffer_create_info).value;
|
||||
|
||||
image.fence = device.createFenceUnique(fence_create_info).value;
|
||||
}
|
||||
|
||||
device.waitIdle();
|
||||
|
||||
current_swapchain_image = 0;
|
||||
current_frame = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -259,7 +287,7 @@ bool Swapchain::begin_frame()
|
||||
|
||||
auto &frame = frames[current_frame];
|
||||
|
||||
auto result = device.waitForFences(frame.fence.get(), true, 33333333);
|
||||
auto result = device.waitForFences({ frame.fence.get() }, true, 33333333);
|
||||
if (result != vk::Result::eSuccess)
|
||||
{
|
||||
printf("Timed out waiting for fence.\n");
|
||||
@ -268,7 +296,7 @@ bool Swapchain::begin_frame()
|
||||
|
||||
vk::ResultValue<uint32_t> result_value(vk::Result::eSuccess, 0);
|
||||
result_value = device.acquireNextImageKHR(swapchain_object.get(), UINT64_MAX, frame.acquire.get());
|
||||
|
||||
|
||||
if (result_value.result == vk::Result::eErrorOutOfDateKHR ||
|
||||
result_value.result == vk::Result::eSuboptimalKHR)
|
||||
{
|
||||
@ -320,13 +348,22 @@ bool Swapchain::swap()
|
||||
.setSwapchains(swapchain_object.get())
|
||||
.setImageIndices(current_swapchain_image);
|
||||
|
||||
vk::Result result = vk::Result::eSuccess;
|
||||
result = queue.presentKHR(present_info);
|
||||
vk::SwapchainPresentModeInfoEXT present_mode_info;
|
||||
auto present_mode = get_present_mode();
|
||||
present_mode_info.setPresentModes(present_mode);
|
||||
present_info.setPNext(&present_mode_info);
|
||||
|
||||
auto &present_fence = image_data[current_swapchain_image].fence.get();
|
||||
device.resetFences(present_fence);
|
||||
vk::SwapchainPresentFenceInfoEXT present_fence_info(present_fence);
|
||||
present_mode_info.setPNext(&present_fence_info);
|
||||
|
||||
vk::Result result = queue.presentKHR(present_info);
|
||||
if (result == vk::Result::eErrorOutOfDateKHR)
|
||||
{
|
||||
// NVIDIA binary drivers will set OutOfDate between acquire and
|
||||
// present. Ignore this and fix it on the next swapchain acquire.
|
||||
}
|
||||
}
|
||||
|
||||
current_frame = (current_frame + 1) % num_swapchain_images;
|
||||
|
||||
@ -343,7 +380,7 @@ bool Swapchain::end_frame()
|
||||
|
||||
vk::Framebuffer Swapchain::get_framebuffer()
|
||||
{
|
||||
return imageviewfbs[current_swapchain_image].framebuffer.get();
|
||||
return image_data[current_swapchain_image].framebuffer.get();
|
||||
}
|
||||
|
||||
vk::CommandBuffer &Swapchain::get_cmd()
|
||||
@ -360,7 +397,7 @@ void Swapchain::begin_render_pass()
|
||||
|
||||
auto render_pass_begin_info = vk::RenderPassBeginInfo{}
|
||||
.setRenderPass(render_pass.get())
|
||||
.setFramebuffer(imageviewfbs[current_swapchain_image].framebuffer.get())
|
||||
.setFramebuffer(image_data[current_swapchain_image].framebuffer.get())
|
||||
.setRenderArea(vk::Rect2D({}, extents))
|
||||
.setClearValues(value);
|
||||
get_cmd().beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline);
|
||||
@ -379,10 +416,16 @@ void Swapchain::end_render_pass()
|
||||
|
||||
bool Swapchain::wait_on_frame(int frame_num)
|
||||
{
|
||||
auto result = device.waitForFences(frames[frame_num].fence.get(), true, 33000000);
|
||||
auto result = device.waitForFences({ image_data[frame_num].fence.get() }, true, 33000000);
|
||||
return (result == vk::Result::eSuccess);
|
||||
}
|
||||
|
||||
void Swapchain::wait_on_frames()
|
||||
{
|
||||
for (auto i = 0; i < image_data.size(); i++)
|
||||
wait_on_frame(i);
|
||||
}
|
||||
|
||||
vk::Extent2D Swapchain::get_extents()
|
||||
{
|
||||
return extents;
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "vulkan/vulkan_hpp_wrapper.hpp"
|
||||
#include "vulkan_hpp_wrapper.hpp"
|
||||
#include <functional>
|
||||
|
||||
namespace Vulkan
|
||||
@ -17,6 +17,7 @@ class Swapchain
|
||||
~Swapchain();
|
||||
bool create(unsigned int num_frames, int width = -1, int height = -1);
|
||||
bool recreate(int width = -1, int height = -1);
|
||||
bool create_resources();
|
||||
bool check_and_resize(int width = -1, int height = -1);
|
||||
bool begin_frame();
|
||||
void begin_render_pass();
|
||||
@ -25,10 +26,11 @@ class Swapchain
|
||||
bool end_frame();
|
||||
void end_frame_without_swap();
|
||||
bool swap();
|
||||
// Returns true if vsync setting was changed, false if it was the same
|
||||
bool set_vsync(bool on);
|
||||
void wait_on_frames();
|
||||
void set_vsync(bool on);
|
||||
void on_render_pass_end(std::function<void()> function);
|
||||
int get_num_frames() { return num_swapchain_images; }
|
||||
vk::PresentModeKHR get_present_mode();
|
||||
|
||||
vk::Image get_image();
|
||||
vk::Framebuffer get_framebuffer();
|
||||
@ -48,9 +50,10 @@ class Swapchain
|
||||
vk::UniqueCommandBuffer command_buffer;
|
||||
};
|
||||
|
||||
struct ImageViewFB
|
||||
struct ImageData
|
||||
{
|
||||
vk::Image image;
|
||||
vk::UniqueFence fence;
|
||||
vk::UniqueImageView image_view;
|
||||
vk::UniqueFramebuffer framebuffer;
|
||||
};
|
||||
@ -64,8 +67,11 @@ class Swapchain
|
||||
unsigned int current_swapchain_image = 0;
|
||||
unsigned int num_swapchain_images = 0;
|
||||
bool vsync = true;
|
||||
bool supports_immediate = false;
|
||||
bool supports_mailbox = false;
|
||||
bool supports_relaxed = false;
|
||||
std::vector<Frame> frames;
|
||||
std::vector<ImageViewFB> imageviewfbs;
|
||||
std::vector<ImageData> image_data;
|
||||
|
||||
vk::Device device;
|
||||
vk::SurfaceKHR surface;
|
@ -1,3 +1,33 @@
|
||||
Snes9x 1.63
|
||||
General:
|
||||
- Added a shortcut to change the backdrop color for sprite extraction.
|
||||
- Fixed QuickSave 0-9 slot shortcuts not working.
|
||||
- Allow "Address:byte" form for cheat inputs.
|
||||
- Fixed ZIP files not being closed after patch search.
|
||||
- Various memmap fixes to allow unofficial mappings.
|
||||
- Added usage of ImGui to draw things on top of the screen instead of inside.
|
||||
|
||||
Win32:
|
||||
- Fixed AVI not recording audio.
|
||||
- Fixed framerate throttling in turbo mode (now works during AVI recording).
|
||||
- Fixed interlaced output speed being double.
|
||||
- Fixed command line arguments not working.
|
||||
- Fixed WaveOut device name display for names longer than 31 characters.
|
||||
- Fixed Bank+/- hotkey saving.
|
||||
- Added hotkeys for aspect ratio, cheat edit/search.
|
||||
- Added multiselect for cheat edit dialog.
|
||||
|
||||
Gtk:
|
||||
- Fixed config file location to never put files directly in $HOME and obey
|
||||
$XDG_CONFIG_HOME.
|
||||
- Updated translations from JakeSmarter and StanleyKid-22.
|
||||
|
||||
Mac:
|
||||
- Added a new cheat finder.
|
||||
- Added MultiCart support back.
|
||||
- Create a blank window when starting the program, so the global menu change
|
||||
doesn't go unnoticed.
|
||||
|
||||
Snes9x 1.62
|
||||
- Fixed SA1 division with negative dividend again. (Atari2)
|
||||
- Fixed timing on several instructions. (pi1541)
|
||||
|
59
external/imgui/snes9x_imgui.cpp
vendored
@ -12,6 +12,7 @@
|
||||
#include "movie.h"
|
||||
#include "gfx.h"
|
||||
#include "ppu.h"
|
||||
#include "cheats.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -115,6 +116,42 @@ static void ImGui_DrawPressedKeys(int spacing)
|
||||
}
|
||||
}
|
||||
|
||||
static void ImGui_GetWatchesText(std::string& osd_text)
|
||||
{
|
||||
for (unsigned int i = 0; i < sizeof(watches) / sizeof(watches[0]); i++)
|
||||
{
|
||||
if (!watches[i].on)
|
||||
break;
|
||||
|
||||
int32 displayNumber = 0;
|
||||
char buf[64];
|
||||
|
||||
for (int r = 0; r < watches[i].size; r++)
|
||||
displayNumber += (Cheat.CWatchRAM[(watches[i].address - 0x7E0000) + r]) << (8 * r);
|
||||
|
||||
if (watches[i].format == 1)
|
||||
sprintf(buf, "%s,%du = %u", watches[i].desc, watches[i].size, (unsigned int)displayNumber);
|
||||
else
|
||||
if (watches[i].format == 3)
|
||||
sprintf(buf, "%s,%dx = %X", watches[i].desc, watches[i].size, (unsigned int)displayNumber);
|
||||
else // signed
|
||||
{
|
||||
if (watches[i].size == 1)
|
||||
displayNumber = (int32)((int8)displayNumber);
|
||||
else if (watches[i].size == 2)
|
||||
displayNumber = (int32)((int16)displayNumber);
|
||||
else if (watches[i].size == 3)
|
||||
if (displayNumber >= 8388608)
|
||||
displayNumber -= 16777216;
|
||||
|
||||
sprintf(buf, "%s,%ds = %d", watches[i].desc, watches[i].size, (int)displayNumber);
|
||||
}
|
||||
|
||||
osd_text += buf;
|
||||
osd_text += '\n';
|
||||
}
|
||||
}
|
||||
|
||||
static void ImGui_DrawTextOverlay(const char *text,
|
||||
int x, int y,
|
||||
int padding,
|
||||
@ -246,9 +283,29 @@ bool S9xImGuiDraw(int width, int height)
|
||||
}
|
||||
}
|
||||
|
||||
std::string utf8_message;
|
||||
if (Settings.DisplayWatchedAddresses)
|
||||
{
|
||||
ImGui_GetWatchesText(utf8_message);
|
||||
}
|
||||
|
||||
if (!GFX.InfoString.empty())
|
||||
{
|
||||
auto utf8_message = sjis_to_utf8(GFX.InfoString);
|
||||
utf8_message += sjis_to_utf8(GFX.InfoString);
|
||||
}
|
||||
|
||||
if (Settings.DisplayMovieFrame && S9xMovieActive())
|
||||
{
|
||||
// move movie frame count into its own line if info message is active and not already a newline at end
|
||||
if (!utf8_message.empty() && utf8_message.back() != '\n')
|
||||
{
|
||||
utf8_message += '\n';
|
||||
}
|
||||
utf8_message += GFX.FrameDisplayString;
|
||||
}
|
||||
|
||||
if (!utf8_message.empty())
|
||||
{
|
||||
ImGui_DrawTextOverlay(utf8_message.c_str(),
|
||||
settings.spacing,
|
||||
height - settings.spacing,
|
||||
|
487
external/stb/stb_image.h
vendored
@ -1,4 +1,4 @@
|
||||
/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb
|
||||
/* stb_image - v2.30 - public domain image loader - http://nothings.org/stb
|
||||
no warranty implied; use at your own risk
|
||||
|
||||
Do this:
|
||||
@ -48,6 +48,9 @@ LICENSE
|
||||
|
||||
RECENT REVISION HISTORY:
|
||||
|
||||
2.30 (2024-05-31) avoid erroneous gcc warning
|
||||
2.29 (2023-05-xx) optimizations
|
||||
2.28 (2023-01-29) many error fixes, security errors, just tons of stuff
|
||||
2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
|
||||
2.26 (2020-07-13) many minor fixes
|
||||
2.25 (2020-02-02) fix warnings
|
||||
@ -108,7 +111,7 @@ RECENT REVISION HISTORY:
|
||||
Cass Everitt Ryamond Barbiero github:grim210
|
||||
Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
|
||||
Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus
|
||||
Josh Tobin Matthew Gregan github:poppolopoppo
|
||||
Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo
|
||||
Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
|
||||
Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
|
||||
Brad Weinberger Matvey Cherevko github:mosra
|
||||
@ -140,7 +143,7 @@ RECENT REVISION HISTORY:
|
||||
// // ... x = width, y = height, n = # 8-bit components per pixel ...
|
||||
// // ... replace '0' with '1'..'4' to force that many components per pixel
|
||||
// // ... but 'n' will always be the number that it would have been if you said 0
|
||||
// stbi_image_free(data)
|
||||
// stbi_image_free(data);
|
||||
//
|
||||
// Standard parameters:
|
||||
// int *x -- outputs image width in pixels
|
||||
@ -635,7 +638,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#if defined(_MSC_VER) || defined(__SYMBIAN32__)
|
||||
typedef unsigned short stbi__uint16;
|
||||
typedef signed short stbi__int16;
|
||||
typedef unsigned int stbi__uint32;
|
||||
@ -1063,6 +1066,23 @@ static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
|
||||
}
|
||||
#endif
|
||||
|
||||
// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow.
|
||||
static int stbi__addints_valid(int a, int b)
|
||||
{
|
||||
if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow
|
||||
if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0.
|
||||
return a <= INT_MAX - b;
|
||||
}
|
||||
|
||||
// returns 1 if the product of two ints fits in a signed short, 0 on overflow.
|
||||
static int stbi__mul2shorts_valid(int a, int b)
|
||||
{
|
||||
if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow
|
||||
if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid
|
||||
if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN
|
||||
return a >= SHRT_MIN / b;
|
||||
}
|
||||
|
||||
// stbi__err - error
|
||||
// stbi__errpf - error returning pointer to float
|
||||
// stbi__errpuc - error returning pointer to unsigned char
|
||||
@ -1985,9 +2005,12 @@ static int stbi__build_huffman(stbi__huffman *h, int *count)
|
||||
int i,j,k=0;
|
||||
unsigned int code;
|
||||
// build size list for each symbol (from JPEG spec)
|
||||
for (i=0; i < 16; ++i)
|
||||
for (j=0; j < count[i]; ++j)
|
||||
for (i=0; i < 16; ++i) {
|
||||
for (j=0; j < count[i]; ++j) {
|
||||
h->size[k++] = (stbi_uc) (i+1);
|
||||
if(k >= 257) return stbi__err("bad size list","Corrupt JPEG");
|
||||
}
|
||||
}
|
||||
h->size[k] = 0;
|
||||
|
||||
// compute actual symbols (from jpeg spec)
|
||||
@ -2112,6 +2135,8 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h)
|
||||
|
||||
// convert the huffman code to the symbol id
|
||||
c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
|
||||
if(c < 0 || c >= 256) // symbol id out of bounds!
|
||||
return -1;
|
||||
STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);
|
||||
|
||||
// convert the id to a symbol
|
||||
@ -2130,6 +2155,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)
|
||||
unsigned int k;
|
||||
int sgn;
|
||||
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
|
||||
if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
|
||||
|
||||
sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative)
|
||||
k = stbi_lrot(j->code_buffer, n);
|
||||
@ -2144,6 +2170,7 @@ stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n)
|
||||
{
|
||||
unsigned int k;
|
||||
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
|
||||
if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
|
||||
k = stbi_lrot(j->code_buffer, n);
|
||||
j->code_buffer = k & ~stbi__bmask[n];
|
||||
k &= stbi__bmask[n];
|
||||
@ -2155,6 +2182,7 @@ stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j)
|
||||
{
|
||||
unsigned int k;
|
||||
if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);
|
||||
if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing
|
||||
k = j->code_buffer;
|
||||
j->code_buffer <<= 1;
|
||||
--j->code_bits;
|
||||
@ -2192,8 +2220,10 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
|
||||
memset(data,0,64*sizeof(data[0]));
|
||||
|
||||
diff = t ? stbi__extend_receive(j, t) : 0;
|
||||
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG");
|
||||
dc = j->img_comp[b].dc_pred + diff;
|
||||
j->img_comp[b].dc_pred = dc;
|
||||
if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
|
||||
data[0] = (short) (dc * dequant[0]);
|
||||
|
||||
// decode AC components, see JPEG spec
|
||||
@ -2207,6 +2237,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
|
||||
if (r) { // fast-AC path
|
||||
k += (r >> 4) & 15; // run
|
||||
s = r & 15; // combined length
|
||||
if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
|
||||
j->code_buffer <<= s;
|
||||
j->code_bits -= s;
|
||||
// decode into unzigzag'd location
|
||||
@ -2246,8 +2277,10 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__
|
||||
if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
|
||||
diff = t ? stbi__extend_receive(j, t) : 0;
|
||||
|
||||
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG");
|
||||
dc = j->img_comp[b].dc_pred + diff;
|
||||
j->img_comp[b].dc_pred = dc;
|
||||
if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
|
||||
data[0] = (short) (dc * (1 << j->succ_low));
|
||||
} else {
|
||||
// refinement scan for DC coefficient
|
||||
@ -2282,6 +2315,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
|
||||
if (r) { // fast-AC path
|
||||
k += (r >> 4) & 15; // run
|
||||
s = r & 15; // combined length
|
||||
if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
|
||||
j->code_buffer <<= s;
|
||||
j->code_bits -= s;
|
||||
zig = stbi__jpeg_dezigzag[k++];
|
||||
@ -3102,6 +3136,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m)
|
||||
sizes[i] = stbi__get8(z->s);
|
||||
n += sizes[i];
|
||||
}
|
||||
if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values!
|
||||
L -= 17;
|
||||
if (tc == 0) {
|
||||
if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;
|
||||
@ -3351,6 +3386,28 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
|
||||
{
|
||||
// some JPEGs have junk at end, skip over it but if we find what looks
|
||||
// like a valid marker, resume there
|
||||
while (!stbi__at_eof(j->s)) {
|
||||
stbi_uc x = stbi__get8(j->s);
|
||||
while (x == 0xff) { // might be a marker
|
||||
if (stbi__at_eof(j->s)) return STBI__MARKER_none;
|
||||
x = stbi__get8(j->s);
|
||||
if (x != 0x00 && x != 0xff) {
|
||||
// not a stuffed zero or lead-in to another marker, looks
|
||||
// like an actual marker, return it
|
||||
return x;
|
||||
}
|
||||
// stuffed zero has x=0 now which ends the loop, meaning we go
|
||||
// back to regular scan loop.
|
||||
// repeated 0xff keeps trying to read the next byte of the marker.
|
||||
}
|
||||
}
|
||||
return STBI__MARKER_none;
|
||||
}
|
||||
|
||||
// decode image to YCbCr format
|
||||
static int stbi__decode_jpeg_image(stbi__jpeg *j)
|
||||
{
|
||||
@ -3367,25 +3424,22 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j)
|
||||
if (!stbi__process_scan_header(j)) return 0;
|
||||
if (!stbi__parse_entropy_coded_data(j)) return 0;
|
||||
if (j->marker == STBI__MARKER_none ) {
|
||||
// handle 0s at the end of image data from IP Kamera 9060
|
||||
while (!stbi__at_eof(j->s)) {
|
||||
int x = stbi__get8(j->s);
|
||||
if (x == 255) {
|
||||
j->marker = stbi__get8(j->s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
j->marker = stbi__skip_jpeg_junk_at_end(j);
|
||||
// if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
|
||||
}
|
||||
m = stbi__get_marker(j);
|
||||
if (STBI__RESTART(m))
|
||||
m = stbi__get_marker(j);
|
||||
} else if (stbi__DNL(m)) {
|
||||
int Ld = stbi__get16be(j->s);
|
||||
stbi__uint32 NL = stbi__get16be(j->s);
|
||||
if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG");
|
||||
if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG");
|
||||
m = stbi__get_marker(j);
|
||||
} else {
|
||||
if (!stbi__process_marker(j, m)) return 0;
|
||||
if (!stbi__process_marker(j, m)) return 1;
|
||||
m = stbi__get_marker(j);
|
||||
}
|
||||
m = stbi__get_marker(j);
|
||||
}
|
||||
if (j->progressive)
|
||||
stbi__jpeg_finish(j);
|
||||
@ -3976,6 +4030,7 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re
|
||||
unsigned char* result;
|
||||
stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
|
||||
if (!j) return stbi__errpuc("outofmem", "Out of memory");
|
||||
memset(j, 0, sizeof(stbi__jpeg));
|
||||
STBI_NOTUSED(ri);
|
||||
j->s = s;
|
||||
stbi__setup_jpeg(j);
|
||||
@ -3989,6 +4044,7 @@ static int stbi__jpeg_test(stbi__context *s)
|
||||
int r;
|
||||
stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
|
||||
if (!j) return stbi__err("outofmem", "Out of memory");
|
||||
memset(j, 0, sizeof(stbi__jpeg));
|
||||
j->s = s;
|
||||
stbi__setup_jpeg(j);
|
||||
r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
|
||||
@ -4014,6 +4070,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
|
||||
int result;
|
||||
stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
|
||||
if (!j) return stbi__err("outofmem", "Out of memory");
|
||||
memset(j, 0, sizeof(stbi__jpeg));
|
||||
j->s = s;
|
||||
result = stbi__jpeg_info_raw(j, x, y, comp);
|
||||
STBI_FREE(j);
|
||||
@ -4121,6 +4178,7 @@ typedef struct
|
||||
{
|
||||
stbi_uc *zbuffer, *zbuffer_end;
|
||||
int num_bits;
|
||||
int hit_zeof_once;
|
||||
stbi__uint32 code_buffer;
|
||||
|
||||
char *zout;
|
||||
@ -4187,9 +4245,20 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
|
||||
int b,s;
|
||||
if (a->num_bits < 16) {
|
||||
if (stbi__zeof(a)) {
|
||||
return -1; /* report error for unexpected end of data. */
|
||||
if (!a->hit_zeof_once) {
|
||||
// This is the first time we hit eof, insert 16 extra padding btis
|
||||
// to allow us to keep going; if we actually consume any of them
|
||||
// though, that is invalid data. This is caught later.
|
||||
a->hit_zeof_once = 1;
|
||||
a->num_bits += 16; // add 16 implicit zero bits
|
||||
} else {
|
||||
// We already inserted our extra 16 padding bits and are again
|
||||
// out, this stream is actually prematurely terminated.
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
stbi__fill_bits(a);
|
||||
}
|
||||
stbi__fill_bits(a);
|
||||
}
|
||||
b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
|
||||
if (b) {
|
||||
@ -4254,17 +4323,25 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
|
||||
int len,dist;
|
||||
if (z == 256) {
|
||||
a->zout = zout;
|
||||
if (a->hit_zeof_once && a->num_bits < 16) {
|
||||
// The first time we hit zeof, we inserted 16 extra zero bits into our bit
|
||||
// buffer so the decoder can just do its speculative decoding. But if we
|
||||
// actually consumed any of those bits (which is the case when num_bits < 16),
|
||||
// the stream actually read past the end so it is malformed.
|
||||
return stbi__err("unexpected end","Corrupt PNG");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data
|
||||
z -= 257;
|
||||
len = stbi__zlength_base[z];
|
||||
if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
|
||||
z = stbi__zhuffman_decode(a, &a->z_distance);
|
||||
if (z < 0) return stbi__err("bad huffman code","Corrupt PNG");
|
||||
if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data
|
||||
dist = stbi__zdist_base[z];
|
||||
if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
|
||||
if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
|
||||
if (zout + len > a->zout_end) {
|
||||
if (len > a->zout_end - zout) {
|
||||
if (!stbi__zexpand(a, zout, len)) return 0;
|
||||
zout = a->zout;
|
||||
}
|
||||
@ -4408,6 +4485,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
|
||||
if (!stbi__parse_zlib_header(a)) return 0;
|
||||
a->num_bits = 0;
|
||||
a->code_buffer = 0;
|
||||
a->hit_zeof_once = 0;
|
||||
do {
|
||||
final = stbi__zreceive(a,1);
|
||||
type = stbi__zreceive(a,2);
|
||||
@ -4563,9 +4641,8 @@ enum {
|
||||
STBI__F_up=2,
|
||||
STBI__F_avg=3,
|
||||
STBI__F_paeth=4,
|
||||
// synthetic filters used for first scanline to avoid needing a dummy row of 0s
|
||||
STBI__F_avg_first,
|
||||
STBI__F_paeth_first
|
||||
// synthetic filter used for first scanline to avoid needing a dummy row of 0s
|
||||
STBI__F_avg_first
|
||||
};
|
||||
|
||||
static stbi_uc first_row_filter[5] =
|
||||
@ -4574,29 +4651,56 @@ static stbi_uc first_row_filter[5] =
|
||||
STBI__F_sub,
|
||||
STBI__F_none,
|
||||
STBI__F_avg_first,
|
||||
STBI__F_paeth_first
|
||||
STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub
|
||||
};
|
||||
|
||||
static int stbi__paeth(int a, int b, int c)
|
||||
{
|
||||
int p = a + b - c;
|
||||
int pa = abs(p-a);
|
||||
int pb = abs(p-b);
|
||||
int pc = abs(p-c);
|
||||
if (pa <= pb && pa <= pc) return a;
|
||||
if (pb <= pc) return b;
|
||||
return c;
|
||||
// This formulation looks very different from the reference in the PNG spec, but is
|
||||
// actually equivalent and has favorable data dependencies and admits straightforward
|
||||
// generation of branch-free code, which helps performance significantly.
|
||||
int thresh = c*3 - (a + b);
|
||||
int lo = a < b ? a : b;
|
||||
int hi = a < b ? b : a;
|
||||
int t0 = (hi <= thresh) ? lo : c;
|
||||
int t1 = (thresh <= lo) ? hi : t0;
|
||||
return t1;
|
||||
}
|
||||
|
||||
static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
|
||||
|
||||
// adds an extra all-255 alpha channel
|
||||
// dest == src is legal
|
||||
// img_n must be 1 or 3
|
||||
static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n)
|
||||
{
|
||||
int i;
|
||||
// must process data backwards since we allow dest==src
|
||||
if (img_n == 1) {
|
||||
for (i=x-1; i >= 0; --i) {
|
||||
dest[i*2+1] = 255;
|
||||
dest[i*2+0] = src[i];
|
||||
}
|
||||
} else {
|
||||
STBI_ASSERT(img_n == 3);
|
||||
for (i=x-1; i >= 0; --i) {
|
||||
dest[i*4+3] = 255;
|
||||
dest[i*4+2] = src[i*3+2];
|
||||
dest[i*4+1] = src[i*3+1];
|
||||
dest[i*4+0] = src[i*3+0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create the png data from post-deflated data
|
||||
static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
|
||||
{
|
||||
int bytes = (depth == 16? 2 : 1);
|
||||
int bytes = (depth == 16 ? 2 : 1);
|
||||
stbi__context *s = a->s;
|
||||
stbi__uint32 i,j,stride = x*out_n*bytes;
|
||||
stbi__uint32 img_len, img_width_bytes;
|
||||
stbi_uc *filter_buf;
|
||||
int all_ok = 1;
|
||||
int k;
|
||||
int img_n = s->img_n; // copy it into a local for later
|
||||
|
||||
@ -4608,8 +4712,11 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
|
||||
a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into
|
||||
if (!a->out) return stbi__err("outofmem", "Out of memory");
|
||||
|
||||
// note: error exits here don't need to clean up a->out individually,
|
||||
// stbi__do_png always does on error.
|
||||
if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG");
|
||||
img_width_bytes = (((img_n * x * depth) + 7) >> 3);
|
||||
if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG");
|
||||
img_len = (img_width_bytes + 1) * y;
|
||||
|
||||
// we used to check for exact match between raw_len and img_len on non-interlaced PNGs,
|
||||
@ -4617,189 +4724,137 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
|
||||
// so just check for raw_len < img_len always.
|
||||
if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
|
||||
|
||||
// Allocate two scan lines worth of filter workspace buffer.
|
||||
filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0);
|
||||
if (!filter_buf) return stbi__err("outofmem", "Out of memory");
|
||||
|
||||
// Filtering for low-bit-depth images
|
||||
if (depth < 8) {
|
||||
filter_bytes = 1;
|
||||
width = img_width_bytes;
|
||||
}
|
||||
|
||||
for (j=0; j < y; ++j) {
|
||||
stbi_uc *cur = a->out + stride*j;
|
||||
stbi_uc *prior;
|
||||
// cur/prior filter buffers alternate
|
||||
stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes;
|
||||
stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes;
|
||||
stbi_uc *dest = a->out + stride*j;
|
||||
int nk = width * filter_bytes;
|
||||
int filter = *raw++;
|
||||
|
||||
if (filter > 4)
|
||||
return stbi__err("invalid filter","Corrupt PNG");
|
||||
|
||||
if (depth < 8) {
|
||||
if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG");
|
||||
cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place
|
||||
filter_bytes = 1;
|
||||
width = img_width_bytes;
|
||||
// check filter type
|
||||
if (filter > 4) {
|
||||
all_ok = stbi__err("invalid filter","Corrupt PNG");
|
||||
break;
|
||||
}
|
||||
prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above
|
||||
|
||||
// if first row, use special filter that doesn't sample previous row
|
||||
if (j == 0) filter = first_row_filter[filter];
|
||||
|
||||
// handle first byte explicitly
|
||||
for (k=0; k < filter_bytes; ++k) {
|
||||
switch (filter) {
|
||||
case STBI__F_none : cur[k] = raw[k]; break;
|
||||
case STBI__F_sub : cur[k] = raw[k]; break;
|
||||
case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
|
||||
case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break;
|
||||
case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break;
|
||||
case STBI__F_avg_first : cur[k] = raw[k]; break;
|
||||
case STBI__F_paeth_first: cur[k] = raw[k]; break;
|
||||
}
|
||||
// perform actual filtering
|
||||
switch (filter) {
|
||||
case STBI__F_none:
|
||||
memcpy(cur, raw, nk);
|
||||
break;
|
||||
case STBI__F_sub:
|
||||
memcpy(cur, raw, filter_bytes);
|
||||
for (k = filter_bytes; k < nk; ++k)
|
||||
cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]);
|
||||
break;
|
||||
case STBI__F_up:
|
||||
for (k = 0; k < nk; ++k)
|
||||
cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
|
||||
break;
|
||||
case STBI__F_avg:
|
||||
for (k = 0; k < filter_bytes; ++k)
|
||||
cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1));
|
||||
for (k = filter_bytes; k < nk; ++k)
|
||||
cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1));
|
||||
break;
|
||||
case STBI__F_paeth:
|
||||
for (k = 0; k < filter_bytes; ++k)
|
||||
cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0)
|
||||
for (k = filter_bytes; k < nk; ++k)
|
||||
cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes]));
|
||||
break;
|
||||
case STBI__F_avg_first:
|
||||
memcpy(cur, raw, filter_bytes);
|
||||
for (k = filter_bytes; k < nk; ++k)
|
||||
cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1));
|
||||
break;
|
||||
}
|
||||
|
||||
if (depth == 8) {
|
||||
if (img_n != out_n)
|
||||
cur[img_n] = 255; // first pixel
|
||||
raw += img_n;
|
||||
cur += out_n;
|
||||
prior += out_n;
|
||||
} else if (depth == 16) {
|
||||
if (img_n != out_n) {
|
||||
cur[filter_bytes] = 255; // first pixel top byte
|
||||
cur[filter_bytes+1] = 255; // first pixel bottom byte
|
||||
}
|
||||
raw += filter_bytes;
|
||||
cur += output_bytes;
|
||||
prior += output_bytes;
|
||||
} else {
|
||||
raw += 1;
|
||||
cur += 1;
|
||||
prior += 1;
|
||||
}
|
||||
raw += nk;
|
||||
|
||||
// this is a little gross, so that we don't switch per-pixel or per-component
|
||||
if (depth < 8 || img_n == out_n) {
|
||||
int nk = (width - 1)*filter_bytes;
|
||||
#define STBI__CASE(f) \
|
||||
case f: \
|
||||
for (k=0; k < nk; ++k)
|
||||
switch (filter) {
|
||||
// "none" filter turns into a memcpy here; make that explicit.
|
||||
case STBI__F_none: memcpy(cur, raw, nk); break;
|
||||
STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break;
|
||||
STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
|
||||
STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break;
|
||||
STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break;
|
||||
STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break;
|
||||
STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break;
|
||||
}
|
||||
#undef STBI__CASE
|
||||
raw += nk;
|
||||
} else {
|
||||
STBI_ASSERT(img_n+1 == out_n);
|
||||
#define STBI__CASE(f) \
|
||||
case f: \
|
||||
for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \
|
||||
for (k=0; k < filter_bytes; ++k)
|
||||
switch (filter) {
|
||||
STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break;
|
||||
STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break;
|
||||
STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
|
||||
STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break;
|
||||
STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break;
|
||||
STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break;
|
||||
STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break;
|
||||
}
|
||||
#undef STBI__CASE
|
||||
|
||||
// the loop above sets the high byte of the pixels' alpha, but for
|
||||
// 16 bit png files we also need the low byte set. we'll do that here.
|
||||
if (depth == 16) {
|
||||
cur = a->out + stride*j; // start at the beginning of the row again
|
||||
for (i=0; i < x; ++i,cur+=output_bytes) {
|
||||
cur[filter_bytes+1] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we make a separate pass to expand bits to pixels; for performance,
|
||||
// this could run two scanlines behind the above code, so it won't
|
||||
// intefere with filtering but will still be in the cache.
|
||||
if (depth < 8) {
|
||||
for (j=0; j < y; ++j) {
|
||||
stbi_uc *cur = a->out + stride*j;
|
||||
stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes;
|
||||
// unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit
|
||||
// png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop
|
||||
// expand decoded bits in cur to dest, also adding an extra alpha channel if desired
|
||||
if (depth < 8) {
|
||||
stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
|
||||
stbi_uc *in = cur;
|
||||
stbi_uc *out = dest;
|
||||
stbi_uc inb = 0;
|
||||
stbi__uint32 nsmp = x*img_n;
|
||||
|
||||
// note that the final byte might overshoot and write more data than desired.
|
||||
// we can allocate enough data that this never writes out of memory, but it
|
||||
// could also overwrite the next scanline. can it overwrite non-empty data
|
||||
// on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.
|
||||
// so we need to explicitly clamp the final ones
|
||||
|
||||
// expand bits to bytes first
|
||||
if (depth == 4) {
|
||||
for (k=x*img_n; k >= 2; k-=2, ++in) {
|
||||
*cur++ = scale * ((*in >> 4) );
|
||||
*cur++ = scale * ((*in ) & 0x0f);
|
||||
for (i=0; i < nsmp; ++i) {
|
||||
if ((i & 1) == 0) inb = *in++;
|
||||
*out++ = scale * (inb >> 4);
|
||||
inb <<= 4;
|
||||
}
|
||||
if (k > 0) *cur++ = scale * ((*in >> 4) );
|
||||
} else if (depth == 2) {
|
||||
for (k=x*img_n; k >= 4; k-=4, ++in) {
|
||||
*cur++ = scale * ((*in >> 6) );
|
||||
*cur++ = scale * ((*in >> 4) & 0x03);
|
||||
*cur++ = scale * ((*in >> 2) & 0x03);
|
||||
*cur++ = scale * ((*in ) & 0x03);
|
||||
for (i=0; i < nsmp; ++i) {
|
||||
if ((i & 3) == 0) inb = *in++;
|
||||
*out++ = scale * (inb >> 6);
|
||||
inb <<= 2;
|
||||
}
|
||||
if (k > 0) *cur++ = scale * ((*in >> 6) );
|
||||
if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);
|
||||
if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);
|
||||
} else if (depth == 1) {
|
||||
for (k=x*img_n; k >= 8; k-=8, ++in) {
|
||||
*cur++ = scale * ((*in >> 7) );
|
||||
*cur++ = scale * ((*in >> 6) & 0x01);
|
||||
*cur++ = scale * ((*in >> 5) & 0x01);
|
||||
*cur++ = scale * ((*in >> 4) & 0x01);
|
||||
*cur++ = scale * ((*in >> 3) & 0x01);
|
||||
*cur++ = scale * ((*in >> 2) & 0x01);
|
||||
*cur++ = scale * ((*in >> 1) & 0x01);
|
||||
*cur++ = scale * ((*in ) & 0x01);
|
||||
} else {
|
||||
STBI_ASSERT(depth == 1);
|
||||
for (i=0; i < nsmp; ++i) {
|
||||
if ((i & 7) == 0) inb = *in++;
|
||||
*out++ = scale * (inb >> 7);
|
||||
inb <<= 1;
|
||||
}
|
||||
if (k > 0) *cur++ = scale * ((*in >> 7) );
|
||||
if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);
|
||||
if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);
|
||||
if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);
|
||||
if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);
|
||||
if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);
|
||||
if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
|
||||
}
|
||||
if (img_n != out_n) {
|
||||
int q;
|
||||
// insert alpha = 255
|
||||
cur = a->out + stride*j;
|
||||
|
||||
// insert alpha=255 values if desired
|
||||
if (img_n != out_n)
|
||||
stbi__create_png_alpha_expand8(dest, dest, x, img_n);
|
||||
} else if (depth == 8) {
|
||||
if (img_n == out_n)
|
||||
memcpy(dest, cur, x*img_n);
|
||||
else
|
||||
stbi__create_png_alpha_expand8(dest, cur, x, img_n);
|
||||
} else if (depth == 16) {
|
||||
// convert the image data from big-endian to platform-native
|
||||
stbi__uint16 *dest16 = (stbi__uint16*)dest;
|
||||
stbi__uint32 nsmp = x*img_n;
|
||||
|
||||
if (img_n == out_n) {
|
||||
for (i = 0; i < nsmp; ++i, ++dest16, cur += 2)
|
||||
*dest16 = (cur[0] << 8) | cur[1];
|
||||
} else {
|
||||
STBI_ASSERT(img_n+1 == out_n);
|
||||
if (img_n == 1) {
|
||||
for (q=x-1; q >= 0; --q) {
|
||||
cur[q*2+1] = 255;
|
||||
cur[q*2+0] = cur[q];
|
||||
for (i = 0; i < x; ++i, dest16 += 2, cur += 2) {
|
||||
dest16[0] = (cur[0] << 8) | cur[1];
|
||||
dest16[1] = 0xffff;
|
||||
}
|
||||
} else {
|
||||
STBI_ASSERT(img_n == 3);
|
||||
for (q=x-1; q >= 0; --q) {
|
||||
cur[q*4+3] = 255;
|
||||
cur[q*4+2] = cur[q*3+2];
|
||||
cur[q*4+1] = cur[q*3+1];
|
||||
cur[q*4+0] = cur[q*3+0];
|
||||
for (i = 0; i < x; ++i, dest16 += 4, cur += 6) {
|
||||
dest16[0] = (cur[0] << 8) | cur[1];
|
||||
dest16[1] = (cur[2] << 8) | cur[3];
|
||||
dest16[2] = (cur[4] << 8) | cur[5];
|
||||
dest16[3] = 0xffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (depth == 16) {
|
||||
// force the image data from big-endian to platform-native.
|
||||
// this is done in a separate pass due to the decoding relying
|
||||
// on the data being untouched, but could probably be done
|
||||
// per-line during decode if care is taken.
|
||||
stbi_uc *cur = a->out;
|
||||
stbi__uint16 *cur16 = (stbi__uint16*)cur;
|
||||
|
||||
for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {
|
||||
*cur16 = (cur[0] << 8) | cur[1];
|
||||
}
|
||||
}
|
||||
|
||||
STBI_FREE(filter_buf);
|
||||
if (!all_ok) return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -4955,7 +5010,7 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
|
||||
static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;
|
||||
static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;
|
||||
|
||||
STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
|
||||
STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
|
||||
{
|
||||
stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;
|
||||
stbi__unpremultiply_on_load_set = 1;
|
||||
@ -5064,14 +5119,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
|
||||
if (!pal_img_n) {
|
||||
s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
|
||||
if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
|
||||
if (scan == STBI__SCAN_header) return 1;
|
||||
} else {
|
||||
// if paletted, then pal_n is our final components, and
|
||||
// img_n is # components to decompress/filter.
|
||||
s->img_n = 1;
|
||||
if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
|
||||
// if SCAN_header, have to scan to see if we have a tRNS
|
||||
}
|
||||
// even with SCAN_header, have to scan to see if we have a tRNS
|
||||
break;
|
||||
}
|
||||
|
||||
@ -5103,10 +5157,14 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
|
||||
if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
|
||||
if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
|
||||
has_trans = 1;
|
||||
// non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.
|
||||
if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }
|
||||
if (z->depth == 16) {
|
||||
for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
|
||||
for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning
|
||||
tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
|
||||
} else {
|
||||
for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
|
||||
for (k = 0; k < s->img_n && k < 3; ++k)
|
||||
tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -5115,7 +5173,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
|
||||
case STBI__PNG_TYPE('I','D','A','T'): {
|
||||
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
|
||||
if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
|
||||
if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }
|
||||
if (scan == STBI__SCAN_header) {
|
||||
// header scan definitely stops at first IDAT
|
||||
if (pal_img_n)
|
||||
s->img_n = pal_img_n;
|
||||
return 1;
|
||||
}
|
||||
if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes");
|
||||
if ((int)(ioff + c.length) < (int)ioff) return 0;
|
||||
if (ioff + c.length > idata_limit) {
|
||||
stbi__uint32 idata_limit_old = idata_limit;
|
||||
@ -5498,8 +5562,22 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||
psize = (info.offset - info.extra_read - info.hsz) >> 2;
|
||||
}
|
||||
if (psize == 0) {
|
||||
if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) {
|
||||
return stbi__errpuc("bad offset", "Corrupt BMP");
|
||||
// accept some number of extra bytes after the header, but if the offset points either to before
|
||||
// the header ends or implies a large amount of extra data, reject the file as malformed
|
||||
int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original);
|
||||
int header_limit = 1024; // max we actually read is below 256 bytes currently.
|
||||
int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size.
|
||||
if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) {
|
||||
return stbi__errpuc("bad header", "Corrupt BMP");
|
||||
}
|
||||
// we established that bytes_read_so_far is positive and sensible.
|
||||
// the first half of this test rejects offsets that are either too small positives, or
|
||||
// negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn
|
||||
// ensures the number computed in the second half of the test can't overflow.
|
||||
if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) {
|
||||
return stbi__errpuc("bad offset", "Corrupt BMP");
|
||||
} else {
|
||||
stbi__skip(s, info.offset - bytes_read_so_far);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7187,12 +7265,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re
|
||||
// Run
|
||||
value = stbi__get8(s);
|
||||
count -= 128;
|
||||
if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
|
||||
if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
|
||||
for (z = 0; z < count; ++z)
|
||||
scanline[i++ * 4 + k] = value;
|
||||
} else {
|
||||
// Dump
|
||||
if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
|
||||
if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
|
||||
for (z = 0; z < count; ++z)
|
||||
scanline[i++ * 4 + k] = stbi__get8(s);
|
||||
}
|
||||
@ -7446,10 +7524,17 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||
|
||||
out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0);
|
||||
if (!out) return stbi__errpuc("outofmem", "Out of memory");
|
||||
stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8));
|
||||
if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) {
|
||||
STBI_FREE(out);
|
||||
return stbi__errpuc("bad PNM", "PNM file truncated");
|
||||
}
|
||||
|
||||
if (req_comp && req_comp != s->img_n) {
|
||||
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
|
||||
if (ri->bits_per_channel == 16) {
|
||||
out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y);
|
||||
} else {
|
||||
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
|
||||
}
|
||||
if (out == NULL) return out; // stbi__convert_format frees input on failure
|
||||
}
|
||||
return out;
|
||||
@ -7486,6 +7571,8 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c)
|
||||
while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
|
||||
value = value*10 + (*c - '0');
|
||||
*c = (char) stbi__get8(s);
|
||||
if((value > 214748364) || (value == 214748364 && *c > '7'))
|
||||
return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int");
|
||||
}
|
||||
|
||||
return value;
|
||||
@ -7516,9 +7603,13 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)
|
||||
stbi__pnm_skip_whitespace(s, &c);
|
||||
|
||||
*x = stbi__pnm_getinteger(s, &c); // read width
|
||||
if(*x == 0)
|
||||
return stbi__err("invalid width", "PPM image header had zero or overflowing width");
|
||||
stbi__pnm_skip_whitespace(s, &c);
|
||||
|
||||
*y = stbi__pnm_getinteger(s, &c); // read height
|
||||
if (*y == 0)
|
||||
return stbi__err("invalid width", "PPM image header had zero or overflowing width");
|
||||
stbi__pnm_skip_whitespace(s, &c);
|
||||
|
||||
maxv = stbi__pnm_getinteger(s, &c); // read max value
|
||||
|
@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
project(snes9x-gtk VERSION 1.62.3)
|
||||
project(snes9x-gtk VERSION 1.63)
|
||||
|
||||
option(USE_SLANG "Build support for Vulkan output and .slangp shaders" ON)
|
||||
option(USE_XV "Build support for XVideo output" ON)
|
||||
@ -68,23 +68,23 @@ list(APPEND ARGS ${SDL2_CFLAGS} ${GTK_CFLAGS} ${XRANDR_CFLAGS})
|
||||
list(APPEND LIBS ${X11} ${XEXT} ${CMAKE_DL_LIBS} ${SDL2_LIBRARIES} ${GTK_LIBRARIES} ${XRANDR_LIBRARIES})
|
||||
|
||||
list(APPEND SOURCES src/gtk_display_driver_opengl.cpp
|
||||
../common/video/glx_context.cpp
|
||||
../shaders/glsl.cpp
|
||||
../shaders/shader_helpers.cpp
|
||||
../vulkan/slang_helpers.cpp
|
||||
../vulkan/slang_helpers.hpp
|
||||
../vulkan/slang_preset_ini.cpp
|
||||
../vulkan/slang_preset_ini.hpp
|
||||
../common/video/opengl/glx_context.cpp
|
||||
../common/video/opengl/shaders/glsl.cpp
|
||||
../common/video/opengl/shaders/shader_helpers.cpp
|
||||
../common/video/vulkan/slang_helpers.cpp
|
||||
../common/video/vulkan/slang_helpers.hpp
|
||||
../common/video/vulkan/slang_preset_ini.cpp
|
||||
../common/video/vulkan/slang_preset_ini.hpp
|
||||
../external/glad/src/glx.c
|
||||
../external/glad/src/egl.c
|
||||
../external/glad/src/gl.c
|
||||
src/gtk_shader_parameters.cpp
|
||||
../vulkan/std_chrono_throttle.cpp
|
||||
../vulkan/std_chrono_throttle.hpp)
|
||||
../common/video/std_chrono_throttle.cpp
|
||||
../common/video/std_chrono_throttle.hpp)
|
||||
list(APPEND INCLUDES ../external/glad/include)
|
||||
|
||||
if(USE_SLANG)
|
||||
list(APPEND SOURCES ../shaders/slang.cpp)
|
||||
list(APPEND SOURCES ../common/video/opengl/shaders/slang.cpp)
|
||||
list(APPEND INCLUDES ../external/glslang)
|
||||
|
||||
set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS CACHE BOOL ON)
|
||||
@ -116,26 +116,26 @@ if(USE_SLANG)
|
||||
list(APPEND INCLUDES ../external/VulkanMemoryAllocator-Hpp/include)
|
||||
list(APPEND INCLUDES ../external/stb)
|
||||
list(APPEND SOURCES ../external/stb/stb_image_implementation.cpp)
|
||||
list(APPEND SOURCES ../vulkan/slang_shader.cpp
|
||||
../vulkan/slang_shader.hpp
|
||||
../vulkan/slang_preset.cpp
|
||||
../vulkan/slang_preset.hpp
|
||||
../vulkan/vulkan_hpp_storage.cpp
|
||||
../vulkan/vk_mem_alloc_implementation.cpp
|
||||
../vulkan/vulkan_context.cpp
|
||||
../vulkan/vulkan_context.hpp
|
||||
../vulkan/vulkan_texture.cpp
|
||||
../vulkan/vulkan_texture.hpp
|
||||
../vulkan/vulkan_swapchain.cpp
|
||||
../vulkan/vulkan_swapchain.hpp
|
||||
../vulkan/vulkan_slang_pipeline.cpp
|
||||
../vulkan/vulkan_slang_pipeline.hpp
|
||||
../vulkan/vulkan_pipeline_image.cpp
|
||||
../vulkan/vulkan_pipeline_image.hpp
|
||||
../vulkan/vulkan_shader_chain.cpp
|
||||
../vulkan/vulkan_shader_chain.hpp
|
||||
../vulkan/vulkan_simple_output.hpp
|
||||
../vulkan/vulkan_simple_output.cpp
|
||||
list(APPEND SOURCES ../common/video/vulkan/slang_shader.cpp
|
||||
../common/video/vulkan/slang_shader.hpp
|
||||
../common/video/vulkan/slang_preset.cpp
|
||||
../common/video/vulkan/slang_preset.hpp
|
||||
../common/video/vulkan/vulkan_hpp_storage.cpp
|
||||
../common/video/vulkan/vk_mem_alloc_implementation.cpp
|
||||
../common/video/vulkan/vulkan_context.cpp
|
||||
../common/video/vulkan/vulkan_context.hpp
|
||||
../common/video/vulkan/vulkan_texture.cpp
|
||||
../common/video/vulkan/vulkan_texture.hpp
|
||||
../common/video/vulkan/vulkan_swapchain.cpp
|
||||
../common/video/vulkan/vulkan_swapchain.hpp
|
||||
../common/video/vulkan/vulkan_slang_pipeline.cpp
|
||||
../common/video/vulkan/vulkan_slang_pipeline.hpp
|
||||
../common/video/vulkan/vulkan_pipeline_image.cpp
|
||||
../common/video/vulkan/vulkan_pipeline_image.hpp
|
||||
../common/video/vulkan/vulkan_shader_chain.cpp
|
||||
../common/video/vulkan/vulkan_shader_chain.hpp
|
||||
../common/video/vulkan/vulkan_simple_output.hpp
|
||||
../common/video/vulkan/vulkan_simple_output.cpp
|
||||
src/gtk_display_driver_vulkan.cpp
|
||||
src/gtk_display_driver_vulkan.h
|
||||
../external/imgui/imgui_impl_vulkan.cpp)
|
||||
@ -154,13 +154,13 @@ list(APPEND INCLUDES ../external/imgui)
|
||||
if(USE_WAYLAND)
|
||||
pkg_check_modules(WAYLAND REQUIRED wayland-client wayland-egl)
|
||||
list(APPEND DEFINES "USE_WAYLAND")
|
||||
list(APPEND SOURCES ../common/video/wayland_egl_context.cpp
|
||||
../common/video/wayland_egl_context.hpp
|
||||
../common/video/wayland_surface.cpp
|
||||
../common/video/wayland_surface.hpp
|
||||
../common/video/wayland-idle-inhibit-unstable-v1.c
|
||||
../common/video/viewporter-client-protocol.c
|
||||
../common/video/fractional-scale-v1.c)
|
||||
list(APPEND SOURCES ../common/video/opengl/wayland_egl_context.cpp
|
||||
../common/video/opengl/wayland_egl_context.hpp
|
||||
../common/video/wayland/wayland_surface.cpp
|
||||
../common/video/wayland/wayland_surface.hpp
|
||||
../common/video/wayland/wayland-idle-inhibit-unstable-v1.c
|
||||
../common/video/wayland/viewporter-client-protocol.c
|
||||
../common/video/wayland/fractional-scale-v1.c)
|
||||
list(APPEND ARGS ${WAYLAND_CFLAGS})
|
||||
list(APPEND LIBS ${WAYLAND_LIBRARIES})
|
||||
endif()
|
||||
@ -388,16 +388,16 @@ target_compile_definitions(snes9x-gtk PRIVATE ${DEFINES})
|
||||
|
||||
if(USE_SLANG)
|
||||
add_executable(slang_test EXCLUDE_FROM_ALL
|
||||
../vulkan/slang_helpers.cpp
|
||||
../vulkan/slang_helpers.hpp
|
||||
../vulkan/slang_shader.cpp
|
||||
../vulkan/slang_shader.hpp
|
||||
../vulkan/slang_preset.cpp
|
||||
../vulkan/slang_preset.hpp
|
||||
../vulkan/slang_preset_ini.cpp
|
||||
../vulkan/slang_preset_ini.hpp
|
||||
../vulkan/vulkan_hpp_storage.cpp
|
||||
../vulkan/slang_preset_test.cpp
|
||||
../common/video/vulkan/slang_helpers.cpp
|
||||
../common/video/vulkan/slang_helpers.hpp
|
||||
../common/video/vulkan/slang_shader.cpp
|
||||
../common/video/vulkan/slang_shader.hpp
|
||||
../common/video/vulkan/slang_preset.cpp
|
||||
../common/video/vulkan/slang_preset.hpp
|
||||
../common/video/vulkan/slang_preset_ini.cpp
|
||||
../common/video/vulkan/slang_preset_ini.hpp
|
||||
../common/video/vulkan/vulkan_hpp_storage.cpp
|
||||
../common/video/vulkan/slang_preset_test.cpp
|
||||
../conffile.cpp
|
||||
../stream.cpp)
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Snes9x 1.62.3\n"
|
||||
"Project-Id-Version: Snes9x 1.63\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-10-29 23:56+0100\n"
|
||||
"PO-Revision-Date: 2023-10-30 12:00+0100\n"
|
||||
|
@ -2,7 +2,7 @@
|
||||
# Stanley Kid <stanley.udr.kid@gmail.com>, 2024.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 1.62.3\n"
|
||||
"Project-Id-Version: 1.63\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-10-29 23:56+0100\n"
|
||||
"PO-Revision-Date: 2024-02-14 15:43+0200\n"
|
||||
|
@ -33,22 +33,21 @@ std::string get_config_dir()
|
||||
return std::string{".snes9x"};
|
||||
}
|
||||
|
||||
fs::path config = env_home;
|
||||
fs::path legacy = config;
|
||||
|
||||
fs::path config;
|
||||
// If XDG_CONFIG_HOME is set, use that, otherwise guess default
|
||||
if (!env_xdg_config_home)
|
||||
{
|
||||
config /= ".config/snes9x";
|
||||
legacy /= ".snes9x";
|
||||
}
|
||||
else
|
||||
if (env_xdg_config_home)
|
||||
{
|
||||
config = env_xdg_config_home;
|
||||
config /= "snes9x";
|
||||
}
|
||||
if (fs::exists(legacy) && !fs::exists(config))
|
||||
return legacy;
|
||||
else
|
||||
{
|
||||
config = env_home;
|
||||
config /= ".config/snes9x";
|
||||
}
|
||||
|
||||
if (!fs::exists(config))
|
||||
fs::create_directories(config);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
@ -5,8 +5,10 @@
|
||||
\*****************************************************************************/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <filesystem>
|
||||
|
||||
#include "SDL_joystick.h"
|
||||
#include "fscompat.h"
|
||||
#include "gtk_s9x.h"
|
||||
#include "gtk_config.h"
|
||||
#include "gtk_control.h"
|
||||
@ -195,7 +197,15 @@ static void change_slot(int difference)
|
||||
if (!gui_config->rom_loaded)
|
||||
return;
|
||||
|
||||
auto info_string = "State Slot: " + std::to_string(gui_config->current_save_slot);
|
||||
char extension_string[5];
|
||||
snprintf(extension_string, 5, ".%03d", gui_config->current_save_slot);
|
||||
auto filename = S9xGetFilename(extension_string, SNAPSHOT_DIR);
|
||||
struct stat info;
|
||||
std::string exists = "empty";
|
||||
if (stat(filename.c_str(), &info) == 0)
|
||||
exists = "used";
|
||||
|
||||
auto info_string = "State Slot: " + std::to_string(gui_config->current_save_slot) + " [" + exists + "]";
|
||||
S9xSetInfoString(info_string.c_str());
|
||||
GFX.InfoStringTimeout = 60;
|
||||
}
|
||||
|
@ -12,18 +12,18 @@
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include "common/video/opengl_context.hpp"
|
||||
#include "common/video/opengl/opengl_context.hpp"
|
||||
|
||||
#include "gtk_compat.h"
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
#include "common/video/glx_context.hpp"
|
||||
#include "common/video/opengl/glx_context.hpp"
|
||||
#endif
|
||||
#ifdef GDK_WINDOWING_WAYLAND
|
||||
#include "common/video/wayland_egl_context.hpp"
|
||||
#include "common/video/opengl/wayland_egl_context.hpp"
|
||||
#endif
|
||||
|
||||
#include "shaders/glsl.h"
|
||||
#include "vulkan/std_chrono_throttle.hpp"
|
||||
#include "common/video/opengl/shaders/glsl.h"
|
||||
#include "common/video/std_chrono_throttle.hpp"
|
||||
|
||||
#define BUFFER_OFFSET(i) ((char *)(i))
|
||||
|
||||
|
@ -66,12 +66,12 @@ bool S9xVulkanDisplayDriver::init_imgui()
|
||||
init_info.Device = context->device;;
|
||||
init_info.QueueFamily = context->graphics_queue_family_index;
|
||||
init_info.Queue = context->queue;
|
||||
init_info.DescriptorPool = imgui_descriptor_pool.get();
|
||||
init_info.DescriptorPool = static_cast<VkDescriptorPool>(imgui_descriptor_pool.get());
|
||||
init_info.Subpass = 0;
|
||||
init_info.MinImageCount = context->swapchain->get_num_frames();
|
||||
init_info.ImageCount = context->swapchain->get_num_frames();
|
||||
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
ImGui_ImplVulkan_Init(&init_info, context->swapchain->get_render_pass());
|
||||
ImGui_ImplVulkan_Init(&init_info, static_cast<VkRenderPass>(context->swapchain->get_render_pass()));
|
||||
|
||||
auto cmd = context->begin_cmd_buffer();
|
||||
ImGui_ImplVulkan_CreateFontsTexture(cmd);
|
||||
@ -222,6 +222,7 @@ void S9xVulkanDisplayDriver::update(uint16_t *buffer, int width, int height, int
|
||||
throttle.wait_for_frame_and_rebase_time();
|
||||
}
|
||||
|
||||
context->swapchain->set_vsync(gui_config->sync_to_vblank);
|
||||
context->swapchain->swap();
|
||||
|
||||
if (gui_config->reduce_input_lag)
|
||||
|
@ -7,13 +7,13 @@
|
||||
#pragma once
|
||||
#include "gtk_s9x.h"
|
||||
#include "gtk_display_driver.h"
|
||||
#include "vulkan/vulkan_context.hpp"
|
||||
#include "vulkan/vulkan_shader_chain.hpp"
|
||||
#include "vulkan/vulkan_simple_output.hpp"
|
||||
#include "vulkan/std_chrono_throttle.hpp"
|
||||
#include "common/video/vulkan/vulkan_context.hpp"
|
||||
#include "common/video/vulkan/vulkan_shader_chain.hpp"
|
||||
#include "common/video/vulkan/vulkan_simple_output.hpp"
|
||||
#include "common/video/std_chrono_throttle.hpp"
|
||||
|
||||
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
|
||||
#include "common/video/wayland_surface.hpp"
|
||||
#include "common/video/wayland/wayland_surface.hpp"
|
||||
#endif
|
||||
|
||||
class S9xVulkanDisplayDriver : public S9xDisplayDriver
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "gtk_s9x.h"
|
||||
#include "gtk_display.h"
|
||||
#include "gtk_shader_parameters.h"
|
||||
#include "shaders/glsl.h"
|
||||
#include "common/video/opengl/shaders/glsl.h"
|
||||
|
||||
#include "gfx.h"
|
||||
|
||||
|
@ -1247,6 +1247,8 @@ bool retro_load_game(const struct retro_game_info *game)
|
||||
if (!rom_loaded && log_cb)
|
||||
log_cb(RETRO_LOG_ERROR, "ROM loading failed...\n");
|
||||
|
||||
Memory.ClearSRAM();
|
||||
|
||||
return rom_loaded;
|
||||
}
|
||||
|
||||
|
@ -162,7 +162,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Snes9x (i386)</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>Snes9x 1.62.3, Copyright 1996-2023 Snes9x developers.</string>
|
||||
<string>Snes9x 1.63, Copyright 1996-2023 Snes9x developers.</string>
|
||||
<key>CFBundleHelpBookFolder</key>
|
||||
<string>Snes9x Help</string>
|
||||
<key>CFBundleHelpBookName</key>
|
||||
|
@ -45,7 +45,6 @@ NSWindowFrameAutosaveName const kCheatFinderWindowIdentifier = @"s9xCheatFinderW
|
||||
gameWindow.contentView.layer.backgroundColor = NSColor.blackColor.CGColor;
|
||||
|
||||
gameWindow.title = @"Snes9x";
|
||||
gameWindow.restorationClass = [self class];
|
||||
gameWindow.frameAutosaveName = kMainWindowIdentifier;
|
||||
gameWindow.releasedWhenClosed = NO;
|
||||
gameWindow.backgroundColor = NSColor.clearColor;
|
||||
@ -660,7 +659,6 @@ NSWindowFrameAutosaveName const kCheatFinderWindowIdentifier = @"s9xCheatFinderW
|
||||
window = self.cheatsWindowController.window;
|
||||
|
||||
window.title = NSLocalizedString(@"Cheats", nil);
|
||||
window.restorationClass = self.class;
|
||||
window.frameAutosaveName = kCheatsWindowIdentifier;
|
||||
window.releasedWhenClosed = NO;
|
||||
|
||||
@ -695,7 +693,6 @@ NSWindowFrameAutosaveName const kCheatFinderWindowIdentifier = @"s9xCheatFinderW
|
||||
window = self.cheatFinderWindowController.window;
|
||||
|
||||
window.title = NSLocalizedString(@"Cheat Finder", nil);
|
||||
window.restorationClass = self.class;
|
||||
window.frameAutosaveName = kCheatFinderWindowIdentifier;
|
||||
window.releasedWhenClosed = NO;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
|
||||
CFBundleName = "Snes9x";
|
||||
CFBundleShortVersionString = "1.62.3";
|
||||
CFBundleGetInfoString = "Snes9x 1.62.3, Copyright 1996-2023 Snes9x developers.";
|
||||
CFBundleShortVersionString = "1.63";
|
||||
CFBundleGetInfoString = "Snes9x 1.63, Copyright 1996-2023 Snes9x developers.";
|
||||
|
@ -131,7 +131,7 @@ bool SetButtonCodeForJoypadControl(uint32 vendorID, uint32 productID, uint32 ind
|
||||
void ClearButtonCodeForJoypad(uint32 vendorID, uint32 productID, uint32 index, S9xButtonCode buttonCode);
|
||||
|
||||
void ClearJoypad(uint32 vendorID, uint32 productID, uint32 index);
|
||||
std::unordered_map<struct JoypadInput, S9xButtonCode> GetJuypadButtons(uint32 vendorID, uint32 productID, uint32 index);
|
||||
std::unordered_map<struct JoypadInput, S9xButtonCode> GetJoypadButtons(uint32 vendorID, uint32 productID, uint32 index);
|
||||
|
||||
std::string LabelForInput(uint32 vendorID, uint32 productID, uint32 cookie, int32 value);
|
||||
|
||||
|
@ -684,7 +684,7 @@ void ClearJoypad(uint32 vendorID, uint32 productID, uint32 index)
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<struct JoypadInput, S9xButtonCode> GetJuypadButtons(uint32 vendorID, uint32 productID, uint32 index)
|
||||
std::unordered_map<struct JoypadInput, S9xButtonCode> GetJoypadButtons(uint32 vendorID, uint32 productID, uint32 index)
|
||||
{
|
||||
struct JoypadDevice device;
|
||||
device.vendorID = vendorID;
|
||||
@ -712,7 +712,50 @@ void SetUpHID (void)
|
||||
{
|
||||
IOHIDManagerRegisterInputValueCallback(hidManager, gamepadAction, NULL);
|
||||
IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
|
||||
IOHIDManagerSetDeviceMatching(hidManager, NULL);
|
||||
|
||||
CFMutableArrayRef matching = CFArrayCreateMutable(kCFAllocatorDefault, 4, &kCFTypeArrayCallBacks);
|
||||
|
||||
uint32 page = kHIDPage_GenericDesktop;
|
||||
uint32 usage = kHIDUsage_GD_Joystick;
|
||||
CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
|
||||
CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
|
||||
|
||||
CFMutableDictionaryRef entry = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
CFDictionarySetValue(entry, CFSTR(kIOHIDDeviceUsagePageKey), (void *)pageRef);
|
||||
CFDictionarySetValue(entry, CFSTR(kIOHIDDeviceUsageKey), (void *)usageRef);
|
||||
CFArrayAppendValue(matching, entry);
|
||||
CFRelease(usageRef);
|
||||
CFRelease(entry);
|
||||
|
||||
usage = kHIDUsage_GD_GamePad;
|
||||
usageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
|
||||
entry = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
CFDictionarySetValue(entry, CFSTR(kIOHIDDeviceUsagePageKey), (void *)pageRef);
|
||||
CFDictionarySetValue(entry, CFSTR(kIOHIDDeviceUsageKey), (void *)usageRef);
|
||||
CFArrayAppendValue(matching, entry);
|
||||
CFRelease(usageRef);
|
||||
CFRelease(entry);
|
||||
|
||||
usage = kHIDUsage_GD_MultiAxisController;
|
||||
usageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
|
||||
entry = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
CFDictionarySetValue(entry, CFSTR(kIOHIDDeviceUsagePageKey), (void *)pageRef);
|
||||
CFDictionarySetValue(entry, CFSTR(kIOHIDDeviceUsageKey), (void *)usageRef);
|
||||
CFArrayAppendValue(matching, entry);
|
||||
CFRelease(usageRef);
|
||||
CFRelease(pageRef);
|
||||
CFRelease(entry);
|
||||
|
||||
uint32 vendor = 0x28DE; // Valve, apparently
|
||||
CFNumberRef vendorRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendor);
|
||||
entry = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
CFDictionarySetValue(entry, CFSTR(kIOHIDVendorIDKey), (void *)pageRef);
|
||||
CFArrayAppendValue(matching, entry);
|
||||
CFRelease(vendorRef);
|
||||
CFRelease(entry);
|
||||
|
||||
IOHIDManagerSetDeviceMatchingMultiple(hidManager, matching);
|
||||
CFRelease(matching);
|
||||
|
||||
ParseDefaults();
|
||||
|
||||
|
@ -3286,7 +3286,7 @@ void QuitWithFatalError ( NSString *message)
|
||||
{
|
||||
pthread_mutex_lock(&keyLock);
|
||||
NSMutableArray<S9xJoypadInput *> *inputs = [NSMutableArray new];
|
||||
std::unordered_map<struct JoypadInput, S9xButtonCode> buttonCodeMap = GetJuypadButtons(vendorID, productID, index);
|
||||
std::unordered_map<struct JoypadInput, S9xButtonCode> buttonCodeMap = GetJoypadButtons(vendorID, productID, index);
|
||||
for (auto it = buttonCodeMap.begin(); it != buttonCodeMap.end(); ++it)
|
||||
{
|
||||
S9xJoypadInput *input = [S9xJoypadInput new];
|
||||
|
@ -1501,7 +1501,7 @@
|
||||
INFOPLIST_FILE = Snes9x/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
MARKETING_VERSION = 1.62.3;
|
||||
MARKETING_VERSION = 1.63;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.snes9x.macos.snes9x;
|
||||
@ -1548,7 +1548,7 @@
|
||||
INFOPLIST_FILE = Snes9x/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
MARKETING_VERSION = 1.62.3;
|
||||
MARKETING_VERSION = 1.63;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.snes9x.macos.snes9x;
|
||||
|
@ -3011,7 +3011,7 @@ void CMemory::Map_SA1LoROMMap (void)
|
||||
|
||||
// SA-1 Banks 40->4f
|
||||
for (int c = 0x400; c < 0x500; c++)
|
||||
SA1.Map[c] = SA1.WriteMap[c] = (uint8*)MAP_HIROM_SRAM;
|
||||
SA1.Map[c] = SA1.WriteMap[c] = (uint8*) MAP_SA1RAM;
|
||||
|
||||
// SA-1 Banks 60->6f
|
||||
for (int c = 0x600; c < 0x700; c++)
|
||||
|
@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(snes9x-qt VERSION 1.62)
|
||||
project(snes9x-qt VERSION 1.63)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
@ -115,7 +115,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
||||
list(APPEND LIBS libSDL2.a libz.a opengl32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32 dinput8)
|
||||
list(APPEND DEFINES SDL_MAIN_HANDLED)
|
||||
list(APPEND PLATFORM_SOURCES
|
||||
../common/video/wgl_context.cpp
|
||||
../common/video/opengl/wgl_context.cpp
|
||||
../external/glad/src/wgl.c
|
||||
src/resources/snes9x_win32.rc)
|
||||
else()
|
||||
@ -139,12 +139,18 @@ else()
|
||||
endif()
|
||||
|
||||
list(APPEND PLATFORM_SOURCES
|
||||
../common/video/glx_context.cpp
|
||||
../common/video/wayland_egl_context.cpp
|
||||
../common/video/wayland_surface.cpp
|
||||
../common/video/fractional-scale-v1.c
|
||||
../common/video/viewporter-client-protocol.c
|
||||
../common/video/wayland-idle-inhibit-unstable-v1.c
|
||||
../common/video/opengl/glx_context.cpp
|
||||
../common/video/opengl/glx_context.hpp
|
||||
../common/video/opengl/wayland_egl_context.cpp
|
||||
../common/video/opengl/wayland_egl_context.hpp
|
||||
../common/video/wayland/wayland_surface.cpp
|
||||
../common/video/wayland/wayland_surface.hpp
|
||||
../common/video/wayland/fractional-scale-v1.c
|
||||
../common/video/wayland/fractional-scale-v1.h
|
||||
../common/video/wayland/viewporter-client-protocol.c
|
||||
../common/video/wayland/viewporter-client-protocol.h
|
||||
../common/video/wayland/wayland-idle-inhibit-unstable-v1.c
|
||||
../common/video/wayland/wayland-idle-inhibit-unstable-v1.h
|
||||
../external/glad/src/glx.c
|
||||
../external/glad/src/egl.c)
|
||||
endif()
|
||||
@ -248,36 +254,36 @@ list(APPEND INCLUDES
|
||||
../external/stb
|
||||
"../external/glad/include")
|
||||
list(APPEND SOURCES
|
||||
../vulkan/slang_shader.cpp
|
||||
../vulkan/slang_shader.hpp
|
||||
../vulkan/slang_preset.cpp
|
||||
../vulkan/slang_preset.hpp
|
||||
../vulkan/vulkan_hpp_storage.cpp
|
||||
../vulkan/vk_mem_alloc_implementation.cpp
|
||||
../vulkan/vulkan_context.cpp
|
||||
../vulkan/vulkan_context.hpp
|
||||
../vulkan/vulkan_texture.cpp
|
||||
../vulkan/vulkan_texture.hpp
|
||||
../vulkan/vulkan_swapchain.cpp
|
||||
../vulkan/vulkan_swapchain.hpp
|
||||
../vulkan/vulkan_slang_pipeline.cpp
|
||||
../vulkan/vulkan_slang_pipeline.hpp
|
||||
../vulkan/vulkan_pipeline_image.cpp
|
||||
../vulkan/vulkan_pipeline_image.hpp
|
||||
../vulkan/vulkan_shader_chain.cpp
|
||||
../vulkan/vulkan_shader_chain.hpp
|
||||
../vulkan/vulkan_simple_output.hpp
|
||||
../vulkan/vulkan_simple_output.cpp
|
||||
../vulkan/std_chrono_throttle.cpp
|
||||
../vulkan/std_chrono_throttle.hpp
|
||||
../vulkan/slang_helpers.cpp
|
||||
../vulkan/slang_helpers.hpp
|
||||
../vulkan/slang_preset_ini.cpp
|
||||
../vulkan/slang_preset_ini.hpp
|
||||
../common/video/vulkan/slang_shader.cpp
|
||||
../common/video/vulkan/slang_shader.hpp
|
||||
../common/video/vulkan/slang_preset.cpp
|
||||
../common/video/vulkan/slang_preset.hpp
|
||||
../common/video/vulkan/vulkan_hpp_storage.cpp
|
||||
../common/video/vulkan/vk_mem_alloc_implementation.cpp
|
||||
../common/video/vulkan/vulkan_context.cpp
|
||||
../common/video/vulkan/vulkan_context.hpp
|
||||
../common/video/vulkan/vulkan_texture.cpp
|
||||
../common/video/vulkan/vulkan_texture.hpp
|
||||
../common/video/vulkan/vulkan_swapchain.cpp
|
||||
../common/video/vulkan/vulkan_swapchain.hpp
|
||||
../common/video/vulkan/vulkan_slang_pipeline.cpp
|
||||
../common/video/vulkan/vulkan_slang_pipeline.hpp
|
||||
../common/video/vulkan/vulkan_pipeline_image.cpp
|
||||
../common/video/vulkan/vulkan_pipeline_image.hpp
|
||||
../common/video/vulkan/vulkan_shader_chain.cpp
|
||||
../common/video/vulkan/vulkan_shader_chain.hpp
|
||||
../common/video/vulkan/vulkan_simple_output.hpp
|
||||
../common/video/vulkan/vulkan_simple_output.cpp
|
||||
../common/video/std_chrono_throttle.cpp
|
||||
../common/video/std_chrono_throttle.hpp
|
||||
../common/video/vulkan/slang_helpers.cpp
|
||||
../common/video/vulkan/slang_helpers.hpp
|
||||
../common/video/vulkan/slang_preset_ini.cpp
|
||||
../common/video/vulkan/slang_preset_ini.hpp
|
||||
../external/stb/stb_image_implementation.cpp
|
||||
../shaders/glsl.cpp
|
||||
../shaders/slang.cpp
|
||||
../shaders/shader_helpers.cpp)
|
||||
../common/video/opengl/shaders/glsl.cpp
|
||||
../common/video/opengl/shaders/slang.cpp
|
||||
../common/video/opengl/shaders/shader_helpers.cpp)
|
||||
|
||||
list(APPEND DEFINES "IMGUI_IMPL_VULKAN_NO_PROTOTYPES")
|
||||
list(APPEND SOURCES ../external/imgui/imgui.cpp
|
||||
|
@ -63,7 +63,7 @@ void CheatsDialog::addCode()
|
||||
if (description.empty())
|
||||
description = tr("No description").toStdString();
|
||||
|
||||
if (app->addCheat(description, code))
|
||||
if (!app->addCheat(description, code))
|
||||
{
|
||||
QMessageBox::information(this, tr("Invalid Cheat"), tr("The cheat you entered was not valid."));
|
||||
return;
|
||||
|
@ -98,6 +98,8 @@ DisplayPanel::DisplayPanel(EmuApplication *app_)
|
||||
if (recreate)
|
||||
app->window->recreateUIAssets();
|
||||
});
|
||||
|
||||
groupBox_software_filters->hide();
|
||||
}
|
||||
|
||||
DisplayPanel::~DisplayPanel()
|
||||
|
@ -311,7 +311,7 @@ Output directly will cause the screen to change between the two modes and look w
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<widget class="QGroupBox" name="groupBox_software_filters">
|
||||
<property name="title">
|
||||
<string>Software Filters</string>
|
||||
</property>
|
||||
|
@ -335,22 +335,21 @@ void EmuApplication::handleBinding(std::string name, bool pressed)
|
||||
window->pauseContinue();
|
||||
}
|
||||
|
||||
else if (name == "IncreaseSlot")
|
||||
else if (name == "IncreaseSlot" || name == "DecreaseSlot")
|
||||
{
|
||||
save_slot++;
|
||||
if (name == "IncreaseSlot")
|
||||
save_slot++;
|
||||
else
|
||||
save_slot--;
|
||||
|
||||
if (save_slot > 999)
|
||||
save_slot = 0;
|
||||
emu_thread->runOnThread([&] {
|
||||
core->setMessage("Current slot: " + std::to_string(save_slot));
|
||||
});
|
||||
}
|
||||
else if (name == "DecreaseSlot")
|
||||
{
|
||||
save_slot--;
|
||||
if (save_slot < 0)
|
||||
save_slot = 999;
|
||||
emu_thread->runOnThread([&] {
|
||||
core->setMessage("Current slot: " + std::to_string(save_slot));
|
||||
|
||||
emu_thread->runOnThread([&, slot = this->save_slot] {
|
||||
std::string status = core->slotUsed(slot) ? " [used]" : " [empty]";
|
||||
core->setMessage("Current slot: " + std::to_string(save_slot) + status);
|
||||
});
|
||||
}
|
||||
else if (name == "SaveState")
|
||||
@ -643,6 +642,11 @@ QString EmuApplication::iconPrefix()
|
||||
return blackicons;
|
||||
}
|
||||
|
||||
std::string EmuApplication::getContentFolder()
|
||||
{
|
||||
return core->getContentFolder();
|
||||
}
|
||||
|
||||
void EmuThread::runOnThread(std::function<void()> func, bool blocking)
|
||||
{
|
||||
if (QThread::currentThread() != this)
|
||||
|
@ -85,6 +85,7 @@ struct EmuApplication
|
||||
void stopThread();
|
||||
bool isCoreActive();
|
||||
QString iconPrefix();
|
||||
std::string getContentFolder();
|
||||
|
||||
std::vector<std::tuple<bool, std::string, std::string>> getCheatList();
|
||||
void disableAllCheats();
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QImage>
|
||||
#include "../../vulkan/std_chrono_throttle.hpp"
|
||||
#include "common/video/std_chrono_throttle.hpp"
|
||||
|
||||
class EmuConfig;
|
||||
|
||||
|
@ -3,17 +3,17 @@
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#include <QTimer>
|
||||
#include <QMessageBox>
|
||||
#include "common/video/opengl_context.hpp"
|
||||
#include "common/video/opengl/opengl_context.hpp"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include "common/video/glx_context.hpp"
|
||||
#include "common/video/wayland_egl_context.hpp"
|
||||
#include "common/video/opengl/glx_context.hpp"
|
||||
#include "common/video/opengl/wayland_egl_context.hpp"
|
||||
using namespace QNativeInterface;
|
||||
#include <X11/Xlib.h>
|
||||
#else
|
||||
#include "common/video/wgl_context.hpp"
|
||||
#include "common/video/opengl/wgl_context.hpp"
|
||||
#endif
|
||||
#include "shaders/glsl.h"
|
||||
#include "common/video/opengl/shaders/glsl.h"
|
||||
#include "EmuMainWindow.hpp"
|
||||
#include "snes9x_imgui.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
@ -303,7 +303,7 @@ void EmuCanvasOpenGL::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
|
||||
if (!context)
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
auto g = parent->geometry();
|
||||
|
@ -1,10 +1,9 @@
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QGuiApplication>
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#include <QTimer>
|
||||
#include <QtEvents>
|
||||
#include <QMessageBox>
|
||||
#include "EmuCanvasVulkan.hpp"
|
||||
#include "src/ShaderParametersDialog.hpp"
|
||||
#include "EmuMainWindow.hpp"
|
||||
|
||||
#include "snes9x_imgui.h"
|
||||
@ -178,9 +177,6 @@ void EmuCanvasVulkan::draw()
|
||||
if (!window->isVisible())
|
||||
return;
|
||||
|
||||
if (context->swapchain->set_vsync(config->enable_vsync))
|
||||
context->recreate_swapchain();
|
||||
|
||||
if (S9xImGuiDraw(width() * devicePixelRatioF(), height() * devicePixelRatioF()))
|
||||
{
|
||||
auto draw_data = ImGui::GetDrawData();
|
||||
@ -205,10 +201,11 @@ void EmuCanvasVulkan::draw()
|
||||
if (retval)
|
||||
{
|
||||
throttle();
|
||||
context->swapchain->set_vsync(config->enable_vsync);
|
||||
context->swapchain->swap();
|
||||
if (config->reduce_input_lag)
|
||||
{
|
||||
context->wait_idle();
|
||||
context->swapchain->wait_on_frames();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,11 +3,11 @@
|
||||
|
||||
#include "EmuCanvas.hpp"
|
||||
#include "ShaderParametersDialog.hpp"
|
||||
#include "../../vulkan/vulkan_simple_output.hpp"
|
||||
#include "../../vulkan/vulkan_shader_chain.hpp"
|
||||
#include "common/video/vulkan/vulkan_simple_output.hpp"
|
||||
#include "common/video/vulkan/vulkan_shader_chain.hpp"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include "common/video/wayland_surface.hpp"
|
||||
#include "common/video/wayland/wayland_surface.hpp"
|
||||
#endif
|
||||
|
||||
class EmuCanvasVulkan : public EmuCanvas
|
||||
|
@ -144,7 +144,7 @@ std::string EmuConfig::findConfigDir()
|
||||
fs::path path;
|
||||
|
||||
auto app_dir_path = QGuiApplication::applicationDirPath();
|
||||
auto config_file = QDir(app_dir_path).absoluteFilePath("snes9x.conf");
|
||||
auto config_file = QDir(app_dir_path).absoluteFilePath("snes9x-qt.conf");
|
||||
if (QFile::exists(config_file))
|
||||
return app_dir_path.toStdString();
|
||||
|
||||
@ -164,12 +164,12 @@ std::string EmuConfig::findConfigDir()
|
||||
path = "./.snes9x";
|
||||
}
|
||||
#else
|
||||
if ((dir = getenv("LOCALAPPDATA")))
|
||||
if ((dir = getenv("APPDATA")))
|
||||
{
|
||||
path = dir;
|
||||
path /= "Snes9x";
|
||||
}
|
||||
else if ((dir = getenv("APPDATA")))
|
||||
else if ((dir = getenv("LOCALAPPDATA")))
|
||||
{
|
||||
path = dir;
|
||||
path /= "Snes9x";
|
||||
|
@ -22,6 +22,27 @@
|
||||
|
||||
static EmuSettingsWindow *g_emu_settings_window = nullptr;
|
||||
|
||||
class DefaultBackground
|
||||
: public QWidget
|
||||
{
|
||||
public:
|
||||
DefaultBackground(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void paintEvent(QPaintEvent *event) override
|
||||
{
|
||||
QPainter paint(this);
|
||||
QLinearGradient gradient(0.0, 0.0, 0.0, event->rect().toRectF().height());
|
||||
gradient.setColorAt(0.0, QColor(0, 0, 128));
|
||||
gradient.setColorAt(1.0, QColor(0, 0, 0));
|
||||
|
||||
paint.setBrush(QBrush(gradient));
|
||||
paint.drawRect(0, 0, event->rect().width(), event->rect().height());
|
||||
}
|
||||
};
|
||||
|
||||
EmuMainWindow::EmuMainWindow(EmuApplication *app)
|
||||
: app(app)
|
||||
{
|
||||
@ -33,12 +54,11 @@ EmuMainWindow::EmuMainWindow(EmuApplication *app)
|
||||
mouse_timer.setTimerType(Qt::CoarseTimer);
|
||||
mouse_timer.setInterval(1000);
|
||||
mouse_timer.callOnTimeout([&] {
|
||||
if (cursor_visible && isActivelyDrawing())
|
||||
{
|
||||
if (canvas)
|
||||
canvas->setCursor(QCursor(Qt::BlankCursor));
|
||||
cursor_visible = false;
|
||||
mouse_timer.stop();
|
||||
if (cursor_visible && isActivelyDrawing()) {
|
||||
if (canvas)
|
||||
canvas->setCursor(QCursor(Qt::BlankCursor));
|
||||
cursor_visible = false;
|
||||
mouse_timer.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -71,10 +91,20 @@ void EmuMainWindow::destroyCanvas()
|
||||
widget->deinit();
|
||||
delete widget;
|
||||
}
|
||||
canvas = nullptr;
|
||||
}
|
||||
|
||||
bool EmuMainWindow::createCanvas()
|
||||
{
|
||||
auto fallback = [this]() -> bool {
|
||||
QMessageBox::warning(
|
||||
this, tr("Unable to Start Display Driver"),
|
||||
tr("Unable to create a %1 context. Attempting to use qt.")
|
||||
.arg(QString::fromUtf8(app->config->display_driver)));
|
||||
app->config->display_driver = "qt";
|
||||
return createCanvas();
|
||||
};
|
||||
|
||||
if (app->config->display_driver != "vulkan" &&
|
||||
app->config->display_driver != "opengl" &&
|
||||
app->config->display_driver != "qt")
|
||||
@ -94,7 +124,7 @@ bool EmuMainWindow::createCanvas()
|
||||
if (!canvas->createContext())
|
||||
{
|
||||
delete canvas;
|
||||
return false;
|
||||
return fallback();
|
||||
}
|
||||
}
|
||||
else if (app->config->display_driver == "opengl")
|
||||
@ -121,7 +151,7 @@ bool EmuMainWindow::createCanvas()
|
||||
if (!canvas->createContext())
|
||||
{
|
||||
delete canvas;
|
||||
return false;
|
||||
return fallback();
|
||||
}
|
||||
}
|
||||
else if (app->config->display_driver == "opengl")
|
||||
@ -141,17 +171,12 @@ bool EmuMainWindow::createCanvas()
|
||||
|
||||
void EmuMainWindow::recreateCanvas()
|
||||
{
|
||||
if (!canvas)
|
||||
return;
|
||||
|
||||
app->suspendThread();
|
||||
destroyCanvas();
|
||||
|
||||
if (!createCanvas())
|
||||
{
|
||||
QMessageBox::warning(this,
|
||||
tr("Unable to Start Display Driver"),
|
||||
tr("Unable to create a %1 context. Attempting to use qt.").arg(QString::fromUtf8(app->config->display_driver)));
|
||||
app->config->display_driver = "qt";
|
||||
createCanvas();
|
||||
}
|
||||
createCanvas();
|
||||
|
||||
app->unsuspendThread();
|
||||
}
|
||||
@ -167,9 +192,11 @@ void EmuMainWindow::createWidgets()
|
||||
setWindowTitle("Snes9x");
|
||||
setWindowIcon(QIcon(":/icons/snes9x.svg"));
|
||||
|
||||
auto iconset = app->iconPrefix();
|
||||
|
||||
// File menu
|
||||
auto file_menu = new QMenu(tr("&File"));
|
||||
auto open_item = file_menu->addAction(QIcon::fromTheme("document-open"), tr("&Open File..."));
|
||||
auto open_item = file_menu->addAction(QIcon(iconset + "open.svg"), tr("&Open File..."));
|
||||
open_item->connect(open_item, &QAction::triggered, this, [&] {
|
||||
openFile();
|
||||
});
|
||||
@ -200,7 +227,7 @@ void EmuMainWindow::createWidgets()
|
||||
|
||||
load_state_menu->addSeparator();
|
||||
|
||||
auto load_state_file_item = load_state_menu->addAction(QIcon::fromTheme("document-open"), tr("From &File..."));
|
||||
auto load_state_file_item = load_state_menu->addAction(QIcon(iconset + "open.svg"), tr("From &File..."));
|
||||
connect(load_state_file_item, &QAction::triggered, [&] {
|
||||
this->chooseState(false);
|
||||
});
|
||||
@ -208,7 +235,7 @@ void EmuMainWindow::createWidgets()
|
||||
|
||||
load_state_menu->addSeparator();
|
||||
|
||||
auto load_state_undo_item = load_state_menu->addAction(QIcon::fromTheme("edit-undo"), tr("&Undo Load State"));
|
||||
auto load_state_undo_item = load_state_menu->addAction(QIcon(iconset + "refresh.svg"), tr("&Undo Load State"));
|
||||
connect(load_state_undo_item, &QAction::triggered, [&] {
|
||||
app->loadUndoState();
|
||||
});
|
||||
@ -217,14 +244,14 @@ void EmuMainWindow::createWidgets()
|
||||
file_menu->addMenu(load_state_menu);
|
||||
|
||||
save_state_menu->addSeparator();
|
||||
auto save_state_file_item = save_state_menu->addAction(QIcon::fromTheme("document-save"), tr("To &File..."));
|
||||
auto save_state_file_item = save_state_menu->addAction(QIcon(iconset + "save.svg"), tr("To &File..."));
|
||||
connect(save_state_file_item, &QAction::triggered, [&] {
|
||||
this->chooseState(true);
|
||||
});
|
||||
core_actions.push_back(save_state_file_item);
|
||||
file_menu->addMenu(save_state_menu);
|
||||
|
||||
auto exit_item = new QAction(QIcon::fromTheme("application-exit"), tr("E&xit"));
|
||||
auto exit_item = new QAction(QIcon(iconset + "exit.svg"), tr("E&xit"));
|
||||
exit_item->connect(exit_item, &QAction::triggered, this, [&](bool checked) {
|
||||
close();
|
||||
});
|
||||
@ -245,7 +272,7 @@ void EmuMainWindow::createWidgets()
|
||||
});
|
||||
core_actions.push_back(run_item);
|
||||
|
||||
auto pause_item = emulation_menu->addAction(QIcon::fromTheme("media-playback-pause"), tr("&Pause"));
|
||||
auto pause_item = emulation_menu->addAction(QIcon(iconset + "pause.svg"), tr("&Pause"));
|
||||
connect(pause_item, &QAction::triggered, [&] {
|
||||
if (!manual_pause)
|
||||
{
|
||||
@ -257,7 +284,7 @@ void EmuMainWindow::createWidgets()
|
||||
|
||||
emulation_menu->addSeparator();
|
||||
|
||||
auto reset_item = emulation_menu->addAction(QIcon::fromTheme("view-refresh"), tr("Rese&t"));
|
||||
auto reset_item = emulation_menu->addAction(QIcon(iconset + "refresh.svg"), tr("Rese&t"));
|
||||
connect(reset_item, &QAction::triggered, [&] {
|
||||
app->reset();
|
||||
if (manual_pause)
|
||||
@ -268,7 +295,7 @@ void EmuMainWindow::createWidgets()
|
||||
});
|
||||
core_actions.push_back(reset_item);
|
||||
|
||||
auto hard_reset_item = emulation_menu->addAction(QIcon::fromTheme("process-stop"), tr("&Hard Reset"));
|
||||
auto hard_reset_item = emulation_menu->addAction(QIcon(iconset + "reset.svg"), tr("&Hard Reset"));
|
||||
connect(hard_reset_item, &QAction::triggered, [&] {
|
||||
app->powerCycle();
|
||||
if (manual_pause)
|
||||
@ -308,7 +335,7 @@ void EmuMainWindow::createWidgets()
|
||||
|
||||
view_menu->addSeparator();
|
||||
|
||||
auto fullscreen_item = new QAction(QIcon::fromTheme("view-fullscreen"), tr("&Fullscreen"));
|
||||
auto fullscreen_item = new QAction(QIcon(iconset + "fullscreen.svg"), tr("&Fullscreen"));
|
||||
view_menu->addAction(fullscreen_item);
|
||||
fullscreen_item->connect(fullscreen_item, &QAction::triggered, [&](bool checked) {
|
||||
toggleFullscreen();
|
||||
@ -328,7 +355,6 @@ void EmuMainWindow::createWidgets()
|
||||
tr("&Controllers..."),
|
||||
tr("Shortcu&ts..."),
|
||||
tr("&Files...") };
|
||||
QString iconset = app->iconPrefix();
|
||||
const char *setting_icons[] = { "settings.svg",
|
||||
"display.svg",
|
||||
"sound.svg",
|
||||
@ -361,6 +387,8 @@ void EmuMainWindow::createWidgets()
|
||||
|
||||
if (app->config->main_window_width != 0 && app->config->main_window_height != 0)
|
||||
resize(app->config->main_window_width, app->config->main_window_height);
|
||||
|
||||
setCentralWidget(new DefaultBackground(this));
|
||||
}
|
||||
|
||||
void EmuMainWindow::resizeToMultiple(int multiple)
|
||||
@ -452,6 +480,12 @@ bool EmuMainWindow::openFile(std::string filename)
|
||||
setCoreActionsEnabled(true);
|
||||
if (!isFullScreen() && app->config->fullscreen_on_open)
|
||||
toggleFullscreen();
|
||||
|
||||
if (!canvas)
|
||||
if (!createCanvas())
|
||||
return false;
|
||||
|
||||
QApplication::sync();
|
||||
app->startGame();
|
||||
mouse_timer.start();
|
||||
return true;
|
||||
|
@ -39,8 +39,8 @@ class EmuMainWindow : public QMainWindow
|
||||
void gameChanging();
|
||||
void toggleMouseGrab();
|
||||
std::vector<std::string> getDisplayDeviceList();
|
||||
EmuApplication *app;
|
||||
EmuCanvas *canvas;
|
||||
EmuApplication *app = nullptr;
|
||||
EmuCanvas *canvas = nullptr;
|
||||
|
||||
private:
|
||||
void idle();
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "EmuConfig.hpp"
|
||||
#include <QSpinBox>
|
||||
#include <QFileDialog>
|
||||
#include <QDesktopServices>
|
||||
|
||||
FoldersPanel::FoldersPanel(EmuApplication *app_)
|
||||
: app(app_)
|
||||
@ -29,17 +30,6 @@ void FoldersPanel::connectEntry(QComboBox *combo, QLineEdit *lineEdit, QPushButt
|
||||
this->refreshEntry(combo, lineEdit, browse, location, folder);
|
||||
app->updateSettings();
|
||||
});
|
||||
|
||||
QObject::connect(browse, &QPushButton::pressed, [=] {
|
||||
QFileDialog dialog(this, tr("Select a Folder"));
|
||||
dialog.setFileMode(QFileDialog::Directory);
|
||||
dialog.setDirectory(QString::fromUtf8(*folder));
|
||||
if (!dialog.exec())
|
||||
return;
|
||||
*folder = dialog.selectedFiles().at(0).toUtf8();
|
||||
lineEdit->setText(QString::fromUtf8(*folder));
|
||||
app->updateSettings();
|
||||
});
|
||||
}
|
||||
|
||||
void FoldersPanel::refreshData()
|
||||
@ -53,18 +43,56 @@ void FoldersPanel::refreshData()
|
||||
|
||||
void FoldersPanel::refreshEntry(QComboBox *combo, QLineEdit *lineEdit, QPushButton *browse, int *location, std::string *folder)
|
||||
{
|
||||
QString rom_dir;
|
||||
bool custom = (*location == EmuConfig::eCustomDirectory);
|
||||
combo->setCurrentIndex(*location);
|
||||
if (custom)
|
||||
{
|
||||
lineEdit->setText(QString::fromUtf8(*folder));
|
||||
}
|
||||
else if (*location == EmuConfig::eConfigDirectory)
|
||||
{
|
||||
lineEdit->setText(tr("Config folder is %1").arg(app->config->findConfigDir().c_str()));
|
||||
else
|
||||
lineEdit->clear();
|
||||
} else
|
||||
{
|
||||
rom_dir = QString::fromStdString(app->getContentFolder());
|
||||
if (rom_dir.isEmpty())
|
||||
rom_dir = QString::fromStdString(app->config->last_rom_folder);
|
||||
|
||||
lineEdit->setText("ROM Folder: " + rom_dir);
|
||||
}
|
||||
|
||||
lineEdit->setEnabled(custom);
|
||||
browse->setEnabled(custom);
|
||||
|
||||
browse->disconnect();
|
||||
if (custom)
|
||||
{
|
||||
browse->setText(tr("Browse..."));
|
||||
QObject::connect(browse, &QPushButton::pressed, [=] {
|
||||
QFileDialog dialog(this, tr("Select a Folder"));
|
||||
dialog.setFileMode(QFileDialog::Directory);
|
||||
dialog.setDirectory(QString::fromUtf8(*folder));
|
||||
if (!dialog.exec())
|
||||
return;
|
||||
*folder = dialog.selectedFiles().at(0).toUtf8();
|
||||
lineEdit->setText(QString::fromStdString(*folder));
|
||||
app->updateSettings();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
QString dir{};
|
||||
if (*location == EmuConfig::eConfigDirectory)
|
||||
dir = app->config->findConfigDir().c_str();
|
||||
else if (*location == EmuConfig::eROMDirectory)
|
||||
dir = rom_dir;
|
||||
|
||||
QObject::connect(browse, &QPushButton::pressed, [dir] {
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(dir));
|
||||
});
|
||||
lineEdit->setEnabled(custom);
|
||||
browse->setText(tr("Open Folder..."));
|
||||
}
|
||||
}
|
||||
|
||||
void FoldersPanel::showEvent(QShowEvent *event)
|
||||
|
@ -74,7 +74,6 @@ SDLInputManager::DiscretizeHatEvent(SDL_Event &event)
|
||||
for (auto &s : { SDL_HAT_UP, SDL_HAT_DOWN, SDL_HAT_LEFT, SDL_HAT_RIGHT })
|
||||
if ((old_state & s) != (new_state & s))
|
||||
{
|
||||
printf(" old: %d, new: %d\n", old_state, new_state);
|
||||
dhe.direction = s;
|
||||
dhe.pressed = (new_state & s);
|
||||
old_state = new_state;
|
||||
@ -226,4 +225,4 @@ std::vector<std::pair<int, std::string>> SDLInputManager::getXInputControllers()
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "Snes9xController.hpp"
|
||||
#include "EmuConfig.hpp"
|
||||
#include "SoftwareScalers.hpp"
|
||||
#include "fscompat.h"
|
||||
#include <filesystem>
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@ -531,7 +532,10 @@ void Snes9xController::clearSoundBuffer()
|
||||
|
||||
void S9xMessage(int message_class, int type, const char *message)
|
||||
{
|
||||
S9xSetInfoString(message);
|
||||
if (type == S9X_ROM_INFO)
|
||||
S9xSetInfoString(Memory.GetMultilineROMInfo().c_str());
|
||||
|
||||
printf("%s\n", message);
|
||||
}
|
||||
|
||||
const char *S9xStringInput(const char *prompt)
|
||||
@ -718,6 +722,11 @@ std::string Snes9xController::getStateFolder()
|
||||
return S9xGetDirectory(SNAPSHOT_DIR);
|
||||
}
|
||||
|
||||
bool Snes9xController::slotUsed(int slot)
|
||||
{
|
||||
return fs::exists(save_slot_path(slot));
|
||||
}
|
||||
|
||||
bool Snes9xController::loadState(int slot)
|
||||
{
|
||||
return loadState(save_slot_path(slot).u8string());
|
||||
@ -850,3 +859,8 @@ int Snes9xController::modifyCheat(int index, std::string name, std::string code)
|
||||
{
|
||||
return S9xModifyCheatGroup(index, name, code);
|
||||
}
|
||||
|
||||
std::string Snes9xController::getContentFolder()
|
||||
{
|
||||
return S9xGetDirectory(ROMFILENAME_DIR);
|
||||
}
|
@ -16,13 +16,12 @@ class Snes9xController
|
||||
void deinit();
|
||||
void mainLoop();
|
||||
bool openFile(std::string filename);
|
||||
bool slotUsed(int slot);
|
||||
bool loadState(std::string filename);
|
||||
bool loadState(int slot);
|
||||
void loadUndoState();
|
||||
bool saveState(std::string filename);
|
||||
bool saveState(int slot);
|
||||
void increaseSaveSlot();
|
||||
void decreaseSaveSlot();
|
||||
void updateSettings(const EmuConfig * const config);
|
||||
void updateBindings(const EmuConfig * const config);
|
||||
void reportBinding(EmuBinding b, bool active);
|
||||
@ -47,6 +46,7 @@ class Snes9xController
|
||||
int tryImportCheats(std::string filename);
|
||||
std::string validateCheat(std::string code);
|
||||
int modifyCheat(int index, std::string name, std::string code);
|
||||
std::string getContentFolder();
|
||||
|
||||
std::string getStateFolder();
|
||||
std::string config_folder;
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <clocale>
|
||||
#include <qnamespace.h>
|
||||
#include <QStyle>
|
||||
#include <QStyleHints>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <csignal>
|
||||
@ -28,35 +29,42 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (emu.qtapp->platformName() == "windows")
|
||||
{
|
||||
emu.qtapp->setStyle("fusion");
|
||||
if (emu.qtapp->styleHints()->colorScheme() == Qt::ColorScheme::Dark)
|
||||
{
|
||||
emu.qtapp->setStyle("fusion");
|
||||
|
||||
const QColor darkGray(53, 53, 53);
|
||||
const QColor gray(128, 128, 128);
|
||||
const QColor black(25, 25, 25);
|
||||
const QColor blue(198, 238, 255);
|
||||
const QColor blue2(0, 88, 208);
|
||||
const QColor darkGray(53, 53, 53);
|
||||
const QColor gray(128, 128, 128);
|
||||
const QColor black(25, 25, 25);
|
||||
const QColor blue(198, 238, 255);
|
||||
const QColor blue2(0, 88, 208);
|
||||
|
||||
QPalette darkPalette;
|
||||
darkPalette.setColor(QPalette::Window, darkGray);
|
||||
darkPalette.setColor(QPalette::WindowText, Qt::white);
|
||||
darkPalette.setColor(QPalette::Base, black);
|
||||
darkPalette.setColor(QPalette::AlternateBase, darkGray);
|
||||
darkPalette.setColor(QPalette::ToolTipBase, blue2);
|
||||
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
|
||||
darkPalette.setColor(QPalette::Text, Qt::white);
|
||||
darkPalette.setColor(QPalette::Button, darkGray);
|
||||
darkPalette.setColor(QPalette::ButtonText, Qt::white);
|
||||
darkPalette.setColor(QPalette::Link, blue);
|
||||
darkPalette.setColor(QPalette::Highlight, blue2);
|
||||
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
|
||||
darkPalette.setColor(QPalette::PlaceholderText, QColor(Qt::white).darker());
|
||||
QPalette darkPalette;
|
||||
darkPalette.setColor(QPalette::Window, darkGray);
|
||||
darkPalette.setColor(QPalette::WindowText, Qt::white);
|
||||
darkPalette.setColor(QPalette::Base, black);
|
||||
darkPalette.setColor(QPalette::AlternateBase, darkGray);
|
||||
darkPalette.setColor(QPalette::ToolTipBase, blue2);
|
||||
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
|
||||
darkPalette.setColor(QPalette::Text, Qt::white);
|
||||
darkPalette.setColor(QPalette::Button, darkGray);
|
||||
darkPalette.setColor(QPalette::ButtonText, Qt::white);
|
||||
darkPalette.setColor(QPalette::Link, blue);
|
||||
darkPalette.setColor(QPalette::Highlight, blue2);
|
||||
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
|
||||
darkPalette.setColor(QPalette::PlaceholderText, QColor(Qt::white).darker());
|
||||
|
||||
darkPalette.setColor(QPalette::Active, QPalette::Button, darkGray);
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray);
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray);
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::Light, darkGray);
|
||||
emu.qtapp->setPalette(darkPalette);
|
||||
darkPalette.setColor(QPalette::Active, QPalette::Button, darkGray);
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray);
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray);
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::Light, darkGray);
|
||||
emu.qtapp->setPalette(darkPalette);
|
||||
}
|
||||
else
|
||||
{
|
||||
emu.qtapp->setStyle("windowsvista");
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
|
1
qt/src/resources/blackicons/exit.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><path d="M0,0h24v24H0V0z" fill="none"/></g><g><path d="M17,8l-1.41,1.41L17.17,11H9v2h8.17l-1.58,1.58L17,16l4-4L17,8z M5,5h7V3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h7v-2H5V5z"/></g></svg>
|
After Width: | Height: | Size: 324 B |
1
qt/src/resources/blackicons/fullscreen.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>
|
After Width: | Height: | Size: 235 B |
1
qt/src/resources/blackicons/open.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><path d="M15,22H6c-1.1,0-2-0.9-2-2V4c0-1.1,0.9-2,2-2h8l6,6v6h-2V9h-5V4H6v16h9V22z M19,21.66l0-2.24l2.95,2.95l1.41-1.41L20.41,18 h2.24v-2H17v5.66H19z"/></g></svg>
|
After Width: | Height: | Size: 349 B |
1
qt/src/resources/blackicons/pause.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
|
After Width: | Height: | Size: 190 B |
1
qt/src/resources/blackicons/play.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M8 5v14l11-7z"/></svg>
|
After Width: | Height: | Size: 170 B |
1
qt/src/resources/blackicons/refresh.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/></svg>
|
After Width: | Height: | Size: 258 B |
1
qt/src/resources/blackicons/reset.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><path d="M0,0h24v24H0V0z" fill="none"/></g><g><g><path d="M6,13c0-1.65,0.67-3.15,1.76-4.24L6.34,7.34C4.9,8.79,4,10.79,4,13c0,4.08,3.05,7.44,7,7.93v-2.02 C8.17,18.43,6,15.97,6,13z M20,13c0-4.42-3.58-8-8-8c-0.06,0-0.12,0.01-0.18,0.01l1.09-1.09L11.5,2.5L8,6l3.5,3.5l1.41-1.41 l-1.08-1.08C11.89,7.01,11.95,7,12,7c3.31,0,6,2.69,6,6c0,2.97-2.17,5.43-5,5.91v2.02C16.95,20.44,20,17.08,20,13z"/></g></g></svg>
|
After Width: | Height: | Size: 539 B |
1
qt/src/resources/blackicons/save.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm2 16H5V5h11.17L19 7.83V19zm-7-7c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3zM6 6h9v4H6z"/></svg>
|
After Width: | Height: | Size: 323 B |
@ -21,6 +21,14 @@
|
||||
<file>whiteicons/up.svg</file>
|
||||
<file>whiteicons/x.svg</file>
|
||||
<file>whiteicons/y.svg</file>
|
||||
<file>whiteicons/open.svg</file>
|
||||
<file>whiteicons/pause.svg</file>
|
||||
<file>whiteicons/play.svg</file>
|
||||
<file>whiteicons/refresh.svg</file>
|
||||
<file>whiteicons/reset.svg</file>
|
||||
<file>whiteicons/save.svg</file>
|
||||
<file>whiteicons/exit.svg</file>
|
||||
<file>whiteicons/fullscreen.svg</file>
|
||||
<file>snes9x.svg</file>
|
||||
<file>blackicons/settings.svg</file>
|
||||
<file>blackicons/folders.svg</file>
|
||||
@ -43,5 +51,13 @@
|
||||
<file>blackicons/x.svg</file>
|
||||
<file>blackicons/y.svg</file>
|
||||
<file>blackicons/shader.svg</file>
|
||||
<file>blackicons/open.svg</file>
|
||||
<file>blackicons/pause.svg</file>
|
||||
<file>blackicons/play.svg</file>
|
||||
<file>blackicons/refresh.svg</file>
|
||||
<file>blackicons/reset.svg</file>
|
||||
<file>blackicons/save.svg</file>
|
||||
<file>blackicons/exit.svg</file>
|
||||
<file>blackicons/fullscreen.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -13,12 +13,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "http://www.snes9x.com"
|
||||
VALUE "FileDescription", "Snes9x"
|
||||
VALUE "FileVersion", "1.62.3"
|
||||
VALUE "FileVersion", "1.63"
|
||||
VALUE "InternalName", "Snes9x"
|
||||
VALUE "LegalCopyright", "Copyright (C) 1996-2023"
|
||||
VALUE "OriginalFilename", "snes9x-qt.exe"
|
||||
VALUE "ProductName", "Snes9x Qt Interface"
|
||||
VALUE "ProductVersion", "1.62.3"
|
||||
VALUE "ProductVersion", "1.63"
|
||||
END
|
||||
END
|
||||
|
||||
|