Update to bsnes v053 release.
This release greatly polishes the user interface, adds a new cheat code search utility, adds the snesfilter library, and adds Qt-based GUI support to both snesfilter and snesreader. snesfilter gains 2xSaI, Super 2xSaI and Super Eagle support, plus full configuration for both the NTSC and scanline filters; and snesreader gains support support for multi-file ROM archives (eg GoodMerge sets.) Statically linking Qt to bsnes, snesfilter and snesreader would be too prohibitive size-wise (~10MB or so.) I have to link dynamically so that all three can share the same Qt runtime, which gets all of bsnes and its modules to ~1MB (including the debugger build); and Qt itself to about ~2.5MB. However, there is some bad news. There's a serious bug in MinGW 4.4+, where it is not generating profile-guided input files (*.gcno files.) There is also a serious bug in Qt 4.5.2/Windows when using dynamic linking: the library is hanging indefinitely, forcing me to manually terminate the process upon exit. This prevents the creation of profile-guided output files (*.gcda files.) It would be tough enough to work around one, but facing both of these issues at once is too much. I'm afraid I have no choice but to disable profile-guided optimizations until these issues can be addressed. I did not know about these bugs until trying to build the official v053 release, so it's too late to revert to an all-in-one binary now. And I'm simply not willing to stop releasing new builds because of bugs in third-party software. As soon as I can work around this, I'll post a new optimized binary. In the mean time, despite the fact that this release is actually more optimized, please understand that the Windows binary will run approximately ~10% slower than previous releases. I recommend keeping v052 for now if you need the performance. Linux and OS X users are unaffected. Changelog: - save RAM is initialized to 0xff again to work around Ken Griffey Jr Baseball issue - libco adds assembly-optimized targets for Win64 and PPC-ELF [the latter courtesy of Kernigh] - libco/x86 and libco/amd64 use pre-assembled blocks now, obviates need for custom compilation flags - added a new cheat code search utility to the tools menu - separated filters from main bsnes binary to libsnesfilter / snesfilter.dll - added 2xSaI, Super 2xSaI and Super Eagle filters [kode54] - added full configuration settings for NTSC and scanline filters (12+ new options) - further optimized HQ2x filter [blargg] - added Vsync support to the Mac OS X OpenGL driver - added folder creation button to custom file load dialog - fixed a few oddities with loading of "game folders" (see older news for an explanation on what this is) - updated to blargg's file_extractor v1.0.0 - added full support for multi-file archives (eg GoodMerge sets) - split multi-cart loading again (BS-X, Sufami Turbo, etc) as required for multi-file support - cleaned up handling of file placement detection for save files (.srm, .cht, etc) - file load dialog now remembers your previous folder path across runs even without a custom games folder assigned - windows now save their exact positioning and size across runs, they no longer forcibly center - menus now have radio button and check box icons where appropriate - debugger's hex editor now has a working scrollbar widget - added resize splitter to settings and tools windows - worked around Qt style sheet bug where subclassed widgets were not properly applying style properties
|
@ -37,8 +37,8 @@ else ifeq ($(platform),osx)
|
|||
|
||||
link += $(if $(findstring audio.openal,$(ruby)),-framework OpenAL)
|
||||
else ifeq ($(platform),win)
|
||||
link += -mwindows
|
||||
# link += -mconsole
|
||||
link += -mwindows -mthreads
|
||||
# link += -mconsole -mthreads
|
||||
link += -s -luuid -lkernel32 -luser32 -lgdi32 -lshell32
|
||||
# statically link Qt for Windows build
|
||||
link += -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
||||
|
@ -76,7 +76,7 @@ link += $(call ifhas,input.rawinput,$(ruby),-ldinput8 -ldxguid)
|
|||
### objects ###
|
||||
###############
|
||||
|
||||
objects := libco ruby libfilter
|
||||
objects := libco ruby
|
||||
objects += system cartridge cheat
|
||||
objects += memory smemory cpu cpucore scpu smp smpcore ssmp sdsp ppu bppu
|
||||
objects += sgb superfx sa1
|
||||
|
@ -111,8 +111,6 @@ rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),-D$c)
|
|||
obj/ruby.o: lib/ruby/ruby.cpp $(call rwildcard,lib/ruby/*)
|
||||
$(call compile,$(rubydef) $(rubyflags))
|
||||
obj/libco.o: lib/libco/libco.c lib/libco/*
|
||||
$(c) -O3 -fomit-frame-pointer -static -Ilib -c $< -o $@
|
||||
obj/libfilter.o: lib/libfilter/libfilter.cpp lib/libfilter/*
|
||||
|
||||
#################
|
||||
### utilities ###
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
static const char bsnesVersion[] = "0.052";
|
||||
static const char bsnesVersion[] = "0.053";
|
||||
static const char bsnesTitle[] = "bsnes";
|
||||
static const unsigned bsnesSaveStateVersion = 3;
|
||||
|
||||
|
@ -23,7 +23,6 @@ static const unsigned bsnesSaveStateVersion = 3;
|
|||
#include <nall/file.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/moduloarray.hpp>
|
||||
#include <nall/new.hpp>
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/priorityqueue.hpp>
|
||||
#include <nall/property.hpp>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <../base.hpp>
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/sha256.hpp>
|
||||
|
||||
#define CARTRIDGE_CPP
|
||||
namespace SNES {
|
||||
|
@ -24,25 +25,25 @@ void Cartridge::load(Mode cartridge_mode) {
|
|||
set(mode, cartridge_mode);
|
||||
|
||||
if(cartinfo.ram_size > 0) {
|
||||
memory::cartram.map(new(zeromemory) uint8_t[cartinfo.ram_size], cartinfo.ram_size);
|
||||
memory::cartram.map(allocate<uint8_t>(cartinfo.ram_size, 0xff), cartinfo.ram_size);
|
||||
}
|
||||
|
||||
if(cartinfo.srtc || cartinfo.spc7110rtc) {
|
||||
memory::cartrtc.map(new(zeromemory) uint8_t[20], 20);
|
||||
memory::cartrtc.map(allocate<uint8_t>(20, 0xff), 20);
|
||||
}
|
||||
|
||||
if(mode() == ModeBsx) {
|
||||
memory::bsxram.map (new(zeromemory) uint8_t[ 32 * 1024], 32 * 1024);
|
||||
memory::bsxpram.map(new(zeromemory) uint8_t[512 * 1024], 512 * 1024);
|
||||
memory::bsxram.map (allocate<uint8_t>( 32 * 1024, 0xff), 32 * 1024);
|
||||
memory::bsxpram.map(allocate<uint8_t>(512 * 1024, 0xff), 512 * 1024);
|
||||
}
|
||||
|
||||
if(mode() == ModeSufamiTurbo) {
|
||||
if(memory::stArom.data()) memory::stAram.map(new(zeromemory) uint8_t[128 * 1024], 128 * 1024);
|
||||
if(memory::stBrom.data()) memory::stBram.map(new(zeromemory) uint8_t[128 * 1024], 128 * 1024);
|
||||
if(memory::stArom.data()) memory::stAram.map(allocate<uint8_t>(128 * 1024, 0xff), 128 * 1024);
|
||||
if(memory::stBrom.data()) memory::stBram.map(allocate<uint8_t>(128 * 1024, 0xff), 128 * 1024);
|
||||
}
|
||||
|
||||
if(mode() == ModeSuperGameBoy) {
|
||||
if(memory::gbrom.data()) memory::gbram.map(new(zeromemory) uint8_t[64 * 1024], 64 * 1024);
|
||||
if(memory::gbrom.data()) memory::gbram.map(allocate<uint8_t>(64 * 1024, 0xff), 64 * 1024);
|
||||
}
|
||||
|
||||
memory::cartrom.write_protect(true);
|
||||
|
@ -70,6 +71,21 @@ void Cartridge::load(Mode cartridge_mode) {
|
|||
for(unsigned n = 0; n < memory::gbrom.size(); n++) checksum = crc32_adjust(checksum, memory::gbrom[n]);
|
||||
set(crc32, ~checksum);
|
||||
|
||||
#if 0
|
||||
fprintf(stdout, "crc32 = %.8x\n", crc32());
|
||||
|
||||
sha256_ctx sha;
|
||||
uint8_t shahash[32];
|
||||
sha256_init(&sha);
|
||||
sha256_chunk(&sha, memory::cartrom.data(), memory::cartrom.size());
|
||||
sha256_final(&sha);
|
||||
sha256_hash(&sha, shahash);
|
||||
|
||||
fprintf(stdout, "sha256 = ");
|
||||
for(unsigned i = 0; i < 32; i++) fprintf(stdout, "%.2x", shahash[i]);
|
||||
fprintf(stdout, "\n");
|
||||
#endif
|
||||
|
||||
bus.load_cart();
|
||||
system.serialize_init();
|
||||
set(loaded, true);
|
||||
|
|
Before Width: | Height: | Size: 685 B |
Before Width: | Height: | Size: 632 B |
After Width: | Height: | Size: 635 B |
Before Width: | Height: | Size: 720 B |
After Width: | Height: | Size: 209 B |
After Width: | Height: | Size: 266 B |
After Width: | Height: | Size: 565 B |
After Width: | Height: | Size: 647 B |
After Width: | Height: | Size: 935 B |
Before Width: | Height: | Size: 534 B |
Before Width: | Height: | Size: 650 B |
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
libco.amd64 (2009-10-12)
|
||||
author: byuu
|
||||
license: public domain
|
||||
*/
|
||||
|
||||
#define LIBCO_C
|
||||
#include "libco.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static thread_local long long co_active_buffer[64];
|
||||
static thread_local cothread_t co_active_handle = 0;
|
||||
static void (*co_swap)(cothread_t, cothread_t) = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
//ABI: Win64
|
||||
static unsigned char co_swap_function[] = {
|
||||
0x48, 0x89, 0x22, 0x48, 0x8B, 0x21, 0x58, 0x48, 0x89, 0x6A, 0x08, 0x48, 0x89, 0x72, 0x10, 0x48,
|
||||
0x89, 0x7A, 0x18, 0x48, 0x89, 0x5A, 0x20, 0x4C, 0x89, 0x62, 0x28, 0x4C, 0x89, 0x6A, 0x30, 0x4C,
|
||||
0x89, 0x72, 0x38, 0x4C, 0x89, 0x7A, 0x40, 0x48, 0x81, 0xC2, 0x80, 0x00, 0x00, 0x00, 0x48, 0x83,
|
||||
0xE2, 0xF0, 0x0F, 0x29, 0x32, 0x0F, 0x29, 0x7A, 0x10, 0x44, 0x0F, 0x29, 0x42, 0x20, 0x44, 0x0F,
|
||||
0x29, 0x4A, 0x30, 0x44, 0x0F, 0x29, 0x52, 0x40, 0x44, 0x0F, 0x29, 0x5A, 0x50, 0x44, 0x0F, 0x29,
|
||||
0x62, 0x60, 0x44, 0x0F, 0x29, 0x6A, 0x70, 0x44, 0x0F, 0x29, 0xB2, 0x80, 0x00, 0x00, 0x00, 0x44,
|
||||
0x0F, 0x29, 0xBA, 0x90, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x69, 0x08, 0x48, 0x8B, 0x71, 0x10, 0x48,
|
||||
0x8B, 0x79, 0x18, 0x48, 0x8B, 0x59, 0x20, 0x4C, 0x8B, 0x61, 0x28, 0x4C, 0x8B, 0x69, 0x30, 0x4C,
|
||||
0x8B, 0x71, 0x38, 0x4C, 0x8B, 0x79, 0x40, 0x48, 0x81, 0xC1, 0x80, 0x00, 0x00, 0x00, 0x48, 0x83,
|
||||
0xE1, 0xF0, 0x0F, 0x29, 0x31, 0x0F, 0x29, 0x79, 0x10, 0x44, 0x0F, 0x29, 0x41, 0x20, 0x44, 0x0F,
|
||||
0x29, 0x49, 0x30, 0x44, 0x0F, 0x29, 0x51, 0x40, 0x44, 0x0F, 0x29, 0x59, 0x50, 0x44, 0x0F, 0x29,
|
||||
0x61, 0x60, 0x44, 0x0F, 0x29, 0x69, 0x70, 0x44, 0x0F, 0x29, 0xB1, 0x80, 0x00, 0x00, 0x00, 0x44,
|
||||
0x0F, 0x29, 0xB9, 0x90, 0x00, 0x00, 0x00, 0xFF, 0xE0,
|
||||
};
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
void co_init() {
|
||||
DWORD old_privileges;
|
||||
VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges);
|
||||
}
|
||||
#else
|
||||
//ABI: SystemV
|
||||
static unsigned char co_swap_function[] = {
|
||||
0x48, 0x89, 0x26, 0x48, 0x8B, 0x27, 0x58, 0x48, 0x89, 0x6E, 0x08, 0x48, 0x89, 0x5E, 0x10, 0x4C,
|
||||
0x89, 0x66, 0x18, 0x4C, 0x89, 0x6E, 0x20, 0x4C, 0x89, 0x76, 0x28, 0x4C, 0x89, 0x7E, 0x30, 0x48,
|
||||
0x8B, 0x6F, 0x08, 0x48, 0x8B, 0x5F, 0x10, 0x4C, 0x8B, 0x67, 0x18, 0x4C, 0x8B, 0x6F, 0x20, 0x4C,
|
||||
0x8B, 0x77, 0x28, 0x4C, 0x8B, 0x7F, 0x30, 0xFF, 0xE0,
|
||||
};
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
void co_init() {
|
||||
unsigned long long addr = (unsigned long long)co_swap_function;
|
||||
unsigned long long base = addr - (addr % sysconf(_SC_PAGESIZE));
|
||||
unsigned long long size = (addr - base) + sizeof co_swap_function;
|
||||
mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void crash() {
|
||||
assert(0); /* called only if cothread_t entrypoint returns */
|
||||
}
|
||||
|
||||
cothread_t co_active() {
|
||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||
return co_active_handle;
|
||||
}
|
||||
|
||||
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
||||
cothread_t handle;
|
||||
if(!co_swap) {
|
||||
co_init();
|
||||
co_swap = (void (*)(cothread_t, cothread_t))co_swap_function;
|
||||
}
|
||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||
size += 512; /* allocate additional space for storage */
|
||||
size &= ~15; /* align stack to 16-byte boundary */
|
||||
|
||||
if(handle = (cothread_t)malloc(size)) {
|
||||
long long *p = (long long*)((char*)handle + size); /* seek to top of stack */
|
||||
*--p = (long long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long long)entrypoint; /* start of function */
|
||||
*(long long*)handle = (long long)p; /* stack pointer */
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void co_delete(cothread_t handle) {
|
||||
free(handle);
|
||||
}
|
||||
|
||||
void co_switch(cothread_t handle) {
|
||||
register cothread_t co_previous_handle = co_active_handle;
|
||||
co_swap(co_active_handle = handle, co_previous_handle);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -6,15 +6,17 @@
|
|||
|
||||
#if defined(__GNUC__) && defined(__i386__)
|
||||
#include "x86.c"
|
||||
#elif defined(__GNUC__) && defined(__amd64__) && !defined(__MINGW64__)
|
||||
#include "x86-64.c"
|
||||
#elif defined(__MINGW64__)
|
||||
#include "fiber.c"
|
||||
#elif defined(__GNUC__) && defined(__amd64__)
|
||||
#include "amd64.c"
|
||||
#elif defined(__GNUC__) && defined(__powerpc__) && defined(__ELF__)
|
||||
#include "ppc-elf.c"
|
||||
#elif defined(__GNUC__)
|
||||
#include "sjlj.c"
|
||||
#elif defined(_MSC_VER) && defined(_M_IX86)
|
||||
#include "x86.c"
|
||||
#elif defined(_MSC_VER) && defined(_M_AMD64)
|
||||
#include "amd64.c"
|
||||
#elif defined(_MSC_VER)
|
||||
#include "fiber.c"
|
||||
#else
|
||||
#error "libco: unsupported processor, compiler or operating system"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
libco
|
||||
version: 0.13 rc2 (2008-01-28)
|
||||
version: 0.15 (2009-10-12)
|
||||
license: public domain
|
||||
*/
|
||||
|
||||
|
|
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* libco.ppc-elf
|
||||
* author: Kernigh
|
||||
* license: public domain
|
||||
*
|
||||
* PowerPC 32-bit ELF implementation of libco (for compile with GCC),
|
||||
* ported from PowerPC Mac OS X implementation (ppc.s) by Vas Crabb.
|
||||
* This ELF version works for OpenBSD, and might also work for FreeBSD,
|
||||
* NetBSD and Linux.
|
||||
*
|
||||
* Note 1: This implementation does not handle the AltiVec/VMX
|
||||
* registers, because the ELF ABI does not mention them,
|
||||
* and my OpenBSD system is not using them.
|
||||
*
|
||||
* Note 2: If you want position-independent code, then you must
|
||||
* define __PIC__. gcc -fpic or -fPIC defines __PIC__, but
|
||||
* gcc -fpie or -fPIE might not. If you want to use -fpie
|
||||
* or -fPIE, then you might need a manual definition:
|
||||
* gcc -fpie -D__PIC__=1
|
||||
* gcc -fPIE -D__PIC__=2
|
||||
*
|
||||
* The ELF ABI is "System V Application Binary Interface, PowerPC
|
||||
* Processor Supplement", which you can get from
|
||||
* <http://refspecs.linux-foundation.org/elf/elfspec_ppc.pdf>
|
||||
* (PDF file, hosted by Linux Foundation).
|
||||
*
|
||||
* ELF and Mac OS X use similar conventions to allocate the registers,
|
||||
* and to pass arguments and return values through registers. The main
|
||||
* differences are that ELF has a slightly different stack format, that
|
||||
* symbols are different (and without an extra underscore at the start),
|
||||
* and that the assembly syntax is different.
|
||||
*
|
||||
* A function may destroy the values of volatile registers, but must
|
||||
* preserve the values of nonvolatile registers. So the co_switch()
|
||||
* function only saves the nonvolatile registers.
|
||||
*
|
||||
* [nonvolatile registers in ELF]
|
||||
* %r1, %r14..%r31
|
||||
* %f14..%f31
|
||||
* %cr2..%cr4 in cr
|
||||
*
|
||||
* [volatile registers in ELF]
|
||||
* %r0, %r3..%r10
|
||||
* %f0..%f13
|
||||
* %cr0, %cr1, %cr5..%cr7 in cr
|
||||
* ctr, lr, xer
|
||||
*
|
||||
* lr (link register) is volatile, but it contains the return address,
|
||||
* so co_switch must save lr.
|
||||
*
|
||||
* %r13 is the small data pointer. This is constant across threads, so
|
||||
* co_switch() does not touch %r13.
|
||||
*
|
||||
* %r2 is a reserved register, so co_switch() does not touch %r2. Some
|
||||
* systems might borrow an idea from the PowerPC Embedded ABI, and might
|
||||
* use %r2 as a small read-only data pointer, which is constant across
|
||||
* threads.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void * cothread_t;
|
||||
|
||||
/*
|
||||
* co_active_context is either in a global offset table (if we are
|
||||
* compiling -fPIC or -fPIE) or has an absolute position.
|
||||
*/
|
||||
static void *co_main_stack_pointer;
|
||||
static cothread_t co_active_context = &co_main_stack_pointer;
|
||||
|
||||
extern cothread_t co_active() {
|
||||
return co_active_context;
|
||||
}
|
||||
|
||||
/*
|
||||
* Embedded assembly.
|
||||
*
|
||||
* We are not using the percent-sign substitution feature,
|
||||
* so we must write "%r1", not "%%r1".
|
||||
*
|
||||
* We always write 'bl malloc@plt', not 'bl malloc'. The '@plt'
|
||||
* is necessary in position-indepent code and seems to have no
|
||||
* significant effect in fixed-position code.
|
||||
*
|
||||
* We never use the 'lmw' or 'stmw' instructions. The ELF ABI
|
||||
* mentions that these instructions "are usually slower than
|
||||
* a sequence of other instructions that have the same effect."
|
||||
* We instead use sequences of 'lwz' or 'stz' instructions.
|
||||
*/
|
||||
__asm__("\n"
|
||||
"### embedded assembly \n"
|
||||
".section \".text\" \n"
|
||||
" .balign 4 \n"
|
||||
" \n"
|
||||
/*
|
||||
* void co_switch(co_thread to %r3)
|
||||
*
|
||||
* Allocate our stack frame of 240 bytes:
|
||||
* Old New Value
|
||||
* 4(%r1) 244(%r1) return address, used by us
|
||||
* 0(%r1) 240(%r1) frame pointer
|
||||
* 232(%r1) %f31
|
||||
* 224(%r1) %f30
|
||||
* ...
|
||||
* 96(%r1) %f14
|
||||
* 92(%r1) %r31
|
||||
* 88(%r1) %r30
|
||||
* ...
|
||||
* 24(%r1) %r14
|
||||
* 20(%r1) condition register
|
||||
* 8(%r1) padding of 12 bytes
|
||||
* 4(%r1) return address, never used
|
||||
* 0(%r1) frame pointer
|
||||
*
|
||||
* Save our registers in our stack frame.
|
||||
* Save our stack pointer in 0(%r4).
|
||||
* Switch to the stack of the other thread.
|
||||
* Restore registers and return.
|
||||
*/
|
||||
" .globl co_switch \n"
|
||||
" .type co_switch, @function \n"
|
||||
"co_switch: \n"
|
||||
" mflr %r0 # %r0 = return address \n"
|
||||
" mfcr %r9 # %r9 = condition register \n"
|
||||
" stwu %r1, -240(%r1) # allocate stack frame \n"
|
||||
" \n"
|
||||
" stw %r0, 244(%r1) # save return address \n"
|
||||
" stfd %f31, 232(%r1) # save floating-point regs \n"
|
||||
" stfd %f30, 224(%r1) \n"
|
||||
" stfd %f29, 216(%r1) \n"
|
||||
" stfd %f28, 208(%r1) \n"
|
||||
" stfd %f27, 200(%r1) \n"
|
||||
" stfd %f26, 192(%r1) \n"
|
||||
" stfd %f25, 184(%r1) \n"
|
||||
" stfd %f24, 176(%r1) \n"
|
||||
" stfd %f23, 168(%r1) \n"
|
||||
" stfd %f22, 160(%r1) \n"
|
||||
" stfd %f21, 152(%r1) \n"
|
||||
" stfd %f20, 144(%r1) \n"
|
||||
" stfd %f19, 136(%r1) \n"
|
||||
" stfd %f18, 128(%r1) \n"
|
||||
" stfd %f17, 120(%r1) \n"
|
||||
" stfd %f16, 112(%r1) \n"
|
||||
" stfd %f16, 104(%r1) \n"
|
||||
" stfd %f14, 96(%r1) \n"
|
||||
" stw %r31, 92(%r1) # save general-purpose regs \n"
|
||||
" stw %r30, 88(%r1) \n"
|
||||
" stw %r29, 84(%r1) \n"
|
||||
" stw %r28, 80(%r1) \n"
|
||||
" stw %r27, 76(%r1) \n"
|
||||
" stw %r26, 72(%r1) \n"
|
||||
" stw %r25, 68(%r1) \n"
|
||||
" stw %r24, 64(%r1) \n"
|
||||
" stw %r23, 60(%r1) \n"
|
||||
" stw %r22, 56(%r1) \n"
|
||||
" stw %r21, 52(%r1) \n"
|
||||
" stw %r20, 48(%r1) \n"
|
||||
" stw %r19, 44(%r1) \n"
|
||||
" stw %r18, 40(%r1) \n"
|
||||
" stw %r17, 36(%r1) \n"
|
||||
" stw %r16, 32(%r1) \n"
|
||||
" stw %r15, 28(%r1) \n"
|
||||
" stw %r14, 24(%r1) \n"
|
||||
" stw %r9, 20(%r1) # save condition reg \n"
|
||||
" \n"
|
||||
" # save current context, set new context \n"
|
||||
" # %r4 = co_active_context \n"
|
||||
" # co_active_context = %r3 \n"
|
||||
#if __PIC__ == 2
|
||||
" # position-independent code, large model (-fPIC) \n"
|
||||
" bl _GLOBAL_OFFSET_TABLE_@local-4 \n"
|
||||
" mflr %r8 # %r8 = address of got \n"
|
||||
" addis %r7, %r8, co_active_context@got@ha \n"
|
||||
" lwz %r6, co_active_context@got@l(%r7) \n"
|
||||
" lwz %r4, 0(%r6) \n"
|
||||
" stw %r3, 0(%r6) \n"
|
||||
#elif __PIC__ == 1
|
||||
" # position-independent code, small model (-fpic) \n"
|
||||
" bl _GLOBAL_OFFSET_TABLE_@local-4 \n"
|
||||
" mflr %r8 # %r8 = address of got \n"
|
||||
" lwz %r7, co_active_context@got(%r8) \n"
|
||||
" lwz %r4, 0(%r7) \n"
|
||||
" stw %r3, 0(%r7) \n"
|
||||
#else
|
||||
" # fixed-position code \n"
|
||||
" lis %r8, co_active_context@ha \n"
|
||||
" lwz %r4, co_active_context@l(%r8) \n"
|
||||
" stw %r3, co_active_context@l(%r8) \n"
|
||||
#endif
|
||||
" \n"
|
||||
" # save current stack pointer \n"
|
||||
" stw %r1, 0(%r4) \n"
|
||||
" # get new stack pointer \n"
|
||||
" lwz %r1, 0(%r3) \n"
|
||||
" \n"
|
||||
" lwz %r0, 244(%r1) # get return address \n"
|
||||
" lfd %f31, 232(%r1) # restore floating-point regs \n"
|
||||
" lfd %f30, 224(%r1) \n"
|
||||
" lfd %f29, 216(%r1) \n"
|
||||
" lfd %f28, 208(%r1) \n"
|
||||
" lfd %f27, 200(%r1) \n"
|
||||
" lfd %f26, 192(%r1) \n"
|
||||
" lfd %f25, 184(%r1) \n"
|
||||
" lfd %f24, 176(%r1) \n"
|
||||
" lfd %f23, 168(%r1) \n"
|
||||
" lfd %f22, 160(%r1) \n"
|
||||
" lfd %f21, 152(%r1) \n"
|
||||
" lfd %f20, 144(%r1) \n"
|
||||
" lfd %f19, 136(%r1) \n"
|
||||
" lfd %f18, 128(%r1) \n"
|
||||
" lfd %f17, 120(%r1) \n"
|
||||
" lfd %f16, 112(%r1) \n"
|
||||
" lfd %f16, 104(%r1) \n"
|
||||
" lfd %f14, 96(%r1) \n"
|
||||
" lwz %r31, 92(%r1) # restore general-purpose regs \n"
|
||||
" lwz %r30, 88(%r1) \n"
|
||||
" lwz %r29, 84(%r1) \n"
|
||||
" lwz %r28, 80(%r1) \n"
|
||||
" lwz %r27, 76(%r1) \n"
|
||||
" lwz %r26, 72(%r1) \n"
|
||||
" lwz %r25, 68(%r1) \n"
|
||||
" lwz %r24, 64(%r1) \n"
|
||||
" lwz %r23, 60(%r1) \n"
|
||||
" lwz %r22, 56(%r1) \n"
|
||||
" lwz %r21, 52(%r1) \n"
|
||||
" lwz %r20, 48(%r1) \n"
|
||||
" lwz %r19, 44(%r1) \n"
|
||||
" lwz %r18, 40(%r1) \n"
|
||||
" lwz %r17, 36(%r1) \n"
|
||||
" lwz %r16, 32(%r1) \n"
|
||||
" lwz %r15, 28(%r1) \n"
|
||||
" lwz %r14, 24(%r1) \n"
|
||||
" lwz %r9, 20(%r1) # get condition reg \n"
|
||||
" \n"
|
||||
" addi %r1, %r1, 240 # free stack frame \n"
|
||||
" mtlr %r0 # restore return address \n"
|
||||
" mtcr %r9 # restore condition register \n"
|
||||
" blr # return \n"
|
||||
" .size co_switch, . - co_switch \n"
|
||||
" \n"
|
||||
/*
|
||||
* cothread_t %r3 co_create(unsigned int stack_size %r3,
|
||||
* void (*coentry %r4)())
|
||||
*
|
||||
* Allocate a new stack, such that when you co_switch to that
|
||||
* stack, then co_switch returns to coentry.
|
||||
*/
|
||||
" .globl co_create \n"
|
||||
" .type co_create, @function \n"
|
||||
"co_create: \n"
|
||||
" mflr %r0 # %r0 = return address \n"
|
||||
" stwu %r1, -16(%r1) # allocate my stack frame \n"
|
||||
" stw %r0, 20(%r1) # save return address \n"
|
||||
" stw %r31, 12(%r1) # save %r31 \n"
|
||||
" stw %r30, 8(%r1) # save %r30 \n"
|
||||
" \n"
|
||||
" mr %r30, %r3 # %r30 = stack_size \n"
|
||||
" mr %r31, %r4 # %r31 = coentry \n"
|
||||
" \n"
|
||||
" # Call malloc(stack_size %r3) to allocate stack; \n"
|
||||
" # malloc() probably uses good alignment. \n"
|
||||
" # \n"
|
||||
" bl malloc@plt # returns %r3 = low end \n"
|
||||
" cmpwi %r3, 0 # if returned NULL, \n"
|
||||
" beq- 1f # then abort \n"
|
||||
" \n"
|
||||
" # we return %r3 = low end of stack \n"
|
||||
" add %r4, %r3, %r30 # %r4 = high end of stack \n"
|
||||
" \n"
|
||||
" # uncomment if malloc() uses wrong alignment \n"
|
||||
" #rlwinm %r4,%r4,0,0,27 # force 16-byte alignment \n"
|
||||
" \n"
|
||||
/*
|
||||
* Allocate two stack frames:
|
||||
* 16 bytes for stack frame with return address
|
||||
* 240 bytes for co_switch stack frame
|
||||
*
|
||||
* Old New Value
|
||||
* -8(%r4) 248(%r5) padding of 8 bytes
|
||||
* -12(%r4) 244(%r5) return address = coentry
|
||||
* -16(%r4) 240(%r5) frame pointer = NULL
|
||||
* 232(%r5) %f31 = 0
|
||||
* ...
|
||||
* 20(%r5) condition register = 0
|
||||
* 0(%r5) frame pointer
|
||||
*/
|
||||
" li %r9, (240-20)/4+1 \n"
|
||||
" addi %r5, %r4, -16 # allocate first stack frame \n"
|
||||
" li %r0, 0 \n"
|
||||
" stwu %r5, -240(%r5) # allocate second stack frame \n"
|
||||
" li %r8, 20 \n"
|
||||
" mtctr %r9 # loop %r9 times \n"
|
||||
"2: # loop to store zero to 20(%r5) through 240(%r5) \n"
|
||||
" stwx %r0, %r5, %r8 \n"
|
||||
" addi %r8, %r8, 4 # index += 4 \n"
|
||||
" bdnz+ 2b # ctr -= 1, branch if nonzero \n"
|
||||
" \n"
|
||||
" stw %r31, 244(%r5) # return address = coentry \n"
|
||||
" stw %r5, 0(%r3) # save stack pointer \n"
|
||||
" \n"
|
||||
" lwz %r0, 20(%r1) # get return address \n"
|
||||
" lwz %r31, 12(%r1) # restore %r31 \n"
|
||||
" lwz %r30, 8(%r1) # restore %r30 \n"
|
||||
" mtlr %r0 # restore return address \n"
|
||||
" addi %r1, %r1, 16 # free stack frame \n"
|
||||
" blr # return \n"
|
||||
" \n"
|
||||
"1: b abort@plt # branch 1f to abort \n"
|
||||
" .size co_create, . - co_create \n"
|
||||
" \n"
|
||||
/*
|
||||
* void co_delete(cothread_t) => void free(void *)
|
||||
*/
|
||||
" .globl co_delete \n"
|
||||
" .type co_delete, @function \n"
|
||||
"co_delete: \n"
|
||||
" b free@plt \n"
|
||||
" \n"
|
||||
);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
libco.x86-64 (2008-01-28)
|
||||
author: byuu
|
||||
license: public domain
|
||||
*/
|
||||
|
||||
#define LIBCO_C
|
||||
#include "libco.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static thread_local long co_active_buffer[32];
|
||||
static thread_local cothread_t co_active_ = 0;
|
||||
|
||||
static void crash() {
|
||||
assert(0); /* called only if cothread_t entrypoint returns */
|
||||
}
|
||||
|
||||
cothread_t co_active() {
|
||||
if(!co_active_) co_active_ = &co_active_buffer;
|
||||
return co_active_;
|
||||
}
|
||||
|
||||
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
||||
cothread_t handle;
|
||||
assert(sizeof(long) == 8);
|
||||
if(!co_active_) co_active_ = &co_active_buffer;
|
||||
size += 128; /* allocate additional space for storage */
|
||||
size &= ~15; /* align stack to 16-byte boundary */
|
||||
|
||||
if(handle = (cothread_t)calloc(size, 1)) {
|
||||
long *p = (long*)((char*)handle + size); /* seek to top of stack */
|
||||
*--p = (long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long)entrypoint; /* start of function */
|
||||
*(long*)handle = (long)p; /* stack pointer */
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void co_delete(cothread_t handle) {
|
||||
free(handle);
|
||||
}
|
||||
|
||||
void co_switch(cothread_t to) {
|
||||
register long stack = *(long*)to; /* stack[0] = "to" thread entry point */
|
||||
register cothread_t from = co_active_;
|
||||
co_active_ = to;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"movq %%rsp,(%1) \n\t" /* save old stack pointer */
|
||||
"movq (%0),%%rsp \n\t" /* load new stack pointer */
|
||||
"addq $8,%%rsp \n\t" /* "pop" return address off stack */
|
||||
|
||||
"movq %%rbp, 8(%1) \n\t" /* backup non-volatile registers */
|
||||
"movq %%rbx,16(%1) \n\t"
|
||||
"movq %%r12,24(%1) \n\t"
|
||||
"movq %%r13,32(%1) \n\t"
|
||||
"movq %%r14,40(%1) \n\t"
|
||||
"movq %%r15,48(%1) \n\t"
|
||||
|
||||
"movq 8(%0),%%rbp \n\t" /* restore non-volatile registers */
|
||||
"movq 16(%0),%%rbx \n\t"
|
||||
"movq 24(%0),%%r12 \n\t"
|
||||
"movq 32(%0),%%r13 \n\t"
|
||||
"movq 40(%0),%%r14 \n\t"
|
||||
"movq 48(%0),%%r15 \n\t"
|
||||
|
||||
"jmp *(%2) \n\t" /* jump into "to" thread */
|
||||
: /* no outputs */
|
||||
: "r" (to), "r" (from), "r" (stack)
|
||||
);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
libco.x86 (2008-01-28)
|
||||
libco.x86 (2009-10-12)
|
||||
author: byuu
|
||||
license: public domain
|
||||
*/
|
||||
|
@ -13,26 +13,63 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
static thread_local long co_active_buffer[32];
|
||||
static thread_local cothread_t co_active_ = 0;
|
||||
#if defined(_MSC_VER)
|
||||
#define fastcall __fastcall
|
||||
#elif defined(__GNUC__)
|
||||
#define fastcall __attribute__((fastcall))
|
||||
#else
|
||||
#error "libco: please define fastcall macro"
|
||||
#endif
|
||||
|
||||
static thread_local long co_active_buffer[64];
|
||||
static thread_local cothread_t co_active_handle = 0;
|
||||
static void (fastcall *co_swap)(cothread_t, cothread_t) = 0;
|
||||
|
||||
//ABI: fastcall
|
||||
static unsigned char co_swap_function[] = {
|
||||
0x89, 0x22, 0x8B, 0x21, 0x58, 0x89, 0x6A, 0x04, 0x89, 0x72, 0x08, 0x89, 0x7A, 0x0C, 0x89, 0x5A,
|
||||
0x10, 0x8B, 0x69, 0x04, 0x8B, 0x71, 0x08, 0x8B, 0x79, 0x0C, 0x8B, 0x59, 0x10, 0xFF, 0xE0,
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
void co_init() {
|
||||
DWORD old_privileges;
|
||||
VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges);
|
||||
}
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
void co_init() {
|
||||
unsigned long addr = (unsigned long)co_swap_function;
|
||||
unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE));
|
||||
unsigned long size = (addr - base) + sizeof co_swap_function;
|
||||
mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void crash() {
|
||||
assert(0); /* called only if cothread_t entrypoint returns */
|
||||
}
|
||||
|
||||
cothread_t co_active() {
|
||||
if(!co_active_) co_active_ = &co_active_buffer;
|
||||
return co_active_;
|
||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||
return co_active_handle;
|
||||
}
|
||||
|
||||
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
||||
cothread_t handle;
|
||||
assert(sizeof(long) == 4);
|
||||
if(!co_active_) co_active_ = &co_active_buffer;
|
||||
size += 128; /* allocate additional space for storage */
|
||||
if(!co_swap) {
|
||||
co_init();
|
||||
co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function;
|
||||
}
|
||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||
size += 256; /* allocate additional space for storage */
|
||||
size &= ~15; /* align stack to 16-byte boundary */
|
||||
|
||||
if(handle = (cothread_t)calloc(size, 1)) {
|
||||
if(handle = (cothread_t)malloc(size)) {
|
||||
long *p = (long*)((char*)handle + size); /* seek to top of stack */
|
||||
*--p = (long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long)entrypoint; /* start of function */
|
||||
|
@ -46,65 +83,11 @@ void co_delete(cothread_t handle) {
|
|||
free(handle);
|
||||
}
|
||||
|
||||
#if defined(__GNUC__)
|
||||
|
||||
void co_switch(cothread_t to) {
|
||||
register long stack = *(long*)to; /* stack[0] = "to" thread entry point */
|
||||
register cothread_t from = co_active_;
|
||||
co_active_ = to;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"movl %%esp,(%1) \n\t" /* save old stack pointer */
|
||||
"movl (%0),%%esp \n\t" /* load new stack pointer */
|
||||
"addl $4,%%esp \n\t" /* "pop" return address off stack */
|
||||
|
||||
"movl %%ebp, 4(%1) \n\t" /* backup non-volatile registers */
|
||||
"movl %%esi, 8(%1) \n\t"
|
||||
"movl %%edi,12(%1) \n\t"
|
||||
"movl %%ebx,16(%1) \n\t"
|
||||
|
||||
"movl 4(%0),%%ebp \n\t" /* restore non-volatile registers */
|
||||
"movl 8(%0),%%esi \n\t"
|
||||
"movl 12(%0),%%edi \n\t"
|
||||
"movl 16(%0),%%ebx \n\t"
|
||||
|
||||
"jmp *(%2) \n\t" /* jump into "to" thread */
|
||||
: /* no outputs */
|
||||
: "r" (to), "r" (from), "r" (stack)
|
||||
);
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
__declspec(naked) __declspec(noinline)
|
||||
static void __fastcall co_swap(register cothread_t to, register cothread_t from) {
|
||||
/* ecx = to, edx = from */
|
||||
__asm {
|
||||
mov [edx],esp
|
||||
mov esp,[ecx]
|
||||
pop eax
|
||||
|
||||
mov [edx+ 4],ebp
|
||||
mov [edx+ 8],esi
|
||||
mov [edx+12],edi
|
||||
mov [edx+16],ebx
|
||||
|
||||
mov ebp,[ecx+ 4]
|
||||
mov esi,[ecx+ 8]
|
||||
mov edi,[ecx+12]
|
||||
mov ebx,[ecx+16]
|
||||
|
||||
jmp eax
|
||||
}
|
||||
}
|
||||
|
||||
void co_switch(cothread_t handle) {
|
||||
register cothread_t co_prev_ = co_active_;
|
||||
co_swap(co_active_ = handle, co_prev_);
|
||||
register cothread_t co_previous_handle = co_active_handle;
|
||||
co_swap(co_active_handle = handle, co_previous_handle);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
class Colortable {
|
||||
public:
|
||||
enum Format {
|
||||
RGB555,
|
||||
RGB565,
|
||||
RGB888,
|
||||
};
|
||||
|
||||
const inline uint32_t operator[](uint16_t index) const { return table[index]; }
|
||||
|
||||
void set_format(Format);
|
||||
void set_contrast(signed);
|
||||
void set_brightness(signed);
|
||||
void set_gamma(signed);
|
||||
void enable_gamma_ramp(bool);
|
||||
void enable_sepia(bool);
|
||||
void enable_grayscale(bool);
|
||||
void enable_invert(bool);
|
||||
|
||||
void update();
|
||||
|
||||
Colortable();
|
||||
~Colortable();
|
||||
|
||||
private:
|
||||
uint32_t *table;
|
||||
Format format;
|
||||
|
||||
signed contrast;
|
||||
signed brightness;
|
||||
signed gamma;
|
||||
|
||||
bool gamma_ramp;
|
||||
bool sepia;
|
||||
bool grayscale;
|
||||
bool invert;
|
||||
|
||||
static const uint8_t gamma_ramp_table[32];
|
||||
uint8_t contrast_adjust(uint8_t input);
|
||||
uint8_t brightness_adjust(uint8_t input);
|
||||
uint8_t gamma_adjust(uint8_t input);
|
||||
};
|
||||
|
||||
extern Colortable colortable;
|
|
@ -1,32 +0,0 @@
|
|||
DirectFilter filter_direct;
|
||||
|
||||
void DirectFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) {
|
||||
outwidth = width;
|
||||
outheight = height;
|
||||
}
|
||||
|
||||
void DirectFilter::render(
|
||||
uint32_t *output, unsigned outpitch, uint16_t *input, unsigned pitch,
|
||||
unsigned *line, unsigned width, unsigned height
|
||||
) {
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
if(width == 512 && line[y] == 256) {
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
uint16_t p = *input++;
|
||||
*output++ = colortable[p];
|
||||
*output++ = colortable[p];
|
||||
}
|
||||
input += 256;
|
||||
} else {
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint16_t p = *input++;
|
||||
*output++ = colortable[p];
|
||||
}
|
||||
}
|
||||
input += pitch - width;
|
||||
output += outpitch - width;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
class DirectFilter : public Filter {
|
||||
public:
|
||||
void size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height);
|
||||
void render(uint32_t*, unsigned, uint16_t*, unsigned, unsigned*, unsigned, unsigned);
|
||||
};
|
||||
|
||||
extern DirectFilter filter_direct;
|
|
@ -1,32 +0,0 @@
|
|||
FilterInterface filter;
|
||||
|
||||
void FilterInterface::set(FilterInterface::FilterType type) {
|
||||
active_filter = type;
|
||||
}
|
||||
|
||||
void FilterInterface::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) {
|
||||
switch(active_filter) { default:
|
||||
case Direct: return filter_direct.size(outwidth, outheight, width, height);
|
||||
case Scanline: return filter_scanline.size(outwidth, outheight, width, height);
|
||||
case Scale2x: return filter_scale2x.size(outwidth, outheight, width, height);
|
||||
case LQ2x: return filter_lq2x.size(outwidth, outheight, width, height);
|
||||
case HQ2x: return filter_hq2x.size(outwidth, outheight, width, height);
|
||||
case NTSC: return filter_ntsc.size(outwidth, outheight, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void FilterInterface::render(
|
||||
uint32_t *output, unsigned outpitch, uint16_t *input, unsigned pitch,
|
||||
unsigned *line, unsigned width, unsigned height
|
||||
) {
|
||||
switch(active_filter) { default:
|
||||
case Direct: return filter_direct.render(output, outpitch, input, pitch, line, width, height);
|
||||
case Scanline: return filter_scanline.render(output, outpitch, input, pitch, line, width, height);
|
||||
case Scale2x: return filter_scale2x.render(output, outpitch, input, pitch, line, width, height);
|
||||
case LQ2x: return filter_lq2x.render(output, outpitch, input, pitch, line, width, height);
|
||||
case HQ2x: return filter_hq2x.render(output, outpitch, input, pitch, line, width, height);
|
||||
case NTSC: return filter_ntsc.render(output, outpitch, input, pitch, line, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
FilterInterface::FilterInterface() : active_filter(FilterInterface::Direct) {}
|
|
@ -1,37 +0,0 @@
|
|||
class Filter {
|
||||
public:
|
||||
virtual void size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) = 0;
|
||||
|
||||
virtual void render(
|
||||
uint32_t *output, unsigned outpitch, uint16_t *input, unsigned pitch,
|
||||
unsigned *line, unsigned width, unsigned height
|
||||
) = 0;
|
||||
};
|
||||
|
||||
class FilterInterface : public Filter {
|
||||
public:
|
||||
enum FilterType {
|
||||
Direct,
|
||||
Scanline,
|
||||
Scale2x,
|
||||
LQ2x,
|
||||
HQ2x,
|
||||
NTSC,
|
||||
};
|
||||
|
||||
void set(FilterType type);
|
||||
|
||||
void size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height);
|
||||
|
||||
void render(
|
||||
uint32_t *output, unsigned outpitch, uint16_t *input, unsigned pitch,
|
||||
unsigned *line, unsigned width, unsigned height
|
||||
);
|
||||
|
||||
FilterInterface();
|
||||
|
||||
private:
|
||||
FilterType active_filter;
|
||||
};
|
||||
|
||||
extern FilterInterface filter;
|
|
@ -1,204 +0,0 @@
|
|||
//HQ2x filter
|
||||
//authors: byuu and blargg
|
||||
//license: public domain
|
||||
//
|
||||
//note: this is a clean reimplementation of the original HQ2x filter, which was
|
||||
//written by Maxim Stepin (MaxSt). it is not 100% identical, but very similar.
|
||||
|
||||
HQ2xFilter filter_hq2x;
|
||||
|
||||
const uint8_t HQ2xFilter::hqTable[256] = {
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
|
||||
4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 12, 12, 5, 3, 1, 12,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
|
||||
4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 16, 12, 5, 3, 1, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 12, 12, 5, 19, 16, 12,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 1, 12, 5, 19, 1, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 18, 5, 3, 16, 12, 5, 19, 1, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 13, 5, 3, 1, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 13,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 12,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 1, 12, 5, 3, 1, 14,
|
||||
};
|
||||
|
||||
bool HQ2xFilter::same(uint16_t x, uint16_t y) {
|
||||
return !((yuvTable[x] - yuvTable[y] + diff_offset) & diff_mask);
|
||||
}
|
||||
|
||||
bool HQ2xFilter::diff(uint32_t x, uint16_t y) {
|
||||
return ((x - yuvTable[y]) & diff_mask);
|
||||
}
|
||||
|
||||
void HQ2xFilter::grow(uint32_t &n) { n |= n << 16; n &= 0x03e07c1f; }
|
||||
uint16_t HQ2xFilter::pack(uint32_t n) { n &= 0x03e07c1f; return n | (n >> 16); }
|
||||
|
||||
uint16_t HQ2xFilter::blend1(uint32_t A, uint32_t B) {
|
||||
grow(A); grow(B);
|
||||
A = (A * 3 + B) >> 2;
|
||||
return pack(A);
|
||||
}
|
||||
|
||||
uint16_t HQ2xFilter::blend2(uint32_t A, uint32_t B, uint32_t C) {
|
||||
grow(A); grow(B); grow(C);
|
||||
return pack((A * 2 + B + C) >> 2);
|
||||
}
|
||||
|
||||
uint16_t HQ2xFilter::blend3(uint32_t A, uint32_t B, uint32_t C) {
|
||||
grow(A); grow(B); grow(C);
|
||||
return pack((A * 5 + B * 2 + C) >> 3);
|
||||
}
|
||||
|
||||
uint16_t HQ2xFilter::blend4(uint32_t A, uint32_t B, uint32_t C) {
|
||||
grow(A); grow(B); grow(C);
|
||||
return pack((A * 6 + B + C) >> 3);
|
||||
}
|
||||
|
||||
uint16_t HQ2xFilter::blend5(uint32_t A, uint32_t B, uint32_t C) {
|
||||
grow(A); grow(B); grow(C);
|
||||
return pack((A * 2 + (B + C) * 3) >> 3);
|
||||
}
|
||||
|
||||
uint16_t HQ2xFilter::blend6(uint32_t A, uint32_t B, uint32_t C) {
|
||||
grow(A); grow(B); grow(C);
|
||||
return pack((A * 14 + B + C) >> 4);
|
||||
}
|
||||
|
||||
alwaysinline uint16_t HQ2xFilter::blend(unsigned rule, uint16_t E, uint16_t A, uint16_t B, uint16_t D, uint16_t F, uint16_t H) {
|
||||
switch(rule) { default:
|
||||
case 0: return E;
|
||||
case 1: return blend1(E, A);
|
||||
case 2: return blend1(E, D);
|
||||
case 3: return blend1(E, B);
|
||||
case 4: return blend2(E, D, B);
|
||||
case 5: return blend2(E, A, B);
|
||||
case 6: return blend2(E, A, D);
|
||||
case 7: return blend3(E, B, D);
|
||||
case 8: return blend3(E, D, B);
|
||||
case 9: return blend4(E, D, B);
|
||||
case 10: return blend5(E, D, B);
|
||||
case 11: return blend6(E, D, B);
|
||||
case 12: return same(B, D) ? blend2(E, D, B) : E;
|
||||
case 13: return same(B, D) ? blend5(E, D, B) : E;
|
||||
case 14: return same(B, D) ? blend6(E, D, B) : E;
|
||||
case 15: return same(B, D) ? blend2(E, D, B) : blend1(E, A);
|
||||
case 16: return same(B, D) ? blend4(E, D, B) : blend1(E, A);
|
||||
case 17: return same(B, D) ? blend5(E, D, B) : blend1(E, A);
|
||||
case 18: return same(B, F) ? blend3(E, B, D) : blend1(E, D);
|
||||
case 19: return same(D, H) ? blend3(E, D, B) : blend1(E, B);
|
||||
}
|
||||
}
|
||||
|
||||
void HQ2xFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) {
|
||||
outwidth = width;
|
||||
outheight = height;
|
||||
|
||||
if(width <= 256 && height <= 240) {
|
||||
outwidth *= 2;
|
||||
outheight *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void HQ2xFilter::render(
|
||||
uint32_t *output, unsigned outpitch, uint16_t *input, unsigned pitch,
|
||||
unsigned *line, unsigned width, unsigned height
|
||||
) {
|
||||
if(width > 256 || height > 240) {
|
||||
filter_direct.render(output, outpitch, input, pitch, line, width, height);
|
||||
return;
|
||||
}
|
||||
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
uint32_t *out0 = output;
|
||||
uint32_t *out1 = output + outpitch;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
int prevline = (y == 0) ? 0 : pitch;
|
||||
int nextline = (y == height - 1) ? 0 : pitch;
|
||||
|
||||
input++;
|
||||
*out0++ = 0; *out0++ = 0;
|
||||
*out1++ = 0; *out1++ = 0;
|
||||
|
||||
for(unsigned x = 1; x < 256 - 1; x++) {
|
||||
uint16_t A = *(input - prevline - 1);
|
||||
uint16_t B = *(input - prevline + 0);
|
||||
uint16_t C = *(input - prevline + 1);
|
||||
uint16_t D = *(input - 1);
|
||||
uint16_t E = *(input + 0);
|
||||
uint16_t F = *(input + 1);
|
||||
uint16_t G = *(input + nextline - 1);
|
||||
uint16_t H = *(input + nextline + 0);
|
||||
uint16_t I = *(input + nextline + 1);
|
||||
uint32_t e = yuvTable[E] + diff_offset;
|
||||
|
||||
uint8_t pattern;
|
||||
pattern = diff(e, A) << 0;
|
||||
pattern |= diff(e, B) << 1;
|
||||
pattern |= diff(e, C) << 2;
|
||||
pattern |= diff(e, D) << 3;
|
||||
pattern |= diff(e, F) << 4;
|
||||
pattern |= diff(e, G) << 5;
|
||||
pattern |= diff(e, H) << 6;
|
||||
pattern |= diff(e, I) << 7;
|
||||
|
||||
*(out0 + 0) = colortable[blend(hqTable[pattern], E, A, B, D, F, H)]; pattern = rotate[pattern];
|
||||
*(out0 + 1) = colortable[blend(hqTable[pattern], E, C, F, B, H, D)]; pattern = rotate[pattern];
|
||||
*(out1 + 1) = colortable[blend(hqTable[pattern], E, I, H, F, D, B)]; pattern = rotate[pattern];
|
||||
*(out1 + 0) = colortable[blend(hqTable[pattern], E, G, D, H, B, F)];
|
||||
|
||||
input++;
|
||||
out0 += 2;
|
||||
out1 += 2;
|
||||
}
|
||||
|
||||
input++;
|
||||
*out0++ = 0; *out0++ = 0;
|
||||
*out1++ = 0; *out1++ = 0;
|
||||
|
||||
input += pitch - 256;
|
||||
out0 += outpitch + outpitch - 512;
|
||||
out1 += outpitch + outpitch - 512;
|
||||
}
|
||||
}
|
||||
|
||||
HQ2xFilter::HQ2xFilter() {
|
||||
yuvTable = new uint32_t[32768];
|
||||
|
||||
for(unsigned i = 0; i < 32768; i++) {
|
||||
uint8_t R = (i >> 0) & 31;
|
||||
uint8_t G = (i >> 5) & 31;
|
||||
uint8_t B = (i >> 10) & 31;
|
||||
|
||||
//bgr555->bgr888
|
||||
double r = (R << 3) | (R >> 2);
|
||||
double g = (G << 3) | (G >> 2);
|
||||
double b = (B << 3) | (B >> 2);
|
||||
|
||||
//bgr888->yuv888
|
||||
double y = (r + g + b) * (0.25f * (63.5f / 48.0f));
|
||||
double u = ((r - b) * 0.25f + 128.0f) * (7.5f / 7.0f);
|
||||
double v = ((g * 2.0f - r - b) * 0.125f + 128.0f) * (7.5f / 6.0f);
|
||||
|
||||
yuvTable[i] = ((unsigned)y << 21) + ((unsigned)u << 11) + ((unsigned)v);
|
||||
}
|
||||
|
||||
diff_offset = (0x440 << 21) + (0x207 << 11) + 0x407;
|
||||
diff_mask = (0x380 << 21) + (0x1f0 << 11) + 0x3f0;
|
||||
|
||||
for(unsigned n = 0; n < 256; n++) {
|
||||
rotate[n] = ((n >> 2) & 0x11) | ((n << 2) & 0x88)
|
||||
| ((n & 0x01) << 5) | ((n & 0x08) << 3)
|
||||
| ((n & 0x10) >> 3) | ((n & 0x80) >> 5);
|
||||
}
|
||||
}
|
||||
|
||||
HQ2xFilter::~HQ2xFilter() {
|
||||
delete[] yuvTable;
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
class HQ2xFilter : public Filter {
|
||||
public:
|
||||
void size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height);
|
||||
void render(uint32_t*, unsigned, uint16_t*, unsigned, unsigned*, unsigned, unsigned);
|
||||
|
||||
HQ2xFilter();
|
||||
~HQ2xFilter();
|
||||
|
||||
private:
|
||||
static const uint8_t hqTable[256];
|
||||
uint32_t *yuvTable;
|
||||
uint32_t diff_offset;
|
||||
uint32_t diff_mask;
|
||||
uint8_t rotate[256];
|
||||
|
||||
bool same(uint16_t, uint16_t);
|
||||
bool diff(uint32_t, uint16_t);
|
||||
|
||||
void grow(uint32_t&);
|
||||
uint16_t pack(uint32_t);
|
||||
|
||||
uint16_t blend1(uint32_t, uint32_t);
|
||||
uint16_t blend2(uint32_t, uint32_t, uint32_t);
|
||||
uint16_t blend3(uint32_t, uint32_t, uint32_t);
|
||||
uint16_t blend4(uint32_t, uint32_t, uint32_t);
|
||||
uint16_t blend5(uint32_t, uint32_t, uint32_t);
|
||||
uint16_t blend6(uint32_t, uint32_t, uint32_t);
|
||||
uint16_t blend (unsigned, uint16_t, uint16_t, uint16_t, uint16_t, uint16_t, uint16_t);
|
||||
};
|
||||
|
||||
extern HQ2xFilter filter_hq2x;
|
|
@ -1,15 +0,0 @@
|
|||
#include "libfilter.hpp"
|
||||
using nall::min;
|
||||
using nall::max;
|
||||
|
||||
namespace libfilter {
|
||||
#include "colortable.cpp"
|
||||
#include "filter.cpp"
|
||||
|
||||
#include "direct.cpp"
|
||||
#include "scanline.cpp"
|
||||
#include "scale2x.cpp"
|
||||
#include "lq2x.cpp"
|
||||
#include "hq2x.cpp"
|
||||
#include "ntsc.cpp"
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
#ifndef LIBFILTER_H
|
||||
#define LIBFILTER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace libfilter {
|
||||
#include "colortable.hpp"
|
||||
#include "filter.hpp"
|
||||
|
||||
#include "direct.hpp"
|
||||
#include "scanline.hpp"
|
||||
#include "scale2x.hpp"
|
||||
#include "lq2x.hpp"
|
||||
#include "hq2x.hpp"
|
||||
#include "ntsc.hpp"
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,57 +0,0 @@
|
|||
LQ2xFilter filter_lq2x;
|
||||
|
||||
void LQ2xFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) {
|
||||
outwidth = width;
|
||||
outheight = height;
|
||||
|
||||
if(width <= 256 && height <= 240) {
|
||||
outwidth *= 2;
|
||||
outheight *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void LQ2xFilter::render(
|
||||
uint32_t *output, unsigned outpitch, uint16_t *input, unsigned pitch,
|
||||
unsigned *line, unsigned width, unsigned height
|
||||
) {
|
||||
if(width > 256 || height > 240) {
|
||||
filter_direct.render(output, outpitch, input, pitch, line, width, height);
|
||||
return;
|
||||
}
|
||||
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
uint32_t *out0 = output;
|
||||
uint32_t *out1 = output + outpitch;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
int prevline = (y == 0) ? 0 : pitch;
|
||||
int nextline = (y == height - 1) ? 0 : pitch;
|
||||
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
uint16_t A = *(input - prevline);
|
||||
uint16_t B = (x > 0) ? *(input - 1) : *input;
|
||||
uint16_t C = *input;
|
||||
uint16_t D = (x < 255) ? *(input + 1) : *input;
|
||||
uint16_t E = *(input++ + nextline);
|
||||
uint32_t c = colortable[C];
|
||||
|
||||
if(A != E && B != D) {
|
||||
*out0++ = (A == B ? colortable[C + A - ((C ^ A) & 0x0421) >> 1] : c);
|
||||
*out0++ = (A == D ? colortable[C + A - ((C ^ A) & 0x0421) >> 1] : c);
|
||||
*out1++ = (E == B ? colortable[C + E - ((C ^ E) & 0x0421) >> 1] : c);
|
||||
*out1++ = (E == D ? colortable[C + E - ((C ^ E) & 0x0421) >> 1] : c);
|
||||
} else {
|
||||
*out0++ = c;
|
||||
*out0++ = c;
|
||||
*out1++ = c;
|
||||
*out1++ = c;
|
||||
}
|
||||
}
|
||||
|
||||
input += pitch - 256;
|
||||
out0 += outpitch + outpitch - 512;
|
||||
out1 += outpitch + outpitch - 512;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
class LQ2xFilter : public Filter {
|
||||
public:
|
||||
void size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height);
|
||||
void render(uint32_t*, unsigned, uint16_t*, unsigned, unsigned*, unsigned, unsigned);
|
||||
};
|
||||
|
||||
extern LQ2xFilter filter_lq2x;
|
|
@ -1,79 +0,0 @@
|
|||
#include "snes_ntsc/snes_ntsc.c"
|
||||
|
||||
NTSCFilter filter_ntsc;
|
||||
|
||||
void NTSCFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) {
|
||||
outwidth = SNES_NTSC_OUT_WIDTH(256);
|
||||
outheight = height;
|
||||
}
|
||||
|
||||
void NTSCFilter::render(
|
||||
uint32_t *output, unsigned outpitch, uint16_t *input, unsigned pitch,
|
||||
unsigned *line, unsigned width, unsigned height
|
||||
) {
|
||||
if(!ntsc) return;
|
||||
|
||||
width = SNES_NTSC_OUT_WIDTH(256);
|
||||
burst ^= burst_toggle;
|
||||
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
unsigned line_burst = burst;
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
uint16_t *in = input + y * pitch;
|
||||
uint32_t *out = output + y * outpitch;
|
||||
|
||||
//render as many lines in one snes_ntsc_blit as possible:
|
||||
//do this by determining for how many lines the width stays the same
|
||||
unsigned rheight = 1;
|
||||
unsigned rwidth = line[y];
|
||||
while(y + rheight < height && rwidth == line[y + rheight]) rheight++;
|
||||
|
||||
if(rwidth == 256) {
|
||||
snes_ntsc_blit (ntsc, in, pitch, line_burst, rwidth, rheight, out, outpitch << 2);
|
||||
} else {
|
||||
snes_ntsc_blit_hires(ntsc, in, pitch, line_burst, rwidth, rheight, out, outpitch << 2);
|
||||
}
|
||||
|
||||
line_burst = (line_burst + rheight) % 3;
|
||||
y += rheight;
|
||||
}
|
||||
}
|
||||
|
||||
void NTSCFilter::adjust(
|
||||
float hue, float saturation, float contrast,
|
||||
float brightness, float sharpness, bool merge_fields
|
||||
) {
|
||||
static snes_ntsc_setup_t defaults;
|
||||
snes_ntsc_setup_t setup = defaults;
|
||||
setup.hue = hue;
|
||||
setup.saturation = saturation;
|
||||
setup.contrast = contrast;
|
||||
setup.brightness = brightness;
|
||||
setup.sharpness = sharpness;
|
||||
setup.resolution = sharpness;
|
||||
setup.merge_fields = merge_fields;
|
||||
setup.bsnes_colortbl = 0;
|
||||
|
||||
if(!ntsc) {
|
||||
ntsc = (snes_ntsc_t*)malloc(sizeof *ntsc);
|
||||
if(!ntsc) {
|
||||
fprintf(stderr, "error: snes_ntsc: out of memory\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
burst = 0;
|
||||
burst_toggle = (merge_fields ? 0 : 1); //don't toggle burst when fields are merged
|
||||
snes_ntsc_init(ntsc, &setup);
|
||||
}
|
||||
|
||||
NTSCFilter::NTSCFilter() {
|
||||
ntsc = 0;
|
||||
adjust(0, 0, 0, 0, 0, false);
|
||||
}
|
||||
|
||||
NTSCFilter::~NTSCFilter() {
|
||||
if(ntsc) free(ntsc);
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
#include "snes_ntsc/snes_ntsc.h"
|
||||
|
||||
class NTSCFilter : public Filter {
|
||||
public:
|
||||
void size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height);
|
||||
void render(uint32_t*, unsigned, uint16_t*, unsigned, unsigned*, unsigned, unsigned);
|
||||
void adjust(float hue, float saturation, float contrast, float brightness, float sharpness, bool merge_fields);
|
||||
|
||||
NTSCFilter();
|
||||
~NTSCFilter();
|
||||
|
||||
private:
|
||||
struct snes_ntsc_t *ntsc;
|
||||
int burst, burst_toggle;
|
||||
};
|
||||
|
||||
extern NTSCFilter filter_ntsc;
|
|
@ -1,57 +0,0 @@
|
|||
Scale2xFilter filter_scale2x;
|
||||
|
||||
void Scale2xFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) {
|
||||
outwidth = width;
|
||||
outheight = height;
|
||||
|
||||
if(width <= 256 && height <= 240) {
|
||||
outwidth *= 2;
|
||||
outheight *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void Scale2xFilter::render(
|
||||
uint32_t *output, unsigned outpitch, uint16_t *input, unsigned pitch,
|
||||
unsigned *line, unsigned width, unsigned height
|
||||
) {
|
||||
if(width > 256 || height > 240) {
|
||||
filter_direct.render(output, outpitch, input, pitch, line, width, height);
|
||||
return;
|
||||
}
|
||||
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
uint32_t *out0 = output;
|
||||
uint32_t *out1 = output + outpitch;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
int prevline = (y == 0) ? 0 : pitch;
|
||||
int nextline = (y == height - 1) ? 0 : pitch;
|
||||
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
uint16_t A = *(input - prevline);
|
||||
uint16_t B = (x > 0) ? *(input - 1) : *input;
|
||||
uint16_t C = *input;
|
||||
uint16_t D = (x < 255) ? *(input + 1) : *input;
|
||||
uint16_t E = *(input++ + nextline);
|
||||
uint32_t c = colortable[C];
|
||||
|
||||
if(A != E && B != D) {
|
||||
*out0++ = (A == B ? colortable[A] : c);
|
||||
*out0++ = (A == D ? colortable[A] : c);
|
||||
*out1++ = (E == B ? colortable[E] : c);
|
||||
*out1++ = (E == D ? colortable[E] : c);
|
||||
} else {
|
||||
*out0++ = c;
|
||||
*out0++ = c;
|
||||
*out1++ = c;
|
||||
*out1++ = c;
|
||||
}
|
||||
}
|
||||
|
||||
input += pitch - 256;
|
||||
out0 += outpitch + outpitch - 512;
|
||||
out1 += outpitch + outpitch - 512;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
class Scale2xFilter : public Filter {
|
||||
public:
|
||||
void size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height);
|
||||
void render(uint32_t*, unsigned, uint16_t*, unsigned, unsigned*, unsigned, unsigned);
|
||||
};
|
||||
|
||||
extern Scale2xFilter filter_scale2x;
|
|
@ -1,42 +0,0 @@
|
|||
ScanlineFilter filter_scanline;
|
||||
|
||||
void ScanlineFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) {
|
||||
outwidth = width;
|
||||
outheight = height > 240 ? height : height * 2;
|
||||
}
|
||||
|
||||
void ScanlineFilter::render(
|
||||
uint32_t *output, unsigned outpitch, uint16_t *input, unsigned pitch,
|
||||
unsigned *line, unsigned width, unsigned height
|
||||
) {
|
||||
if(height > 240) {
|
||||
filter_direct.render(output, outpitch, input, pitch, line, width, height);
|
||||
return;
|
||||
}
|
||||
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
uint32_t *out0 = output;
|
||||
uint32_t *out1 = output + outpitch;
|
||||
if(width == 512 && line[y] == 256) {
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
uint16_t p = *input++;
|
||||
*out0++ = colortable[p];
|
||||
*out0++ = colortable[p];
|
||||
*out1++ = (colortable[p] >> 1) & 0x7f7f7f;
|
||||
*out1++ = (colortable[p] >> 1) & 0x7f7f7f;
|
||||
}
|
||||
input += 256;
|
||||
} else {
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint16_t p = *input++;
|
||||
*out0++ = colortable[p];
|
||||
*out1++ = (colortable[p] >> 1) & 0x7f7f7f;
|
||||
}
|
||||
}
|
||||
input += pitch - width;
|
||||
output += outpitch * 2;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
class ScanlineFilter : public Filter {
|
||||
public:
|
||||
void size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height);
|
||||
void render(uint32_t*, unsigned, uint16_t*, unsigned, unsigned*, unsigned, unsigned);
|
||||
};
|
||||
|
||||
extern ScanlineFilter filter_scanline;
|
|
@ -1,251 +0,0 @@
|
|||
/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
|
||||
|
||||
#include "snes_ntsc.h"
|
||||
|
||||
/* Copyright (C) 2006-2007 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
snes_ntsc_setup_t const snes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0 };
|
||||
snes_ntsc_setup_t const snes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 };
|
||||
snes_ntsc_setup_t const snes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0 };
|
||||
snes_ntsc_setup_t const snes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 1, 0, 0 };
|
||||
|
||||
#define alignment_count 3
|
||||
#define burst_count 3
|
||||
#define rescale_in 8
|
||||
#define rescale_out 7
|
||||
|
||||
#define artifacts_mid 1.0f
|
||||
#define fringing_mid 1.0f
|
||||
#define std_decoder_hue 0
|
||||
|
||||
#define rgb_bits 7 /* half normal range to allow for doubled hires pixels */
|
||||
#define gamma_size 32
|
||||
|
||||
#include "snes_ntsc_impl.h"
|
||||
|
||||
/* 3 input pixels -> 8 composite samples */
|
||||
pixel_info_t const snes_ntsc_pixels [alignment_count] = {
|
||||
{ PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } },
|
||||
{ PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } },
|
||||
{ PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } },
|
||||
};
|
||||
|
||||
static void merge_kernel_fields( snes_ntsc_rgb_t* io )
|
||||
{
|
||||
int n;
|
||||
for ( n = burst_size; n; --n )
|
||||
{
|
||||
snes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias;
|
||||
snes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias;
|
||||
snes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias;
|
||||
/* merge colors without losing precision */
|
||||
io [burst_size * 0] =
|
||||
((p0 + p1 - ((p0 ^ p1) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
|
||||
io [burst_size * 1] =
|
||||
((p1 + p2 - ((p1 ^ p2) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
|
||||
io [burst_size * 2] =
|
||||
((p2 + p0 - ((p2 ^ p0) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
|
||||
++io;
|
||||
}
|
||||
}
|
||||
|
||||
static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out )
|
||||
{
|
||||
int n;
|
||||
for ( n = burst_count; n; --n )
|
||||
{
|
||||
unsigned i;
|
||||
for ( i = 0; i < rgb_kernel_size / 2; i++ )
|
||||
{
|
||||
snes_ntsc_rgb_t error = color -
|
||||
out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] -
|
||||
out [i + 7] - out [i + 5 +14] - out [i + 3 +28];
|
||||
DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 );
|
||||
}
|
||||
out += alignment_count * rgb_kernel_size;
|
||||
}
|
||||
}
|
||||
|
||||
void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup )
|
||||
{
|
||||
int merge_fields;
|
||||
int entry;
|
||||
init_t impl;
|
||||
if ( !setup )
|
||||
setup = &snes_ntsc_composite;
|
||||
init( &impl, setup );
|
||||
|
||||
merge_fields = setup->merge_fields;
|
||||
if ( setup->artifacts <= -1 && setup->fringing <= -1 )
|
||||
merge_fields = 1;
|
||||
|
||||
for ( entry = 0; entry < snes_ntsc_palette_size; entry++ )
|
||||
{
|
||||
/* Reduce number of significant bits of source color. Clearing the
|
||||
low bits of R and B were least notictable. Modifying green was too
|
||||
noticeable. */
|
||||
int ir = entry >> 8 & 0x1E;
|
||||
int ig = entry >> 4 & 0x1F;
|
||||
int ib = entry << 1 & 0x1E;
|
||||
|
||||
#if SNES_NTSC_BSNES_COLORTBL
|
||||
if ( setup->bsnes_colortbl )
|
||||
{
|
||||
int bgr15 = (ib << 10) | (ig << 5) | ir;
|
||||
unsigned long rgb16 = setup->bsnes_colortbl [bgr15];
|
||||
ir = rgb16 >> 11 & 0x1E;
|
||||
ig = rgb16 >> 6 & 0x1F;
|
||||
ib = rgb16 & 0x1E;
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
float rr = impl.to_float [ir];
|
||||
float gg = impl.to_float [ig];
|
||||
float bb = impl.to_float [ib];
|
||||
|
||||
float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i );
|
||||
|
||||
int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g );
|
||||
snes_ntsc_rgb_t rgb = PACK_RGB( r, g, b );
|
||||
|
||||
snes_ntsc_rgb_t* out = ntsc->table [entry];
|
||||
gen_kernel( &impl, y, i, q, out );
|
||||
if ( merge_fields )
|
||||
merge_kernel_fields( out );
|
||||
correct_errors( rgb, out );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef SNES_NTSC_NO_BLITTERS
|
||||
|
||||
void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
|
||||
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
|
||||
{
|
||||
int chunk_count = (in_width - 1) / snes_ntsc_in_chunk;
|
||||
for ( ; in_height; --in_height )
|
||||
{
|
||||
SNES_NTSC_IN_T const* line_in = input;
|
||||
SNES_NTSC_BEGIN_ROW( ntsc, burst_phase,
|
||||
snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN( *line_in ) );
|
||||
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
|
||||
int n;
|
||||
++line_in;
|
||||
|
||||
for ( n = chunk_count; n; --n )
|
||||
{
|
||||
/* order of input and output pixels must not be altered */
|
||||
SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
|
||||
SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
|
||||
SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
|
||||
SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
line_in += 3;
|
||||
line_out += 7;
|
||||
}
|
||||
|
||||
/* finish final pixels */
|
||||
SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
|
||||
SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
|
||||
SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
|
||||
SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
|
||||
input += in_row_width;
|
||||
rgb_out = (char*) rgb_out + out_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
|
||||
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
|
||||
{
|
||||
int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2);
|
||||
for ( ; in_height; --in_height )
|
||||
{
|
||||
SNES_NTSC_IN_T const* line_in = input;
|
||||
SNES_NTSC_HIRES_ROW( ntsc, burst_phase,
|
||||
snes_ntsc_black, snes_ntsc_black, snes_ntsc_black,
|
||||
SNES_NTSC_ADJ_IN( line_in [0] ),
|
||||
SNES_NTSC_ADJ_IN( line_in [1] ) );
|
||||
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
|
||||
int n;
|
||||
line_in += 2;
|
||||
|
||||
for ( n = chunk_count; n; --n )
|
||||
{
|
||||
/* twice as many input pixels per chunk */
|
||||
SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
|
||||
SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
|
||||
SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
|
||||
SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 3, SNES_NTSC_ADJ_IN( line_in [3] ) );
|
||||
SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 4, SNES_NTSC_ADJ_IN( line_in [4] ) );
|
||||
SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 5, SNES_NTSC_ADJ_IN( line_in [5] ) );
|
||||
SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
line_in += 6;
|
||||
line_out += 7;
|
||||
}
|
||||
|
||||
SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
|
||||
SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
|
||||
SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
|
||||
SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 3, snes_ntsc_black );
|
||||
SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 4, snes_ntsc_black );
|
||||
SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 5, snes_ntsc_black );
|
||||
SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
|
||||
input += in_row_width;
|
||||
rgb_out = (char*) rgb_out + out_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,228 +0,0 @@
|
|||
/* SNES NTSC video filter */
|
||||
|
||||
/* snes_ntsc 0.2.2 */
|
||||
#ifndef SNES_NTSC_H
|
||||
#define SNES_NTSC_H
|
||||
|
||||
#include "snes_ntsc_config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown
|
||||
in parenthesis and should remain fairly stable in future versions. */
|
||||
typedef struct snes_ntsc_setup_t
|
||||
{
|
||||
/* Basic parameters */
|
||||
double hue; /* -1 = -180 degrees +1 = +180 degrees */
|
||||
double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */
|
||||
double contrast; /* -1 = dark (0.5) +1 = light (1.5) */
|
||||
double brightness; /* -1 = dark (0.5) +1 = light (1.5) */
|
||||
double sharpness; /* edge contrast enhancement/blurring */
|
||||
|
||||
/* Advanced parameters */
|
||||
double gamma; /* -1 = dark (1.5) +1 = light (0.5) */
|
||||
double resolution; /* image resolution */
|
||||
double artifacts; /* artifacts caused by color changes */
|
||||
double fringing; /* color artifacts caused by brightness changes */
|
||||
double bleed; /* color bleed (color resolution reduction) */
|
||||
int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */
|
||||
float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */
|
||||
|
||||
unsigned long const* bsnes_colortbl; /* undocumented; set to 0 */
|
||||
} snes_ntsc_setup_t;
|
||||
|
||||
/* Video format presets */
|
||||
extern snes_ntsc_setup_t const snes_ntsc_composite; /* color bleeding + artifacts */
|
||||
extern snes_ntsc_setup_t const snes_ntsc_svideo; /* color bleeding only */
|
||||
extern snes_ntsc_setup_t const snes_ntsc_rgb; /* crisp image */
|
||||
extern snes_ntsc_setup_t const snes_ntsc_monochrome;/* desaturated + artifacts */
|
||||
|
||||
/* Initializes and adjusts parameters. Can be called multiple times on the same
|
||||
snes_ntsc_t object. Can pass NULL for either parameter. */
|
||||
typedef struct snes_ntsc_t snes_ntsc_t;
|
||||
void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup );
|
||||
|
||||
/* Filters one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT
|
||||
and output RGB depth is set by SNES_NTSC_OUT_DEPTH. Both default to 16-bit RGB.
|
||||
In_row_width is the number of pixels to get to the next input row. Out_pitch
|
||||
is the number of *bytes* to get to the next output row. */
|
||||
void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
|
||||
long in_row_width, int burst_phase, int in_width, int in_height,
|
||||
void* rgb_out, long out_pitch );
|
||||
|
||||
void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
|
||||
long in_row_width, int burst_phase, int in_width, int in_height,
|
||||
void* rgb_out, long out_pitch );
|
||||
|
||||
/* Number of output pixels written by low-res blitter for given input width. Width
|
||||
might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded
|
||||
value. Guaranteed not to round 256 down at all. */
|
||||
#define SNES_NTSC_OUT_WIDTH( in_width ) \
|
||||
((((in_width) - 1) / snes_ntsc_in_chunk + 1) * snes_ntsc_out_chunk)
|
||||
|
||||
/* Number of low-res input pixels that will fit within given output width. Might be
|
||||
rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded
|
||||
value. */
|
||||
#define SNES_NTSC_IN_WIDTH( out_width ) \
|
||||
(((out_width) / snes_ntsc_out_chunk - 1) * snes_ntsc_in_chunk + 1)
|
||||
|
||||
|
||||
/* Interface for user-defined custom blitters */
|
||||
|
||||
enum { snes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */
|
||||
enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */
|
||||
enum { snes_ntsc_black = 0 }; /* palette index for black */
|
||||
enum { snes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */
|
||||
|
||||
/* Begins outputting row and starts three pixels. First pixel will be cut off a bit.
|
||||
Use snes_ntsc_black for unused pixels. Declares variables, so must be before first
|
||||
statement in a block (unless you're using C++). */
|
||||
#define SNES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \
|
||||
char const* ktable = \
|
||||
(char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\
|
||||
SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, SNES_NTSC_IN_FORMAT, ktable )
|
||||
|
||||
/* Begins input pixel */
|
||||
#define SNES_NTSC_COLOR_IN( index, color ) \
|
||||
SNES_NTSC_COLOR_IN_( index, color, SNES_NTSC_IN_FORMAT, ktable )
|
||||
|
||||
/* Generates output pixel. Bits can be 24, 16, 15, 14, 32 (treated as 24), or 0:
|
||||
24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB)
|
||||
16: RRRRRGGG GGGBBBBB (5-6-5 RGB)
|
||||
15: RRRRRGG GGGBBBBB (5-5-5 RGB)
|
||||
14: BBBBBGG GGGRRRRR (5-5-5 BGR, native SNES format)
|
||||
0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */
|
||||
#define SNES_NTSC_RGB_OUT( index, rgb_out, bits ) \
|
||||
SNES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 1 )
|
||||
|
||||
/* Hires equivalents */
|
||||
#define SNES_NTSC_HIRES_ROW( ntsc, burst, pixel1, pixel2, pixel3, pixel4, pixel5 ) \
|
||||
char const* ktable = \
|
||||
(char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\
|
||||
unsigned const snes_ntsc_pixel1_ = (pixel1);\
|
||||
snes_ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel1_ );\
|
||||
unsigned const snes_ntsc_pixel2_ = (pixel2);\
|
||||
snes_ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel2_ );\
|
||||
unsigned const snes_ntsc_pixel3_ = (pixel3);\
|
||||
snes_ntsc_rgb_t const* kernel3 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel3_ );\
|
||||
unsigned const snes_ntsc_pixel4_ = (pixel4);\
|
||||
snes_ntsc_rgb_t const* kernel4 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel4_ );\
|
||||
unsigned const snes_ntsc_pixel5_ = (pixel5);\
|
||||
snes_ntsc_rgb_t const* kernel5 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel5_ );\
|
||||
snes_ntsc_rgb_t const* kernel0 = kernel1;\
|
||||
snes_ntsc_rgb_t const* kernelx0;\
|
||||
snes_ntsc_rgb_t const* kernelx1 = kernel1;\
|
||||
snes_ntsc_rgb_t const* kernelx2 = kernel1;\
|
||||
snes_ntsc_rgb_t const* kernelx3 = kernel1;\
|
||||
snes_ntsc_rgb_t const* kernelx4 = kernel1;\
|
||||
snes_ntsc_rgb_t const* kernelx5 = kernel1
|
||||
|
||||
#define SNES_NTSC_HIRES_OUT( x, rgb_out, bits ) {\
|
||||
snes_ntsc_rgb_t raw_ =\
|
||||
kernel0 [ x ] + kernel2 [(x+5)%7+14] + kernel4 [(x+3)%7+28] +\
|
||||
kernelx0 [(x+7)%7+7] + kernelx2 [(x+5)%7+21] + kernelx4 [(x+3)%7+35] +\
|
||||
kernel1 [(x+6)%7 ] + kernel3 [(x+4)%7+14] + kernel5 [(x+2)%7+28] +\
|
||||
kernelx1 [(x+6)%7+7] + kernelx3 [(x+4)%7+21] + kernelx5 [(x+2)%7+35];\
|
||||
SNES_NTSC_CLAMP_( raw_, 0 );\
|
||||
SNES_NTSC_RGB_OUT_( rgb_out, (bits), 0 );\
|
||||
}
|
||||
|
||||
|
||||
/* private */
|
||||
enum { snes_ntsc_entry_size = 128 };
|
||||
enum { snes_ntsc_palette_size = 0x2000 };
|
||||
typedef unsigned long snes_ntsc_rgb_t;
|
||||
struct snes_ntsc_t {
|
||||
snes_ntsc_rgb_t table [snes_ntsc_palette_size] [snes_ntsc_entry_size];
|
||||
};
|
||||
enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count };
|
||||
|
||||
#define SNES_NTSC_RGB16( ktable, n ) \
|
||||
(snes_ntsc_rgb_t const*) (ktable + ((n & 0x001E) | (n >> 1 & 0x03E0) | (n >> 2 & 0x3C00)) * \
|
||||
(snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t)))
|
||||
|
||||
#define SNES_NTSC_BGR15( ktable, n ) \
|
||||
(snes_ntsc_rgb_t const*) (ktable + ((n << 9 & 0x3C00) | (n & 0x03E0) | (n >> 10 & 0x001E)) * \
|
||||
(snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t)))
|
||||
|
||||
/* common 3->7 ntsc macros */
|
||||
#define SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \
|
||||
unsigned const snes_ntsc_pixel0_ = (pixel0);\
|
||||
snes_ntsc_rgb_t const* kernel0 = ENTRY( table, snes_ntsc_pixel0_ );\
|
||||
unsigned const snes_ntsc_pixel1_ = (pixel1);\
|
||||
snes_ntsc_rgb_t const* kernel1 = ENTRY( table, snes_ntsc_pixel1_ );\
|
||||
unsigned const snes_ntsc_pixel2_ = (pixel2);\
|
||||
snes_ntsc_rgb_t const* kernel2 = ENTRY( table, snes_ntsc_pixel2_ );\
|
||||
snes_ntsc_rgb_t const* kernelx0;\
|
||||
snes_ntsc_rgb_t const* kernelx1 = kernel0;\
|
||||
snes_ntsc_rgb_t const* kernelx2 = kernel0
|
||||
|
||||
#define SNES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\
|
||||
snes_ntsc_rgb_t raw_ =\
|
||||
kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\
|
||||
kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\
|
||||
SNES_NTSC_CLAMP_( raw_, shift );\
|
||||
SNES_NTSC_RGB_OUT_( rgb_out, bits, shift );\
|
||||
}
|
||||
|
||||
/* common ntsc macros */
|
||||
#define snes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1))
|
||||
#define snes_ntsc_clamp_mask (snes_ntsc_rgb_builder * 3 / 2)
|
||||
#define snes_ntsc_clamp_add (snes_ntsc_rgb_builder * 0x101)
|
||||
#define SNES_NTSC_CLAMP_( io, shift ) {\
|
||||
snes_ntsc_rgb_t sub = (io) >> (9-(shift)) & snes_ntsc_clamp_mask;\
|
||||
snes_ntsc_rgb_t clamp = snes_ntsc_clamp_add - sub;\
|
||||
io |= clamp;\
|
||||
clamp -= sub;\
|
||||
io &= clamp;\
|
||||
}
|
||||
|
||||
#define SNES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\
|
||||
unsigned color_;\
|
||||
kernelx##index = kernel##index;\
|
||||
kernel##index = (color_ = (color), ENTRY( table, color_ ));\
|
||||
}
|
||||
|
||||
/* x is always zero except in snes_ntsc library */
|
||||
/* original routine */
|
||||
/*
|
||||
#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\
|
||||
if ( bits == 16 )\
|
||||
rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\
|
||||
if ( bits == 24 || bits == 32 )\
|
||||
rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\
|
||||
if ( bits == 15 )\
|
||||
rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\
|
||||
if ( bits == 14 )\
|
||||
rgb_out = (raw_>>(24-x)& 0x001F)|(raw_>>(9-x)&0x03E0)|(raw_<<(6+x)&0x7C00);\
|
||||
if ( bits == 0 )\
|
||||
rgb_out = raw_ << x;\
|
||||
}
|
||||
*/
|
||||
|
||||
/* custom bsnes routine -- hooks into bsnes colortable */
|
||||
#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\
|
||||
if ( bits == 16 ) {\
|
||||
rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\
|
||||
rgb_out = ((rgb_out&0xf800)>>11)|((rgb_out&0x07c0)>>1)|((rgb_out&0x001f)<<10);\
|
||||
rgb_out = colortable[rgb_out];\
|
||||
} else if ( bits == 24 || bits == 32 ) {\
|
||||
rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\
|
||||
rgb_out = ((rgb_out&0xf80000)>>19)|((rgb_out&0x00f800)>>6)|((rgb_out&0x0000f8)<<7);\
|
||||
rgb_out = colortable[rgb_out];\
|
||||
} else if ( bits == 15 ) {\
|
||||
rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\
|
||||
rgb_out = ((rgb_out&0x7c00)>>10)|((rgb_out&0x03e0))|((rgb_out&0x001f)<<10);\
|
||||
rgb_out = colortable[rgb_out];\
|
||||
} else {\
|
||||
rgb_out = raw_ << x;\
|
||||
}\
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,26 +0,0 @@
|
|||
/* Configure library by modifying this file */
|
||||
|
||||
#ifndef SNES_NTSC_CONFIG_H
|
||||
#define SNES_NTSC_CONFIG_H
|
||||
|
||||
/* Format of source pixels */
|
||||
/* #define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB16 */
|
||||
#define SNES_NTSC_IN_FORMAT SNES_NTSC_BGR15
|
||||
|
||||
/* The following affect the built-in blitter only; a custom blitter can
|
||||
handle things however it wants. */
|
||||
|
||||
/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */
|
||||
#define SNES_NTSC_OUT_DEPTH 32
|
||||
|
||||
/* Type of input pixel values */
|
||||
#define SNES_NTSC_IN_T unsigned short
|
||||
|
||||
/* Each raw pixel input value is passed through this. You might want to mask
|
||||
the pixel index if you use the high bits as flags, etc. */
|
||||
#define SNES_NTSC_ADJ_IN( in ) in
|
||||
|
||||
/* For each pixel, this is the basic operation:
|
||||
output_color = SNES_NTSC_ADJ_IN( SNES_NTSC_IN_T ) */
|
||||
|
||||
#endif
|
|
@ -1,439 +0,0 @@
|
|||
/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
|
||||
|
||||
/* Common implementation of NTSC filters */
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
/* Copyright (C) 2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#define DISABLE_CORRECTION 0
|
||||
|
||||
#undef PI
|
||||
#define PI 3.14159265358979323846f
|
||||
|
||||
#ifndef LUMA_CUTOFF
|
||||
#define LUMA_CUTOFF 0.20
|
||||
#endif
|
||||
#ifndef gamma_size
|
||||
#define gamma_size 1
|
||||
#endif
|
||||
#ifndef rgb_bits
|
||||
#define rgb_bits 8
|
||||
#endif
|
||||
#ifndef artifacts_max
|
||||
#define artifacts_max (artifacts_mid * 1.5f)
|
||||
#endif
|
||||
#ifndef fringing_max
|
||||
#define fringing_max (fringing_mid * 2)
|
||||
#endif
|
||||
#ifndef STD_HUE_CONDITION
|
||||
#define STD_HUE_CONDITION( setup ) 1
|
||||
#endif
|
||||
|
||||
#define ext_decoder_hue (std_decoder_hue + 15)
|
||||
#define rgb_unit (1 << rgb_bits)
|
||||
#define rgb_offset (rgb_unit * 2 + 0.5f)
|
||||
|
||||
enum { burst_size = snes_ntsc_entry_size / burst_count };
|
||||
enum { kernel_half = 16 };
|
||||
enum { kernel_size = kernel_half * 2 + 1 };
|
||||
|
||||
typedef struct init_t
|
||||
{
|
||||
float to_rgb [burst_count * 6];
|
||||
float to_float [gamma_size];
|
||||
float contrast;
|
||||
float brightness;
|
||||
float artifacts;
|
||||
float fringing;
|
||||
float kernel [rescale_out * kernel_size * 2];
|
||||
} init_t;
|
||||
|
||||
#define ROTATE_IQ( i, q, sin_b, cos_b ) {\
|
||||
float t;\
|
||||
t = i * cos_b - q * sin_b;\
|
||||
q = i * sin_b + q * cos_b;\
|
||||
i = t;\
|
||||
}
|
||||
|
||||
static void init_filters( init_t* impl, snes_ntsc_setup_t const* setup )
|
||||
{
|
||||
#if rescale_out > 1
|
||||
float kernels [kernel_size * 2];
|
||||
#else
|
||||
float* const kernels = impl->kernel;
|
||||
#endif
|
||||
|
||||
/* generate luma (y) filter using sinc kernel */
|
||||
{
|
||||
/* sinc with rolloff (dsf) */
|
||||
float const rolloff = 1 + (float) setup->sharpness * (float) 0.032;
|
||||
float const maxh = 32;
|
||||
float const pow_a_n = (float) pow( rolloff, maxh );
|
||||
float sum;
|
||||
int i;
|
||||
/* quadratic mapping to reduce negative (blurring) range */
|
||||
float to_angle = (float) setup->resolution + 1;
|
||||
to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1);
|
||||
|
||||
kernels [kernel_size * 3 / 2] = maxh; /* default center value */
|
||||
for ( i = 0; i < kernel_half * 2 + 1; i++ )
|
||||
{
|
||||
int x = i - kernel_half;
|
||||
float angle = x * to_angle;
|
||||
/* instability occurs at center point with rolloff very close to 1.0 */
|
||||
if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 )
|
||||
{
|
||||
float rolloff_cos_a = rolloff * (float) cos( angle );
|
||||
float num = 1 - rolloff_cos_a -
|
||||
pow_a_n * (float) cos( maxh * angle ) +
|
||||
pow_a_n * rolloff * (float) cos( (maxh - 1) * angle );
|
||||
float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
|
||||
float dsf = num / den;
|
||||
kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
/* apply blackman window and find sum */
|
||||
sum = 0;
|
||||
for ( i = 0; i < kernel_half * 2 + 1; i++ )
|
||||
{
|
||||
float x = PI * 2 / (kernel_half * 2) * i;
|
||||
float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 );
|
||||
sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman);
|
||||
}
|
||||
|
||||
/* normalize kernel */
|
||||
sum = 1.0f / sum;
|
||||
for ( i = 0; i < kernel_half * 2 + 1; i++ )
|
||||
{
|
||||
int x = kernel_size * 3 / 2 - kernel_half + i;
|
||||
kernels [x] *= sum;
|
||||
assert( kernels [x] == kernels [x] ); /* catch numerical instability */
|
||||
}
|
||||
}
|
||||
|
||||
/* generate chroma (iq) filter using gaussian kernel */
|
||||
{
|
||||
float const cutoff_factor = -0.03125f;
|
||||
float cutoff = (float) setup->bleed;
|
||||
int i;
|
||||
|
||||
if ( cutoff < 0 )
|
||||
{
|
||||
/* keep extreme value accessible only near upper end of scale (1.0) */
|
||||
cutoff *= cutoff;
|
||||
cutoff *= cutoff;
|
||||
cutoff *= cutoff;
|
||||
cutoff *= -30.0f / 0.65f;
|
||||
}
|
||||
cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff;
|
||||
|
||||
for ( i = -kernel_half; i <= kernel_half; i++ )
|
||||
kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff );
|
||||
|
||||
/* normalize even and odd phases separately */
|
||||
for ( i = 0; i < 2; i++ )
|
||||
{
|
||||
float sum = 0;
|
||||
int x;
|
||||
for ( x = i; x < kernel_size; x += 2 )
|
||||
sum += kernels [x];
|
||||
|
||||
sum = 1.0f / sum;
|
||||
for ( x = i; x < kernel_size; x += 2 )
|
||||
{
|
||||
kernels [x] *= sum;
|
||||
assert( kernels [x] == kernels [x] ); /* catch numerical instability */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
printf( "luma:\n" );
|
||||
for ( i = kernel_size; i < kernel_size * 2; i++ )
|
||||
printf( "%f\n", kernels [i] );
|
||||
printf( "chroma:\n" );
|
||||
for ( i = 0; i < kernel_size; i++ )
|
||||
printf( "%f\n", kernels [i] );
|
||||
*/
|
||||
|
||||
/* generate linear rescale kernels */
|
||||
#if rescale_out > 1
|
||||
{
|
||||
float weight = 1.0f;
|
||||
float* out = impl->kernel;
|
||||
int n = rescale_out;
|
||||
do
|
||||
{
|
||||
float remain = 0;
|
||||
int i;
|
||||
weight -= 1.0f / rescale_in;
|
||||
for ( i = 0; i < kernel_size * 2; i++ )
|
||||
{
|
||||
float cur = kernels [i];
|
||||
float m = cur * weight;
|
||||
*out++ = m + remain;
|
||||
remain = cur - m;
|
||||
}
|
||||
}
|
||||
while ( --n );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static float const default_decoder [6] =
|
||||
{ 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f };
|
||||
|
||||
static void init( init_t* impl, snes_ntsc_setup_t const* setup )
|
||||
{
|
||||
impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset;
|
||||
impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit;
|
||||
#ifdef default_palette_contrast
|
||||
if ( !setup->palette )
|
||||
impl->contrast *= default_palette_contrast;
|
||||
#endif
|
||||
|
||||
impl->artifacts = (float) setup->artifacts;
|
||||
if ( impl->artifacts > 0 )
|
||||
impl->artifacts *= artifacts_max - artifacts_mid;
|
||||
impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid;
|
||||
|
||||
impl->fringing = (float) setup->fringing;
|
||||
if ( impl->fringing > 0 )
|
||||
impl->fringing *= fringing_max - fringing_mid;
|
||||
impl->fringing = impl->fringing * fringing_mid + fringing_mid;
|
||||
|
||||
init_filters( impl, setup );
|
||||
|
||||
/* generate gamma table */
|
||||
if ( gamma_size > 1 )
|
||||
{
|
||||
float const to_float = 1.0f / (gamma_size - (gamma_size > 1));
|
||||
float const gamma = 1.1333f - (float) setup->gamma * 0.5f;
|
||||
/* match common PC's 2.2 gamma to TV's 2.65 gamma */
|
||||
int i;
|
||||
for ( i = 0; i < gamma_size; i++ )
|
||||
impl->to_float [i] =
|
||||
(float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness;
|
||||
}
|
||||
|
||||
/* setup decoder matricies */
|
||||
{
|
||||
float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue;
|
||||
float sat = (float) setup->saturation + 1;
|
||||
float const* decoder = setup->decoder_matrix;
|
||||
if ( !decoder )
|
||||
{
|
||||
decoder = default_decoder;
|
||||
if ( STD_HUE_CONDITION( setup ) )
|
||||
hue += PI / 180 * (std_decoder_hue - ext_decoder_hue);
|
||||
}
|
||||
|
||||
{
|
||||
float s = (float) sin( hue ) * sat;
|
||||
float c = (float) cos( hue ) * sat;
|
||||
float* out = impl->to_rgb;
|
||||
int n;
|
||||
|
||||
n = burst_count;
|
||||
do
|
||||
{
|
||||
float const* in = decoder;
|
||||
int n = 3;
|
||||
do
|
||||
{
|
||||
float i = *in++;
|
||||
float q = *in++;
|
||||
*out++ = i * c - q * s;
|
||||
*out++ = i * s + q * c;
|
||||
}
|
||||
while ( --n );
|
||||
if ( burst_count <= 1 )
|
||||
break;
|
||||
ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */
|
||||
}
|
||||
while ( --n );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* kernel generation */
|
||||
|
||||
#define RGB_TO_YIQ( r, g, b, y, i ) (\
|
||||
(y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\
|
||||
(i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\
|
||||
((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\
|
||||
)
|
||||
|
||||
#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\
|
||||
r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\
|
||||
g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\
|
||||
(type) (y + to_rgb [4] * i + to_rgb [5] * q)\
|
||||
)
|
||||
|
||||
#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1)
|
||||
|
||||
enum { rgb_kernel_size = burst_size / alignment_count };
|
||||
enum { rgb_bias = rgb_unit * 2 * snes_ntsc_rgb_builder };
|
||||
|
||||
typedef struct pixel_info_t
|
||||
{
|
||||
int offset;
|
||||
float negate;
|
||||
float kernel [4];
|
||||
} pixel_info_t;
|
||||
|
||||
#if rescale_in > 1
|
||||
#define PIXEL_OFFSET_( ntsc, scaled ) \
|
||||
(kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \
|
||||
(kernel_size * 2 * scaled))
|
||||
|
||||
#define PIXEL_OFFSET( ntsc, scaled ) \
|
||||
PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\
|
||||
(((scaled) + rescale_out * 10) % rescale_out) ),\
|
||||
(1.0f - (((ntsc) + 100) & 2))
|
||||
#else
|
||||
#define PIXEL_OFFSET( ntsc, scaled ) \
|
||||
(kernel_size / 2 + (ntsc) - (scaled)),\
|
||||
(1.0f - (((ntsc) + 100) & 2))
|
||||
#endif
|
||||
|
||||
extern pixel_info_t const snes_ntsc_pixels [alignment_count];
|
||||
|
||||
/* Generate pixel at all burst phases and column alignments */
|
||||
static void gen_kernel( init_t* impl, float y, float i, float q, snes_ntsc_rgb_t* out )
|
||||
{
|
||||
/* generate for each scanline burst phase */
|
||||
float const* to_rgb = impl->to_rgb;
|
||||
int burst_remain = burst_count;
|
||||
y -= rgb_offset;
|
||||
do
|
||||
{
|
||||
/* Encode yiq into *two* composite signals (to allow control over artifacting).
|
||||
Convolve these with kernels which: filter respective components, apply
|
||||
sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack
|
||||
into integer. Based on algorithm by NewRisingSun. */
|
||||
pixel_info_t const* pixel = snes_ntsc_pixels;
|
||||
int alignment_remain = alignment_count;
|
||||
do
|
||||
{
|
||||
/* negate is -1 when composite starts at odd multiple of 2 */
|
||||
float const yy = y * impl->fringing * pixel->negate;
|
||||
float const ic0 = (i + yy) * pixel->kernel [0];
|
||||
float const qc1 = (q + yy) * pixel->kernel [1];
|
||||
float const ic2 = (i - yy) * pixel->kernel [2];
|
||||
float const qc3 = (q - yy) * pixel->kernel [3];
|
||||
|
||||
float const factor = impl->artifacts * pixel->negate;
|
||||
float const ii = i * factor;
|
||||
float const yc0 = (y + ii) * pixel->kernel [0];
|
||||
float const yc2 = (y - ii) * pixel->kernel [2];
|
||||
|
||||
float const qq = q * factor;
|
||||
float const yc1 = (y + qq) * pixel->kernel [1];
|
||||
float const yc3 = (y - qq) * pixel->kernel [3];
|
||||
|
||||
float const* k = &impl->kernel [pixel->offset];
|
||||
int n;
|
||||
++pixel;
|
||||
for ( n = rgb_kernel_size; n; --n )
|
||||
{
|
||||
float i = k[0]*ic0 + k[2]*ic2;
|
||||
float q = k[1]*qc1 + k[3]*qc3;
|
||||
float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 +
|
||||
k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset;
|
||||
if ( rescale_out <= 1 )
|
||||
k--;
|
||||
else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] )
|
||||
k += kernel_size * 2 - 1;
|
||||
else
|
||||
k -= kernel_size * 2 * (rescale_out - 1) + 2;
|
||||
{
|
||||
int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g );
|
||||
*out++ = PACK_RGB( r, g, b ) - rgb_bias;
|
||||
}
|
||||
}
|
||||
}
|
||||
while ( alignment_count > 1 && --alignment_remain );
|
||||
|
||||
if ( burst_count <= 1 )
|
||||
break;
|
||||
|
||||
to_rgb += 6;
|
||||
|
||||
ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */
|
||||
}
|
||||
while ( --burst_remain );
|
||||
}
|
||||
|
||||
static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out );
|
||||
|
||||
#if DISABLE_CORRECTION
|
||||
#define CORRECT_ERROR( a ) { out [i] += rgb_bias; }
|
||||
#define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; }
|
||||
#else
|
||||
#define CORRECT_ERROR( a ) { out [a] += error; }
|
||||
#define DISTRIBUTE_ERROR( a, b, c ) {\
|
||||
snes_ntsc_rgb_t fourth = (error + 2 * snes_ntsc_rgb_builder) >> 2;\
|
||||
fourth &= (rgb_bias >> 1) - snes_ntsc_rgb_builder;\
|
||||
fourth -= rgb_bias >> 2;\
|
||||
out [a] += fourth;\
|
||||
out [b] += fourth;\
|
||||
out [c] += fourth;\
|
||||
out [i] += error - (fourth * 3);\
|
||||
}
|
||||
#endif
|
||||
|
||||
#define RGB_PALETTE_OUT( rgb, out_ )\
|
||||
{\
|
||||
unsigned char* out = (out_);\
|
||||
snes_ntsc_rgb_t clamped = (rgb);\
|
||||
SNES_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\
|
||||
out [0] = (unsigned char) (clamped >> 21);\
|
||||
out [1] = (unsigned char) (clamped >> 11);\
|
||||
out [2] = (unsigned char) (clamped >> 1);\
|
||||
}
|
||||
|
||||
/* blitter related */
|
||||
|
||||
#ifndef restrict
|
||||
#if defined (__GNUC__)
|
||||
#define restrict __restrict__
|
||||
#elif defined (_MSC_VER) && _MSC_VER > 1300
|
||||
#define restrict __restrict
|
||||
#else
|
||||
/* no support for restricted pointers */
|
||||
#define restrict
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#if SNES_NTSC_OUT_DEPTH <= 16
|
||||
#if USHRT_MAX == 0xFFFF
|
||||
typedef unsigned short snes_ntsc_out_t;
|
||||
#else
|
||||
#error "Need 16-bit int type"
|
||||
#endif
|
||||
|
||||
#else
|
||||
#if UINT_MAX == 0xFFFFFFFF
|
||||
typedef unsigned int snes_ntsc_out_t;
|
||||
#elif ULONG_MAX == 0xFFFFFFFF
|
||||
typedef unsigned long snes_ntsc_out_t;
|
||||
#else
|
||||
#error "Need 32-bit int type"
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -47,8 +47,8 @@ else ifeq ($(platform),win)
|
|||
qtlib := -L$(qtpath)/lib
|
||||
qtlib += -L$(qtpath)/plugins/imageformats
|
||||
|
||||
qtlib += -lmingw32 -lqtmain -lQtGui -lcomdlg32 -loleaut32 -limm32 -lwinmm
|
||||
qtlib += -lwinspool -lmsimg32 -lQtCore -lole32 -ladvapi32 -lws2_32 -luuid -lgdi32
|
||||
qtlib += -lmingw32 -lqtmain -lQtGui4 -lcomdlg32 -loleaut32 -limm32 -lwinmm
|
||||
qtlib += -lwinspool -lmsimg32 -lQtCore4 -lole32 -ladvapi32 -lws2_32 -luuid -lgdi32
|
||||
|
||||
# optional image-file support:
|
||||
# qtlib += -lqjpeg -lqmng
|
||||
|
|
|
@ -2,15 +2,13 @@
|
|||
#define NALL_BASE64_HPP
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <nall/new.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
class base64 {
|
||||
public:
|
||||
static bool encode(char *&output, const uint8_t* input, unsigned inlength) {
|
||||
output = new(zeromemory) char[inlength * 8 / 6 + 6];
|
||||
output = new char[inlength * 8 / 6 + 6]();
|
||||
|
||||
unsigned i = 0, o = 0;
|
||||
while(i < inlength) {
|
||||
|
@ -41,7 +39,7 @@ namespace nall {
|
|||
|
||||
static bool decode(uint8_t *&output, unsigned &outlength, const char *input) {
|
||||
unsigned inlength = strlen(input), infix = 0;
|
||||
output = new(zeromemory) uint8_t[inlength];
|
||||
output = new uint8_t[inlength]();
|
||||
|
||||
unsigned i = 0, o = 0;
|
||||
while(i < inlength) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
namespace nall {
|
||||
struct library : noncopyable {
|
||||
bool opened() const { return handle; }
|
||||
bool open(const char*);
|
||||
void* sym(const char*);
|
||||
void close();
|
||||
|
|
|
@ -15,6 +15,14 @@
|
|||
#include <nall/utility.hpp>
|
||||
|
||||
namespace nall {
|
||||
inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) {
|
||||
#if !defined(_WIN32)
|
||||
return fopen(utf8_filename, mode);
|
||||
#else
|
||||
return _wfopen(utf16_t(utf8_filename), utf16_t(mode));
|
||||
#endif
|
||||
}
|
||||
|
||||
class file : noncopyable {
|
||||
public:
|
||||
enum FileMode { mode_read, mode_write, mode_readwrite, mode_writeread };
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#ifndef NALL_MODULO_HPP
|
||||
#define NALL_MODULO_HPP
|
||||
|
||||
#include <nall/new.hpp>
|
||||
#include <nall/serializer.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
@ -26,7 +25,7 @@ namespace nall {
|
|||
}
|
||||
|
||||
modulo_array() {
|
||||
buffer = new(zeromemory) T[size * 3];
|
||||
buffer = new T[size * 3]();
|
||||
}
|
||||
|
||||
~modulo_array() {
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
#ifndef NALL_NEW_HPP
|
||||
#define NALL_NEW_HPP
|
||||
|
||||
#include <string.h>
|
||||
#include <new>
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
struct zeromemory_t {};
|
||||
static zeromemory_t zeromemory;
|
||||
}
|
||||
|
||||
inline void* operator new[](size_t size, const nall::zeromemory_t&) throw(std::bad_alloc) {
|
||||
void *p = new uint8_t[size];
|
||||
memset(p, 0, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
inline void* operator new[](size_t size, const std::nothrow_t&, const nall::zeromemory_t&) throw() {
|
||||
void *p = new(std::nothrow) uint8_t[size];
|
||||
if(p) memset(p, 0, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,7 +1,6 @@
|
|||
#ifndef NALL_SERIALIZER_HPP
|
||||
#define NALL_SERIALIZER_HPP
|
||||
|
||||
#include <nall/new.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/traits.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
@ -92,7 +91,7 @@ namespace nall {
|
|||
|
||||
serializer(unsigned capacity) {
|
||||
mode = Save;
|
||||
idata = new(zeromemory) uint8_t[capacity];
|
||||
idata = new uint8_t[capacity]();
|
||||
isize = 0;
|
||||
icapacity = capacity;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
#ifndef NALL_SHA256_HPP
|
||||
#define NALL_SHA256_HPP
|
||||
|
||||
//author: vladitx
|
||||
|
||||
namespace nall {
|
||||
#define PTR(t, a) ((t*)(a))
|
||||
|
||||
#define SWAP32(x) ((uint32_t)( \
|
||||
(((uint32_t)(x) & 0x000000ff) << 24) | \
|
||||
(((uint32_t)(x) & 0x0000ff00) << 8) | \
|
||||
(((uint32_t)(x) & 0x00ff0000) >> 8) | \
|
||||
(((uint32_t)(x) & 0xff000000) >> 24) \
|
||||
))
|
||||
|
||||
#define ST32(a, d) *PTR(uint32_t, a) = (d)
|
||||
#define ST32BE(a, d) ST32(a, SWAP32(d))
|
||||
|
||||
#define LD32(a) *PTR(uint32_t, a)
|
||||
#define LD32BE(a) SWAP32(LD32(a))
|
||||
|
||||
#define LSL32(x, n) ((uint32_t)(x) << (n))
|
||||
#define LSR32(x, n) ((uint32_t)(x) >> (n))
|
||||
#define ROR32(x, n) (LSR32(x, n) | LSL32(x, 32 - (n)))
|
||||
|
||||
//first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19
|
||||
static const uint32_t T_H[8] = {
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||
};
|
||||
|
||||
//first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311
|
||||
static const uint32_t T_K[64] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||
};
|
||||
|
||||
struct sha256_ctx {
|
||||
uint8_t in[64];
|
||||
unsigned inlen;
|
||||
|
||||
uint32_t w[64];
|
||||
uint32_t h[8];
|
||||
uint64_t len;
|
||||
};
|
||||
|
||||
void sha256_init(sha256_ctx *p) {
|
||||
memset(p, 0, sizeof(sha256_ctx));
|
||||
memcpy(p->h, T_H, sizeof(T_H));
|
||||
}
|
||||
|
||||
static void sha256_block(sha256_ctx *p) {
|
||||
unsigned i;
|
||||
uint32_t s0, s1;
|
||||
uint32_t a, b, c, d, e, f, g, h;
|
||||
uint32_t t1, t2, maj, ch;
|
||||
|
||||
for(i = 0; i < 16; i++) p->w[i] = LD32BE(p->in + i * 4);
|
||||
|
||||
for(i = 16; i < 64; i++) {
|
||||
s0 = ROR32(p->w[i - 15], 7) ^ ROR32(p->w[i - 15], 18) ^ LSR32(p->w[i - 15], 3);
|
||||
s1 = ROR32(p->w[i - 2], 17) ^ ROR32(p->w[i - 2], 19) ^ LSR32(p->w[i - 2], 10);
|
||||
p->w[i] = p->w[i - 16] + s0 + p->w[i - 7] + s1;
|
||||
}
|
||||
|
||||
a = p->h[0]; b = p->h[1]; c = p->h[2]; d = p->h[3];
|
||||
e = p->h[4]; f = p->h[5]; g = p->h[6]; h = p->h[7];
|
||||
|
||||
for(i = 0; i < 64; i++) {
|
||||
s0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22);
|
||||
maj = (a & b) ^ (a & c) ^ (b & c);
|
||||
t2 = s0 + maj;
|
||||
s1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25);
|
||||
ch = (e & f) ^ (~e & g);
|
||||
t1 = h + s1 + ch + T_K[i] + p->w[i];
|
||||
|
||||
h = g; g = f; f = e; e = d + t1;
|
||||
d = c; c = b; b = a; a = t1 + t2;
|
||||
}
|
||||
|
||||
p->h[0] += a; p->h[1] += b; p->h[2] += c; p->h[3] += d;
|
||||
p->h[4] += e; p->h[5] += f; p->h[6] += g; p->h[7] += h;
|
||||
|
||||
//next block
|
||||
p->inlen = 0;
|
||||
}
|
||||
|
||||
void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) {
|
||||
unsigned l;
|
||||
p->len += len;
|
||||
|
||||
while(len) {
|
||||
l = 64 - p->inlen;
|
||||
l = (len < l) ? len : l;
|
||||
|
||||
memcpy(p->in + p->inlen, s, l);
|
||||
s += l;
|
||||
p->inlen += l;
|
||||
len -= l;
|
||||
|
||||
if(p->inlen == 64) sha256_block(p);
|
||||
}
|
||||
}
|
||||
|
||||
void sha256_final(sha256_ctx *p) {
|
||||
uint64_t len;
|
||||
p->in[p->inlen++] = 0x80;
|
||||
|
||||
if(p->inlen > 56) {
|
||||
memset(p->in + p->inlen, 0, 64 - p->inlen);
|
||||
sha256_block(p);
|
||||
}
|
||||
|
||||
memset(p->in + p->inlen, 0, 56 - p->inlen);
|
||||
|
||||
len = p->len << 3;
|
||||
ST32BE(p->in + 56, len >> 32);
|
||||
ST32BE(p->in + 60, len);
|
||||
sha256_block(p);
|
||||
}
|
||||
|
||||
void sha256_hash(sha256_ctx *p, uint8_t *s) {
|
||||
uint32_t *t = (uint32_t*)s;
|
||||
for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]);
|
||||
}
|
||||
|
||||
#undef PTR
|
||||
#undef SWAP32
|
||||
#undef ST32
|
||||
#undef ST32BE
|
||||
#undef LD32
|
||||
#undef LD32BE
|
||||
#undef LSL32
|
||||
#undef LSR32
|
||||
#undef ROR32
|
||||
}
|
||||
|
||||
#endif
|
|
@ -6,6 +6,7 @@
|
|||
#include <nall/string/cast.hpp>
|
||||
#include <nall/string/compare.hpp>
|
||||
#include <nall/string/convert.hpp>
|
||||
#include <nall/string/filename.hpp>
|
||||
#include <nall/string/match.hpp>
|
||||
#include <nall/string/math.hpp>
|
||||
#include <nall/string/strl.hpp>
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef NALL_FILENAME_HPP
|
||||
#define NALL_FILENAME_HPP
|
||||
|
||||
namespace nall {
|
||||
// "foo/bar.c" -> "foo/", "bar.c" -> "./"
|
||||
inline string dir(char const *name) {
|
||||
string result = name;
|
||||
for(signed i = strlen(result); i >= 0; i--) {
|
||||
if(result[i] == '/' || result[i] == '\\') {
|
||||
result[i + 1] = 0;
|
||||
break;
|
||||
}
|
||||
if(i == 0) result = "./";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// "foo/bar.c" -> "bar.c"
|
||||
inline string notdir(char const *name) {
|
||||
for(signed i = strlen(name); i >= 0; i--) {
|
||||
if(name[i] == '/' || name[i] == '\\') {
|
||||
name += i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
string result = name;
|
||||
return result;
|
||||
}
|
||||
|
||||
// "foo/bar.c" -> "foo/bar"
|
||||
inline string basename(char const *name) {
|
||||
string result = name;
|
||||
for(signed i = strlen(result); i >= 0; i--) {
|
||||
if(result[i] == '.') {
|
||||
result[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -3,7 +3,14 @@
|
|||
|
||||
char* ltrim(char *str, const char *key) {
|
||||
if(!key || !*key) return str;
|
||||
while(strbegin(str, key)) strcpy(str, str + strlen(key));
|
||||
while(strbegin(str, key)) {
|
||||
char *dest = str, *src = str + strlen(key);
|
||||
while(true) {
|
||||
*dest = *src++;
|
||||
if(!*dest) break;
|
||||
dest++;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
@ -19,7 +26,14 @@ char* trim(char *str, const char *key) {
|
|||
|
||||
char* ltrim_once(char *str, const char *key) {
|
||||
if(!key || !*key) return str;
|
||||
if(strbegin(str, key)) strcpy(str, str + strlen(key));
|
||||
if(strbegin(str, key)) {
|
||||
char *dest = str, *src = str + strlen(key);
|
||||
while(true) {
|
||||
*dest = *src++;
|
||||
if(!*dest) break;
|
||||
dest++;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include <nall/algorithm.hpp>
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/new.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
@ -98,7 +97,7 @@ namespace nall {
|
|||
//mirror
|
||||
if(x_size != px_size && x_size != py_size) return input_invalid;
|
||||
y_size = (x_size == px_size) ? py_size : px_size;
|
||||
y_data = new(zeromemory) uint8_t[y_size];
|
||||
y_data = new uint8_t[y_size]();
|
||||
|
||||
for(unsigned i = 0; i < x_size && i < y_size; i++) y_data[i] = x_data[i];
|
||||
for(unsigned i = x_size; i < y_size; i++) y_data[i] = 0x00;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef NALL_UTF8_HPP
|
||||
#define NALL_UTF8_HPP
|
||||
|
||||
#include <nall/new.hpp>
|
||||
|
||||
//UTF-8 <> UTF-16 conversion
|
||||
//used only for Win32; Linux, etc use UTF-8 internally
|
||||
|
||||
|
@ -30,7 +28,7 @@ namespace nall {
|
|||
utf16_t(const char *s = "") {
|
||||
if(!s) s = "";
|
||||
unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0);
|
||||
buffer = new(zeromemory) wchar_t[length + 1];
|
||||
buffer = new wchar_t[length + 1]();
|
||||
MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length);
|
||||
}
|
||||
|
||||
|
@ -56,7 +54,7 @@ namespace nall {
|
|||
utf8_t(const wchar_t *s = L"") {
|
||||
if(!s) s = L"";
|
||||
unsigned length = WideCharToMultiByte(CP_UTF8, 0, s, -1, 0, 0, (const char*)0, (BOOL*)0);
|
||||
buffer = new(zeromemory) char[length + 1];
|
||||
buffer = new char[length + 1]();
|
||||
WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,13 @@ namespace nall {
|
|||
noncopyable(const noncopyable&);
|
||||
const noncopyable& operator=(const noncopyable&);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline T* allocate(size_t size, const T &value) {
|
||||
T *array = new T[size];
|
||||
for(size_t i = 0; i < size; i++) array[i] = value;
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <nall/bit.hpp>
|
||||
#include <nall/detect.hpp>
|
||||
#include <nall/input.hpp>
|
||||
#include <nall/new.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
#ifdef __APPLE__
|
||||
#include <OpenGL/OpenGL.h>
|
||||
#endif
|
||||
|
||||
namespace ruby {
|
||||
|
||||
class pVideoQtOpenGL {
|
||||
|
@ -13,6 +17,7 @@ public:
|
|||
uint32_t *buffer;
|
||||
unsigned rasterWidth, rasterHeight;
|
||||
|
||||
bool synchronize;
|
||||
unsigned filter;
|
||||
|
||||
void resize(unsigned width, unsigned height) {
|
||||
|
@ -35,6 +40,15 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void updateSynchronization() {
|
||||
#ifdef __APPLE__
|
||||
makeCurrent();
|
||||
CGLContextObj context = CGLGetCurrentContext();
|
||||
GLint value = synchronize; //0 = draw immediately (no vsync), 1 = draw once per frame (vsync)
|
||||
CGLSetParameter(context, kCGLCPSwapInterval, &value);
|
||||
#endif
|
||||
}
|
||||
|
||||
void paintGL() {
|
||||
unsigned outputWidth = width();
|
||||
unsigned outputHeight = height();
|
||||
|
@ -85,18 +99,26 @@ public:
|
|||
} *widget;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Video::Synchronize) return true;
|
||||
if(name == Video::Filter) return true;
|
||||
if(name == "QWidget") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Video::Synchronize) return widget->synchronize;
|
||||
if(name == Video::Filter) return widget->filter;
|
||||
if(name == "QWidget") return parent;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Video::Synchronize) {
|
||||
widget->synchronize = any_cast<bool>(value);
|
||||
widget->updateSynchronization();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::Filter) {
|
||||
widget->filter = any_cast<unsigned>(value);
|
||||
return true;
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
rm -r libco
|
||||
rm -r nall
|
||||
rm -r ruby
|
||||
|
||||
cp -r ../../../libco ./libco
|
||||
cp -r ../../../nall ./nall
|
||||
cp -r ../../../ruby ./ruby
|
||||
|
||||
rm -r libco/doc
|
||||
rm -r libco/test
|
||||
rm ruby/test*
|
||||
rm ruby/cc.*
|
||||
|
|
|
@ -35,7 +35,7 @@ void MappedRAM::map(uint8 *source, unsigned length) {
|
|||
void MappedRAM::copy(uint8 *data, unsigned size) {
|
||||
if(!data_) {
|
||||
size_ = (size & ~255) + ((bool)(size & 255) << 8);
|
||||
data_ = new(zeromemory) uint8[size_];
|
||||
data_ = new uint8[size_]();
|
||||
}
|
||||
memcpy(data_, data, min(size_, size));
|
||||
}
|
||||
|
|
|
@ -120,12 +120,12 @@ void bPPU::flush_pixel_cache() {
|
|||
}
|
||||
|
||||
void bPPU::alloc_tiledata_cache() {
|
||||
bg_tiledata[TILE_2BIT] = new(zeromemory) uint8_t[262144];
|
||||
bg_tiledata[TILE_4BIT] = new(zeromemory) uint8_t[131072];
|
||||
bg_tiledata[TILE_8BIT] = new(zeromemory) uint8_t[ 65536];
|
||||
bg_tiledata_state[TILE_2BIT] = new(zeromemory) uint8_t[ 4096];
|
||||
bg_tiledata_state[TILE_4BIT] = new(zeromemory) uint8_t[ 2048];
|
||||
bg_tiledata_state[TILE_8BIT] = new(zeromemory) uint8_t[ 1024];
|
||||
bg_tiledata[TILE_2BIT] = new uint8_t[262144]();
|
||||
bg_tiledata[TILE_4BIT] = new uint8_t[131072]();
|
||||
bg_tiledata[TILE_8BIT] = new uint8_t[ 65536]();
|
||||
bg_tiledata_state[TILE_2BIT] = new uint8_t[ 4096]();
|
||||
bg_tiledata_state[TILE_4BIT] = new uint8_t[ 2048]();
|
||||
bg_tiledata_state[TILE_8BIT] = new uint8_t[ 1024]();
|
||||
}
|
||||
|
||||
//marks all tiledata cache entries as dirty
|
||||
|
|
|
@ -37,7 +37,7 @@ void PPU::reset() {
|
|||
}
|
||||
|
||||
PPU::PPU() {
|
||||
output = new(zeromemory) uint16[512 * 480];
|
||||
output = new uint16[512 * 480];
|
||||
|
||||
status.render_output = true;
|
||||
status.frames_updated = false;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
objects := main $(if $(call streq,$(platform),win),resource) $(objects)
|
||||
objects := ui-main $(objects)
|
||||
objects += $(if $(call streq,$(platform),win),resource)
|
||||
link += $(qtlib)
|
||||
|
||||
moc_headers := $(call rwildcard,$(ui)/,%.moc.hpp)
|
||||
|
@ -12,7 +13,7 @@ moc_objects := $(patsubst %.moc.hpp,%.moc,$(moc_headers))
|
|||
%.moc: $<; $(moc) -f $< -o $@
|
||||
$(foreach f,$(moc_objects),$(eval $f: $(patsubst %.moc,%.moc.hpp,$f)))
|
||||
|
||||
obj/main.o: $(ui)/main.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-main.o: $(ui)/main.cpp $(call rwildcard,$(ui)/)
|
||||
$(call compile,$(qtinc))
|
||||
|
||||
$(ui)/resource/resource.rcc: $(ui)/resource/resource.qrc data/*
|
||||
|
|
|
@ -1,27 +1,5 @@
|
|||
#include "init.cpp"
|
||||
|
||||
const char* FileReader::direct_supported() {
|
||||
return "";
|
||||
}
|
||||
|
||||
bool FileReader::direct_load(const char *filename, uint8_t **outdata, unsigned *outsize) {
|
||||
if(file::exists(filename) == false) return false;
|
||||
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode_read) == false) return false;
|
||||
|
||||
unsigned size;
|
||||
uint8_t *data = new uint8_t[size = fp.size()];
|
||||
fp.read(data, size);
|
||||
fp.close();
|
||||
|
||||
//remove copier header, if it exists
|
||||
if((size & 0x7fff) == 512) memmove(data, data + 512, size -= 512);
|
||||
|
||||
*outdata = data;
|
||||
*outsize = size;
|
||||
return true;
|
||||
}
|
||||
#include "qb.cpp"
|
||||
|
||||
void Application::initPaths(const char *basename) {
|
||||
char temp[PATH_MAX];
|
||||
|
@ -94,7 +72,7 @@ int Application::main(int &argc, char **argv) {
|
|||
|
||||
if(argc == 2) {
|
||||
//if valid file was specified on the command-line, attempt to load it now
|
||||
utility.loadCartridge(argv[1]);
|
||||
utility.loadCartridgeNormal(argv[1]);
|
||||
}
|
||||
|
||||
timer = new QTimer(this);
|
||||
|
@ -102,6 +80,22 @@ int Application::main(int &argc, char **argv) {
|
|||
timer->start(0);
|
||||
app->exec();
|
||||
|
||||
//QbWindow::hide() saves window geometry for next run
|
||||
loaderWindow->window->hide();
|
||||
htmlViewerWindow->window->hide();
|
||||
aboutWindow->window->hide();
|
||||
diskBrowser->window->hide();
|
||||
folderCreator->window->hide();
|
||||
settingsWindow->window->hide();
|
||||
inputCaptureWindow->window->hide();
|
||||
inputMouseCaptureWindow->window->hide();
|
||||
inputCalibrationWindow->window->hide();
|
||||
toolsWindow->window->hide();
|
||||
debugger->window->hide();
|
||||
breakpointEditor->window->hide();
|
||||
memoryEditor->window->hide();
|
||||
vramViewer->window->hide();
|
||||
|
||||
utility.unloadCartridge();
|
||||
config.save(configFilename);
|
||||
return 0;
|
||||
|
|
|
@ -1,14 +1,4 @@
|
|||
struct FileReader : public library {
|
||||
string filterList;
|
||||
|
||||
function<const char* ()> supported;
|
||||
function<bool (const char*, uint8_t**, unsigned*)> load;
|
||||
|
||||
const char* direct_supported();
|
||||
bool direct_load(const char*, uint8_t**, unsigned*);
|
||||
|
||||
FileReader();
|
||||
} libsnesreader;
|
||||
#include "qb.hpp"
|
||||
|
||||
class Application : public QObject {
|
||||
Q_OBJECT
|
||||
|
|
|
@ -16,27 +16,6 @@
|
|||
#include "../settings/settings.cpp"
|
||||
#include "../tools/tools.cpp"
|
||||
|
||||
FileReader::FileReader() {
|
||||
if(open("snesreader")) {
|
||||
supported = sym("snesreader_supported");
|
||||
load = sym("snesreader_load");
|
||||
}
|
||||
|
||||
if(!supported || !load) {
|
||||
supported = bind(&FileReader::direct_supported, this);
|
||||
load = bind(&FileReader::direct_load, this);
|
||||
}
|
||||
|
||||
filterList = supported();
|
||||
if(filterList.length() > 0) {
|
||||
filterList = string()
|
||||
<< " *.swc *.fig *.ufo *.gd3 *.gd7 *.dx2 *.mgd *.mgh"
|
||||
<< " *.048 *.058 *.068 *.078 *.bin"
|
||||
<< " *.usa *.eur *.jap *.aus *.bsx"
|
||||
<< " " << filterList;
|
||||
}
|
||||
}
|
||||
|
||||
void Application::init() {
|
||||
if(config.system.crashedOnLastRun == true) {
|
||||
//emulator crashed on last run, disable all drivers
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
void QbWindow::shrink() {
|
||||
for(unsigned i = 0; i < 2; i++) {
|
||||
resize(0, 0);
|
||||
usleep(2000);
|
||||
QApplication::processEvents();
|
||||
}
|
||||
}
|
||||
|
||||
void QbWindow::show() {
|
||||
if(isVisible() == false) {
|
||||
uint8_t *data;
|
||||
unsigned length;
|
||||
base64::decode(data, length, geometryString);
|
||||
QByteArray array((const char*)data, length);
|
||||
delete[] data;
|
||||
restoreGeometry(array);
|
||||
QWidget::show();
|
||||
}
|
||||
|
||||
QApplication::processEvents();
|
||||
activateWindow();
|
||||
raise();
|
||||
}
|
||||
|
||||
void QbWindow::hide() {
|
||||
if(isVisible() == true) {
|
||||
char *data;
|
||||
QByteArray geometry = saveGeometry();
|
||||
base64::encode(data, (const uint8_t*)geometry.data(), geometry.length());
|
||||
geometryString = data;
|
||||
delete[] data;
|
||||
QWidget::hide();
|
||||
}
|
||||
}
|
||||
|
||||
void QbWindow::closeEvent(QCloseEvent *event) {
|
||||
char *data;
|
||||
QByteArray geometry = saveGeometry();
|
||||
base64::encode(data, (const uint8_t*)geometry.data(), geometry.length());
|
||||
geometryString = data;
|
||||
delete[] data;
|
||||
QWidget::hide();
|
||||
}
|
||||
|
||||
QbWindow::QbWindow(string &geometryString_) : geometryString(geometryString_) {
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
bool QbCheckAction::isChecked() const {
|
||||
return checked;
|
||||
}
|
||||
|
||||
void QbCheckAction::setChecked(bool checked_) {
|
||||
checked = checked_;
|
||||
if(checked) setIcon(QIcon(":/16x16/item-check-on.png"));
|
||||
else setIcon(QIcon(":/16x16/item-check-off.png"));
|
||||
}
|
||||
|
||||
void QbCheckAction::toggleChecked() {
|
||||
setChecked(!isChecked());
|
||||
}
|
||||
|
||||
QbCheckAction::QbCheckAction(const QString &text, QObject *parent) : QAction(text, parent) {
|
||||
setChecked(false);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
bool QbRadioAction::isChecked() const {
|
||||
return checked;
|
||||
}
|
||||
|
||||
void QbRadioAction::setChecked(bool checked_) {
|
||||
checked = checked_;
|
||||
if(checked) setIcon(QIcon(":/16x16/item-radio-on.png"));
|
||||
else setIcon(QIcon(":/16x16/item-radio-off.png"));
|
||||
}
|
||||
|
||||
void QbRadioAction::toggleChecked() {
|
||||
setChecked(!isChecked());
|
||||
}
|
||||
|
||||
QbRadioAction::QbRadioAction(const QString &text, QObject *parent) : QAction(text, parent) {
|
||||
setChecked(false);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
class QbWindow : public QWidget {
|
||||
public:
|
||||
void shrink();
|
||||
void show();
|
||||
void hide();
|
||||
void closeEvent(QCloseEvent*);
|
||||
QbWindow(string&);
|
||||
|
||||
private:
|
||||
string &geometryString;
|
||||
};
|
||||
|
||||
class QbCheckAction : public QAction {
|
||||
public:
|
||||
bool isChecked() const;
|
||||
void setChecked(bool);
|
||||
void toggleChecked();
|
||||
QbCheckAction(const QString&, QObject*);
|
||||
|
||||
private:
|
||||
bool checked;
|
||||
};
|
||||
|
||||
class QbRadioAction : public QAction {
|
||||
public:
|
||||
bool isChecked() const;
|
||||
void setChecked(bool);
|
||||
void toggleChecked();
|
||||
QbRadioAction(const QString&, QObject*);
|
||||
|
||||
private:
|
||||
bool checked;
|
||||
};
|
|
@ -1,26 +1,26 @@
|
|||
AboutWindow::AboutWindow() {
|
||||
setObjectName("about-window");
|
||||
setWindowTitle("About bsnes ...");
|
||||
window = new QbWindow(config.geometry.aboutWindow);
|
||||
window->setObjectName("about-window");
|
||||
window->setWindowTitle("About bsnes ...");
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(Style::WidgetSpacing); {
|
||||
logo = new Logo;
|
||||
logo->setFixedSize(600, 106);
|
||||
layout->addWidget(logo);
|
||||
layout->setSpacing(Style::WidgetSpacing);
|
||||
window->setLayout(layout);
|
||||
|
||||
info = new QLabel(utf8() <<
|
||||
"<table>"
|
||||
"<tr><td align='right'><b>Version: </b></td><td>" << bsnesVersion << "</td></tr>"
|
||||
"<tr><td align='right'><b>Author: </b></td><td>byuu</td></tr>"
|
||||
"<tr><td align='right'><b>Homepage: </b></td><td>http://byuu.org/</td></tr>"
|
||||
"</table>"
|
||||
);
|
||||
layout->addWidget(info);
|
||||
}
|
||||
logo = new Logo;
|
||||
logo->setFixedSize(600, 106);
|
||||
layout->addWidget(logo);
|
||||
|
||||
setLayout(layout);
|
||||
info = new QLabel(utf8() <<
|
||||
"<table>"
|
||||
"<tr><td align='right'><b>Version: </b></td><td>" << bsnesVersion << "</td></tr>"
|
||||
"<tr><td align='right'><b>Author: </b></td><td>byuu</td></tr>"
|
||||
"<tr><td align='right'><b>Homepage: </b></td><td>http://byuu.org/</td></tr>"
|
||||
"</table>"
|
||||
);
|
||||
layout->addWidget(info);
|
||||
}
|
||||
|
||||
void AboutWindow::Logo::paintEvent(QPaintEvent*) {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
class AboutWindow : public QbWindow {
|
||||
class AboutWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QbWindow *window;
|
||||
QVBoxLayout *layout;
|
||||
struct Logo : public QWidget {
|
||||
void paintEvent(QPaintEvent*);
|
||||
|
|
|
@ -1,211 +1,474 @@
|
|||
void DiskBrowser::ScreenshotPreview::paintEvent(QPaintEvent*) {
|
||||
//=============
|
||||
//FolderCreator
|
||||
//=============
|
||||
|
||||
FolderCreator::FolderCreator() {
|
||||
window = new QbWindow(config.geometry.folderCreator);
|
||||
window->setObjectName("folder-creator");
|
||||
window->setWindowTitle("Create New Folder");
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(Style::WidgetSpacing);
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
window->setLayout(layout);
|
||||
|
||||
label = new QLabel("Folder name:");
|
||||
layout->addWidget(label);
|
||||
|
||||
name = new QLineEdit;
|
||||
layout->addWidget(name);
|
||||
|
||||
controlLayout = new QHBoxLayout;
|
||||
controlLayout->setAlignment(Qt::AlignRight);
|
||||
layout->addLayout(controlLayout);
|
||||
|
||||
ok = new QPushButton("Ok");
|
||||
controlLayout->addWidget(ok);
|
||||
|
||||
cancel = new QPushButton("Cancel");
|
||||
controlLayout->addWidget(cancel);
|
||||
|
||||
connect(name, SIGNAL(returnPressed()), this, SLOT(createFolder()));
|
||||
connect(ok, SIGNAL(released()), this, SLOT(createFolder()));
|
||||
connect(cancel, SIGNAL(released()), window, SLOT(hide()));
|
||||
}
|
||||
|
||||
void FolderCreator::show() {
|
||||
name->setText("");
|
||||
window->show();
|
||||
name->setFocus();
|
||||
}
|
||||
|
||||
void FolderCreator::createFolder() {
|
||||
if(name->text().length() == 0) {
|
||||
QMessageBox::warning(0, "Create New Folder", "<b>Note:</b> you must enter a folder name.");
|
||||
} else {
|
||||
string folderName = string()
|
||||
<< diskBrowser->path->currentText().toUtf8().constData()
|
||||
<< "/"
|
||||
<< name->text().toUtf8().constData();
|
||||
|
||||
if(mkdir(folderName) == 0) {
|
||||
window->hide();
|
||||
} else {
|
||||
QMessageBox::warning(0, "Create new Folder", "<b>Error:</b> failed to create folder. Please ensure only valid characters were used in the folder name.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===============
|
||||
//DiskBrowserView
|
||||
//===============
|
||||
|
||||
void DiskBrowserView::keyPressEvent(QKeyEvent *event) {
|
||||
//enhance consistency: OS X by default doesn't activate items for these keypresses
|
||||
if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
|
||||
emit activated(currentIndex());
|
||||
return;
|
||||
}
|
||||
|
||||
//simulate popular file managers; backspace = go up one directory
|
||||
if(event->key() == Qt::Key_Backspace) {
|
||||
emit cdUp();
|
||||
return;
|
||||
}
|
||||
|
||||
//fallback: unrecognized keypresses get handled by the widget itself
|
||||
QTreeView::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void DiskBrowserView::keyReleaseEvent(QKeyEvent *event) {
|
||||
//act like a modal dialog; close window on escape keypress
|
||||
if(event->key() == Qt::Key_Escape) {
|
||||
emit escape();
|
||||
return;
|
||||
}
|
||||
|
||||
//fallback: unrecognized keypresses get handled by the widget itself
|
||||
QTreeView::keyReleaseEvent(event);
|
||||
}
|
||||
|
||||
void DiskBrowserView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) {
|
||||
QAbstractItemView::currentChanged(current, previous);
|
||||
emit changed(current);
|
||||
}
|
||||
|
||||
//================
|
||||
//DiskBrowserImage
|
||||
//================
|
||||
|
||||
void DiskBrowserImage::paintEvent(QPaintEvent*) {
|
||||
QPainter painter(this);
|
||||
if(screenshotName != "") {
|
||||
QImage image(screenshotName);
|
||||
if(name != "") {
|
||||
QImage image(name);
|
||||
painter.drawImage(0, 0, image);
|
||||
}
|
||||
}
|
||||
|
||||
void DiskBrowser::onActivate(const QString &file, bool isDir) {
|
||||
if(browseMode == Folder) {
|
||||
setPath(file);
|
||||
return;
|
||||
}
|
||||
|
||||
if(isDir) {
|
||||
QDir directory(file);
|
||||
directory.setNameFilters(QStringList() << "*.sfc" << "*.smc");
|
||||
QStringList list = directory.entryList(QDir::Files | QDir::NoDotAndDotDot);
|
||||
if(list.count() == 1) {
|
||||
imageName = string() << file.toUtf8().constData() << "/" << list[0].toUtf8().constData();
|
||||
onLoad();
|
||||
} else {
|
||||
setPath(file);
|
||||
}
|
||||
} else {
|
||||
imageName = file.toUtf8().constData();
|
||||
onLoad();
|
||||
}
|
||||
}
|
||||
|
||||
void DiskBrowser::onChange(const QString &file, bool isDir) {
|
||||
if(browseMode == Folder) {
|
||||
imageName = file.toUtf8().constData();
|
||||
load->setEnabled(isDir);
|
||||
applyPatch->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
imageName = queryAbsoluteImageName(file, isDir);
|
||||
if(imageName.length() == 0) {
|
||||
load->setEnabled(false);
|
||||
screenshot->screenshotName = "";
|
||||
info->setText("");
|
||||
applyPatch->setVisible(false);
|
||||
} else {
|
||||
load->setEnabled(true);
|
||||
screenshot->screenshotName = string() << utility.basepath(imageName) << "/" << utility.basename(imageName) << ".png";
|
||||
if(file::exists(screenshot->screenshotName) == false) screenshot->screenshotName = "";
|
||||
|
||||
info->setText(utf8() << queryImageInformation(imageName));
|
||||
string patch = string() << utility.basepath(imageName) << utility.basename(imageName) << ".ups";
|
||||
applyPatch->setVisible(file::exists(patch));
|
||||
}
|
||||
screenshot->update();
|
||||
}
|
||||
|
||||
void DiskBrowser::onLoad() {
|
||||
hide();
|
||||
switch(browseMode) { default:
|
||||
case Folder: activePath->selectPath(imageName); break;
|
||||
case AnyCartridge: utility.loadCartridge(imageName); break;
|
||||
case BaseCartridge: loaderWindow->selectBaseCartridge(imageName); break;
|
||||
case BsxCartridge: loaderWindow->selectSlot1Cartridge(imageName); break;
|
||||
case SufamiTurboCartridge1: loaderWindow->selectSlot1Cartridge(imageName); break;
|
||||
case SufamiTurboCartridge2: loaderWindow->selectSlot2Cartridge(imageName); break;
|
||||
case SuperGameBoyCartridge: loaderWindow->selectSlot1Cartridge(imageName); break;
|
||||
}
|
||||
}
|
||||
//===========
|
||||
//DiskBrowser
|
||||
//===========
|
||||
|
||||
void DiskBrowser::chooseFolder(PathSettingWidget *widget, const char *title) {
|
||||
browseMode = Folder;
|
||||
activePath = widget;
|
||||
|
||||
hide();
|
||||
panel()->hide();
|
||||
acceptButton()->setText("Choose");
|
||||
setWindowTitle(utf8() << title);
|
||||
window->hide();
|
||||
group->hide();
|
||||
ok->setText("Choose");
|
||||
window->setWindowTitle(utf8() << title);
|
||||
setPath(utf8() << (config.path.rom != "" ? config.path.rom : config.path.current));
|
||||
setNameFilters("Folders ()");
|
||||
showAt(0.0, 0.0);
|
||||
window->show();
|
||||
}
|
||||
|
||||
void DiskBrowser::loadAnyCartridge() {
|
||||
browseMode = AnyCartridge;
|
||||
|
||||
hide();
|
||||
panel()->show();
|
||||
acceptButton()->setText("Load");
|
||||
setWindowTitle("Load Cartridge");
|
||||
void DiskBrowser::loadCartridge() {
|
||||
browseMode = Cartridge;
|
||||
window->hide();
|
||||
group->show();
|
||||
ok->setText("Load");
|
||||
window->setWindowTitle("Load Cartridge");
|
||||
setPath(utf8() << (config.path.rom != "" ? config.path.rom : config.path.current));
|
||||
setNameFilters(utf8() << "All cartridges (*.sfc *.smc *.bs *.st" << libsnesreader.filterList << ");;All files (*)");
|
||||
showAt(0.0, 0.0);
|
||||
setNameFilters(utf8() << "SNES cartridges (*.sfc *.smc" << reader.filterList << ");;All files (*)");
|
||||
window->show();
|
||||
}
|
||||
|
||||
void DiskBrowser::loadBaseCartridge() {
|
||||
browseMode = BaseCartridge;
|
||||
|
||||
hide();
|
||||
panel()->show();
|
||||
acceptButton()->setText("Load");
|
||||
setWindowTitle("Load Base Cartridge");
|
||||
window->hide();
|
||||
group->show();
|
||||
ok->setText("Load");
|
||||
window->setWindowTitle("Load Base Cartridge");
|
||||
setPath(utf8() << (config.path.rom != "" ? config.path.rom : config.path.current));
|
||||
setNameFilters(utf8() << "SNES cartridges (*.sfc *.smc" << libsnesreader.filterList << ");;All files (*)");
|
||||
showAt(0.0, 0.0);
|
||||
setNameFilters(utf8() << "SNES cartridges (*.sfc *.smc" << reader.filterList << ");;All files (*)");
|
||||
window->show();
|
||||
}
|
||||
|
||||
void DiskBrowser::loadBsxCartridge() {
|
||||
browseMode = BsxCartridge;
|
||||
|
||||
hide();
|
||||
panel()->show();
|
||||
acceptButton()->setText("Load");
|
||||
setWindowTitle("Load BS-X Cartridge");
|
||||
window->hide();
|
||||
group->show();
|
||||
ok->setText("Load");
|
||||
window->setWindowTitle("Load BS-X Cartridge");
|
||||
setPath(utf8() << (config.path.rom != "" ? config.path.rom : config.path.current));
|
||||
setNameFilters(utf8() << "BS-X cartridges (*.bs" << libsnesreader.filterList << ");;All files (*)");
|
||||
showAt(0.0, 0.0);
|
||||
setNameFilters(utf8() << "BS-X cartridges (*.bs" << reader.filterList << ");;All files (*)");
|
||||
window->show();
|
||||
}
|
||||
|
||||
void DiskBrowser::loadSufamiTurboCartridge1() {
|
||||
browseMode = SufamiTurboCartridge1;
|
||||
|
||||
hide();
|
||||
panel()->show();
|
||||
acceptButton()->setText("Load");
|
||||
setWindowTitle("Load Slot-A Sufami Turbo Cartridge");
|
||||
window->hide();
|
||||
group->show();
|
||||
ok->setText("Load");
|
||||
window->setWindowTitle("Load Slot-A Sufami Turbo Cartridge");
|
||||
setPath(utf8() << (config.path.rom != "" ? config.path.rom : config.path.current));
|
||||
setNameFilters(utf8() << "Sufami Turbo cartridges (*.st" << libsnesreader.filterList << ");;All files (*)");
|
||||
showAt(0.0, 0.0);
|
||||
setNameFilters(utf8() << "Sufami Turbo cartridges (*.st" << reader.filterList << ");;All files (*)");
|
||||
window->show();
|
||||
}
|
||||
|
||||
void DiskBrowser::loadSufamiTurboCartridge2() {
|
||||
browseMode = SufamiTurboCartridge2;
|
||||
|
||||
hide();
|
||||
panel()->show();
|
||||
acceptButton()->setText("Load");
|
||||
setWindowTitle("Load Slot-B Sufami Turbo Cartridge");
|
||||
window->hide();
|
||||
group->show();
|
||||
ok->setText("Load");
|
||||
window->setWindowTitle("Load Slot-B Sufami Turbo Cartridge");
|
||||
setPath(utf8() << (config.path.rom != "" ? config.path.rom : config.path.current));
|
||||
setNameFilters(utf8() << "Sufami Turbo Cartridges (*.st" << libsnesreader.filterList << ");;All files (*)");
|
||||
showAt(0.0, 0.0);
|
||||
setNameFilters(utf8() << "Sufami Turbo Cartridges (*.st" << reader.filterList << ");;All files (*)");
|
||||
window->show();
|
||||
}
|
||||
|
||||
void DiskBrowser::loadSuperGameBoyCartridge() {
|
||||
browseMode = SuperGameBoyCartridge;
|
||||
|
||||
hide();
|
||||
panel()->show();
|
||||
acceptButton()->setText("Load");
|
||||
setWindowTitle("Load Super Game Boy Cartridge");
|
||||
window->hide();
|
||||
group->show();
|
||||
ok->setText("Load");
|
||||
window->setWindowTitle("Load Super Game Boy Cartridge");
|
||||
setPath(utf8() << (config.path.rom != "" ? config.path.rom : config.path.current));
|
||||
setNameFilters(utf8() << "Game Boy cartridges (*.gb *.gbc" << libsnesreader.filterList << ");;All files (*)");
|
||||
showAt(0.0, 0.0);
|
||||
setNameFilters(utf8() << "Game Boy cartridges (*.gb *.gbc" << reader.filterList << ");;All files (*)");
|
||||
window->show();
|
||||
}
|
||||
|
||||
string DiskBrowser::queryAbsoluteImageName(const QString &file, bool isDir) {
|
||||
if(isDir) {
|
||||
QDir directory(file);
|
||||
directory.setNameFilters(QStringList() << "*.sfc" << "*.smc");
|
||||
QStringList list = directory.entryList(QDir::Files | QDir::NoDotAndDotDot);
|
||||
if(list.count() == 1) {
|
||||
return string() << file.toUtf8().constData() << "/" << list[0].toUtf8().constData();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
return file.toUtf8().constData();
|
||||
}
|
||||
}
|
||||
|
||||
string DiskBrowser::queryImageInformation(const char *filename) {
|
||||
string DiskBrowser::queryImageInformation() {
|
||||
string text;
|
||||
unsigned size = file::size(filename);
|
||||
text << "<b>" << utility.basename(imageName) << "</b>";
|
||||
text << "<small><table cellspacing='5'>";
|
||||
if(striend(filename, ".sfc") || striend(filename, ".smc")) {
|
||||
text << "<tr><td><b>ROM size:</b></td><td>" << size * 8 / 1024 / 1024 << "mbit</td></tr>";
|
||||
} else {
|
||||
text << "<tr><td><b>File size:</b></td><td>" << size * 8 / 1024 / 1024 << "mbit</td></tr>";
|
||||
string filename;
|
||||
if(currentFilename(filename) == true) {
|
||||
if(striend(filename, ".sfc") || striend(filename, ".smc")) {
|
||||
unsigned size = file::size(filename);
|
||||
text << "<b>" << notdir(nall::basename(filename)) << "</b>";
|
||||
text << "<small><table cellspacing='5'>";
|
||||
text << "<tr><td><b>ROM size:</b></td><td>" << size * 8 / 1024 / 1024 << "mbit</td></tr>";
|
||||
text << "</table></small>";
|
||||
}
|
||||
}
|
||||
text << "</table></small>";
|
||||
return text;
|
||||
}
|
||||
|
||||
void DiskBrowser::activateItem(const QModelIndex &item) {
|
||||
if(browseMode == Folder) {
|
||||
setPath(model->filePath(item));
|
||||
} else {
|
||||
loadSelected();
|
||||
}
|
||||
}
|
||||
|
||||
void DiskBrowser::changeItem(const QModelIndex &item) {
|
||||
if(browseMode == Folder) {
|
||||
ok->setEnabled(model->isDir(item));
|
||||
image->name = "";
|
||||
info->setText("");
|
||||
applyPatch->setVisible(false);
|
||||
} else {
|
||||
string filename;
|
||||
currentFilename(filename);
|
||||
|
||||
if(filename.length() == 0) {
|
||||
//nothing selected?
|
||||
ok->setEnabled(false);
|
||||
image->name = "";
|
||||
info->setText("");
|
||||
applyPatch->setVisible(false);
|
||||
} else {
|
||||
ok->setEnabled(true);
|
||||
image->name = nall::basename(filename) << ".png";
|
||||
if(file::exists(image->name) == false) image->name = "";
|
||||
info->setText(utf8() << queryImageInformation());
|
||||
string patch = nall::basename(filename) << ".ups";
|
||||
applyPatch->setVisible(file::exists(patch));
|
||||
}
|
||||
}
|
||||
|
||||
image->update();
|
||||
}
|
||||
|
||||
void DiskBrowser::loadSelected() {
|
||||
string filename;
|
||||
bool loadable = currentFilename(filename);
|
||||
|
||||
if(browseMode == Folder || loadable == true) {
|
||||
QModelIndex item = view->currentIndex();
|
||||
config.path.current = dir(model->filePath(item).toUtf8().constData());
|
||||
window->hide();
|
||||
|
||||
switch(browseMode) { default:
|
||||
case Folder: activePath->selectPath(filename); break;
|
||||
case Cartridge: utility.loadCartridgeNormal(filename); break;
|
||||
case BaseCartridge: loaderWindow->selectBaseCartridge(filename); break;
|
||||
case BsxCartridge: loaderWindow->selectSlot1Cartridge(filename); break;
|
||||
case SufamiTurboCartridge1: loaderWindow->selectSlot1Cartridge(filename); break;
|
||||
case SufamiTurboCartridge2: loaderWindow->selectSlot2Cartridge(filename); break;
|
||||
case SuperGameBoyCartridge: loaderWindow->selectSlot1Cartridge(filename); break;
|
||||
}
|
||||
} else {
|
||||
//this is a standard folder in ROM loading mode; enter into the folder
|
||||
QModelIndex item = view->currentIndex();
|
||||
setPath(model->filePath(item));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void DiskBrowser::setPath(const QString &reqPath) {
|
||||
disconnect(path, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePath()));
|
||||
|
||||
QString effectivePath = reqPath;
|
||||
if(effectivePath == "<root>") {
|
||||
effectivePath = "";
|
||||
newFolder->setEnabled(false);
|
||||
} else {
|
||||
newFolder->setEnabled(true);
|
||||
}
|
||||
path->clear();
|
||||
model->setRootPath(effectivePath);
|
||||
view->setRootIndex(model->index(effectivePath));
|
||||
view->setCurrentIndex(view->rootIndex());
|
||||
view->setFocus();
|
||||
|
||||
if(effectivePath.length() > 0) {
|
||||
QDir directory(effectivePath);
|
||||
while(true) {
|
||||
path->addItem(directory.absolutePath());
|
||||
if(directory.isRoot()) break;
|
||||
directory.cdUp();
|
||||
}
|
||||
}
|
||||
path->addItem("<root>");
|
||||
|
||||
connect(path, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePath()));
|
||||
}
|
||||
|
||||
void DiskBrowser::setNameFilters(const QString &filters) {
|
||||
disconnect(filter, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFilter()));
|
||||
|
||||
filter->clear();
|
||||
|
||||
string filterData = filters.toUtf8().constData();
|
||||
lstring filterPart;
|
||||
filterPart.split(";;", filterData);
|
||||
|
||||
for(unsigned i = 0; i < filterPart.size(); i++) {
|
||||
filter->addItem(utf8() << filterPart[i]);
|
||||
}
|
||||
|
||||
connect(filter, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFilter()));
|
||||
updateFilter();
|
||||
}
|
||||
|
||||
void DiskBrowser::cdUp() {
|
||||
folderCreator->window->hide();
|
||||
//if we aren't already at the root node, select the second node, which is one path higher than the current
|
||||
if(path->count() > 1) path->setCurrentIndex(1);
|
||||
}
|
||||
|
||||
void DiskBrowser::updatePath() {
|
||||
setPath(path->currentText());
|
||||
}
|
||||
|
||||
void DiskBrowser::updateFilter() {
|
||||
QString currentText = filter->currentText();
|
||||
if(currentText.length() == 0) {
|
||||
model->setNameFilters(QStringList() << "*");
|
||||
} else {
|
||||
string filters = currentText.toUtf8().constData();
|
||||
filters = substr(filters, strpos(filters, "("));
|
||||
ltrim(filters, "(");
|
||||
rtrim(filters, ")");
|
||||
lstring filterPart;
|
||||
filterPart.split(" ", filters);
|
||||
QStringList filterList;
|
||||
for(unsigned i = 0; i < filterPart.size(); i++) filterList << (const char*)filterPart[i];
|
||||
model->setNameFilters(filterList);
|
||||
}
|
||||
}
|
||||
|
||||
//true means filename can be loaded directly
|
||||
//false means it cannot (eg this is a folder and we are attempting to load a ROM)
|
||||
bool DiskBrowser::currentFilename(string &filename) {
|
||||
bool loadable = false;
|
||||
QModelIndex item = view->currentIndex();
|
||||
filename = model->filePath(item).toUtf8().constData();
|
||||
|
||||
if(browseMode != Folder) {
|
||||
if(model->isDir(item) == true) {
|
||||
QDir directory(utf8() << filename);
|
||||
directory.setNameFilters(QStringList() << "*.sfc" << "*.smc");
|
||||
QStringList list = directory.entryList(QDir::Files | QDir::NoDotAndDotDot);
|
||||
if(list.count() == 1) {
|
||||
filename << "/" << list[0].toUtf8().constData();
|
||||
loadable = true;
|
||||
}
|
||||
} else {
|
||||
loadable = true;
|
||||
}
|
||||
}
|
||||
|
||||
return loadable;
|
||||
}
|
||||
|
||||
DiskBrowser::DiskBrowser() {
|
||||
setMinimumSize(720, 480);
|
||||
window = new QbWindow(config.geometry.diskBrowser);
|
||||
window->setObjectName("disk-browser");
|
||||
window->resize(720, 480);
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
panel()->setLayout(layout);
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(Style::WidgetSpacing);
|
||||
window->setLayout(layout);
|
||||
|
||||
topLayout = new QHBoxLayout;
|
||||
layout->addLayout(topLayout);
|
||||
|
||||
browseLayout = new QVBoxLayout;
|
||||
topLayout->addLayout(browseLayout);
|
||||
|
||||
pathLayout = new QHBoxLayout;
|
||||
browseLayout->addLayout(pathLayout);
|
||||
|
||||
path = new QComboBox;
|
||||
path->setMinimumContentsLength(16);
|
||||
path->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
|
||||
path->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
pathLayout->addWidget(path);
|
||||
|
||||
newFolder = new QPushButton;
|
||||
newFolder->setIconSize(QSize(16, 16));
|
||||
newFolder->setIcon(QIcon(":/16x16/folder-new.png"));
|
||||
pathLayout->addWidget(newFolder);
|
||||
|
||||
view = new DiskBrowserView;
|
||||
view->setIconSize(QSize(16, 16));
|
||||
browseLayout->addWidget(view);
|
||||
|
||||
panelLayout = new QVBoxLayout;
|
||||
topLayout->addLayout(panelLayout);
|
||||
|
||||
group = new QGroupBox;
|
||||
panelLayout->addWidget(group);
|
||||
|
||||
groupLayout = new QVBoxLayout;
|
||||
group->setLayout(groupLayout);
|
||||
|
||||
info = new QLabel;
|
||||
info->setFixedWidth(256);
|
||||
layout->addWidget(info);
|
||||
groupLayout->addWidget(info);
|
||||
|
||||
screenshot = new ScreenshotPreview;
|
||||
screenshot->setFixedSize(256, 239);
|
||||
layout->addWidget(screenshot);
|
||||
image = new DiskBrowserImage;
|
||||
image->setFixedSize(256, 239);
|
||||
groupLayout->addWidget(image);
|
||||
|
||||
spacer = new QWidget;
|
||||
spacer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
layout->addWidget(spacer);
|
||||
groupLayout->addWidget(spacer);
|
||||
|
||||
applyPatch = new QCheckBox("Apply UPS patch");
|
||||
applyPatch->setChecked(true);
|
||||
applyPatch->setVisible(false);
|
||||
applyPatch->setEnabled(false);
|
||||
layout->addWidget(applyPatch);
|
||||
groupLayout->addWidget(applyPatch);
|
||||
|
||||
connect(this, SIGNAL(itemActivated(const QString&, bool)), this, SLOT(onActivate(const QString&, bool)));
|
||||
connect(this, SIGNAL(itemChanged(const QString&, bool)), this, SLOT(onChange(const QString&, bool)));
|
||||
connect(load, SIGNAL(released()), this, SLOT(onLoad()));
|
||||
controlLayout = new QHBoxLayout;
|
||||
layout->addLayout(controlLayout);
|
||||
|
||||
filter = new QComboBox;
|
||||
filter->setMinimumContentsLength(16);
|
||||
filter->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
|
||||
filter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
controlLayout->addWidget(filter);
|
||||
|
||||
options = new QPushButton("Options");
|
||||
options->setEnabled(false);
|
||||
controlLayout->addWidget(options);
|
||||
|
||||
QMenu *menu = new QMenu;
|
||||
options->setMenu(menu);
|
||||
|
||||
ok = new QPushButton("Ok");
|
||||
ok->setEnabled(false);
|
||||
controlLayout->addWidget(ok);
|
||||
|
||||
cancel = new QPushButton("Cancel");
|
||||
controlLayout->addWidget(cancel);
|
||||
|
||||
model = new QFileSystemModel;
|
||||
model->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
|
||||
model->setNameFilterDisables(false);
|
||||
|
||||
view->setModel(model);
|
||||
view->setExpandsOnDoubleClick(false);
|
||||
view->setAllColumnsShowFocus(true);
|
||||
view->setUniformRowHeights(true);
|
||||
view->setSortingEnabled(true);
|
||||
view->sortByColumn(0, Qt::AscendingOrder);
|
||||
view->setColumnHidden(1, true);
|
||||
view->setColumnHidden(2, true);
|
||||
view->setColumnHidden(3, true);
|
||||
view->setHeaderHidden(true);
|
||||
|
||||
folderCreator = new FolderCreator;
|
||||
|
||||
connect(newFolder, SIGNAL(released()), folderCreator, SLOT(show()));
|
||||
connect(view, SIGNAL(cdUp()), this, SLOT(cdUp()));
|
||||
connect(view, SIGNAL(activated(const QModelIndex&)), this, SLOT(activateItem(const QModelIndex&)));
|
||||
connect(view, SIGNAL(changed(const QModelIndex&)), this, SLOT(changeItem(const QModelIndex&)));
|
||||
connect(view, SIGNAL(escape()), window, SLOT(hide()));
|
||||
connect(ok, SIGNAL(released()), this, SLOT(loadSelected()));
|
||||
connect(cancel, SIGNAL(released()), window, SLOT(hide()));
|
||||
}
|
||||
|
|
|
@ -1,33 +1,99 @@
|
|||
class PathSettingWidget;
|
||||
|
||||
class DiskBrowser : public QbDiskBrowser {
|
||||
class FolderCreator : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QbWindow *window;
|
||||
QVBoxLayout *layout;
|
||||
QLabel *label;
|
||||
QLineEdit *name;
|
||||
QHBoxLayout *controlLayout;
|
||||
QPushButton *ok;
|
||||
QPushButton *cancel;
|
||||
|
||||
FolderCreator();
|
||||
|
||||
public slots:
|
||||
void show();
|
||||
void createFolder();
|
||||
} *folderCreator;
|
||||
|
||||
class DiskBrowserView : public QTreeView {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
void keyPressEvent(QKeyEvent*);
|
||||
void keyReleaseEvent(QKeyEvent*);
|
||||
|
||||
signals:
|
||||
void cdUp();
|
||||
void changed(const QModelIndex&);
|
||||
void escape();
|
||||
|
||||
public slots:
|
||||
void currentChanged(const QModelIndex&, const QModelIndex&);
|
||||
};
|
||||
|
||||
class DiskBrowserImage : public QWidget {
|
||||
public:
|
||||
string name;
|
||||
void paintEvent(QPaintEvent*);
|
||||
};
|
||||
|
||||
class DiskBrowser : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QbWindow *window;
|
||||
QVBoxLayout *layout;
|
||||
QHBoxLayout *topLayout;
|
||||
QVBoxLayout *browseLayout;
|
||||
QHBoxLayout *pathLayout;
|
||||
QComboBox *path;
|
||||
QPushButton *newFolder;
|
||||
DiskBrowserView *view;
|
||||
QVBoxLayout *panelLayout;
|
||||
QGroupBox *group;
|
||||
QVBoxLayout *groupLayout;
|
||||
QLabel *info;
|
||||
DiskBrowserImage *image;
|
||||
QWidget *spacer;
|
||||
QCheckBox *applyPatch;
|
||||
QHBoxLayout *controlLayout;
|
||||
QComboBox *filter;
|
||||
QPushButton *options;
|
||||
QPushButton *ok;
|
||||
QPushButton *cancel;
|
||||
QFileSystemModel *model;
|
||||
PathSettingWidget *activePath;
|
||||
|
||||
void chooseFolder(PathSettingWidget*, const char*);
|
||||
void loadAnyCartridge();
|
||||
void loadCartridge();
|
||||
void loadBaseCartridge();
|
||||
void loadBsxCartridge();
|
||||
void loadSufamiTurboCartridge1();
|
||||
void loadSufamiTurboCartridge2();
|
||||
void loadSuperGameBoyCartridge();
|
||||
string queryImageInformation();
|
||||
|
||||
string queryAbsoluteImageName(const QString&, bool);
|
||||
string queryImageInformation(const char*);
|
||||
void setPath(const QString&);
|
||||
void setNameFilters(const QString&);
|
||||
|
||||
DiskBrowser();
|
||||
|
||||
public slots:
|
||||
void onActivate(const QString&, bool);
|
||||
void onChange(const QString&, bool);
|
||||
void onLoad();
|
||||
void cdUp();
|
||||
void updatePath();
|
||||
void updateFilter();
|
||||
void activateItem(const QModelIndex&);
|
||||
void changeItem(const QModelIndex&);
|
||||
void loadSelected();
|
||||
|
||||
private:
|
||||
enum BrowseMode {
|
||||
Folder,
|
||||
AnyCartridge,
|
||||
Cartridge,
|
||||
BaseCartridge,
|
||||
BsxCartridge,
|
||||
SufamiTurboCartridge1,
|
||||
|
@ -35,15 +101,5 @@ private:
|
|||
SuperGameBoyCartridge,
|
||||
} browseMode;
|
||||
|
||||
QVBoxLayout *layout;
|
||||
QLabel *info;
|
||||
class ScreenshotPreview : public QWidget {
|
||||
public:
|
||||
string screenshotName;
|
||||
void paintEvent(QPaintEvent*);
|
||||
} *screenshot;
|
||||
QWidget *spacer;
|
||||
QCheckBox *applyPatch;
|
||||
|
||||
string imageName;
|
||||
bool currentFilename(string&);
|
||||
} *diskBrowser;
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
HtmlViewerWindow::HtmlViewerWindow() {
|
||||
setObjectName("html-window");
|
||||
window = new QbWindow(config.geometry.htmlViewerWindow);
|
||||
window->setObjectName("html-window");
|
||||
window->resize(560, 480);
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(0); {
|
||||
document = new QTextBrowser;
|
||||
layout->addWidget(document);
|
||||
}
|
||||
layout->setSpacing(0);
|
||||
window->setLayout(layout);
|
||||
|
||||
setLayout(layout);
|
||||
setMinimumSize(560, 480);
|
||||
document = new QTextBrowser;
|
||||
layout->addWidget(document);
|
||||
}
|
||||
|
||||
void HtmlViewerWindow::show(const char *title, const char *htmlData) {
|
||||
document->setHtml(utf8() << htmlData);
|
||||
setWindowTitle(title);
|
||||
showAt(0.0, 0.0);
|
||||
setFocus();
|
||||
window->setWindowTitle(title);
|
||||
window->show();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
class HtmlViewerWindow : public QbWindow {
|
||||
class HtmlViewerWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QbWindow *window;
|
||||
QVBoxLayout *layout;
|
||||
QTextBrowser *document;
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
LoaderWindow::LoaderWindow() {
|
||||
setObjectName("loader-window");
|
||||
setMinimumWidth(520);
|
||||
window = new QbWindow(config.geometry.loaderWindow);
|
||||
window->setObjectName("loader-window");
|
||||
window->setMinimumWidth(520);
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(0);
|
||||
setLayout(layout);
|
||||
window->setLayout(layout);
|
||||
|
||||
grid = new QGridLayout; {
|
||||
baseLabel = new QLabel("Base cartridge:");
|
||||
|
@ -81,7 +82,7 @@ void LoaderWindow::syncUi() {
|
|||
}
|
||||
|
||||
void LoaderWindow::loadBsxSlottedCartridge(const char *filebase, const char *fileSlot1) {
|
||||
hide();
|
||||
window->hide();
|
||||
baseLabel->show(), baseFile->show(), baseBrowse->show(), baseClear->show();
|
||||
slot1Label->show(), slot1File->show(), slot1Browse->show(), slot1Clear->show();
|
||||
slot2Label->hide(), slot2File->hide(), slot2Browse->hide(), slot2Clear->hide();
|
||||
|
@ -97,7 +98,7 @@ void LoaderWindow::loadBsxSlottedCartridge(const char *filebase, const char *fil
|
|||
}
|
||||
|
||||
void LoaderWindow::loadBsxCartridge(const char *fileBase, const char *fileSlot1) {
|
||||
hide();
|
||||
window->hide();
|
||||
baseLabel->show(), baseFile->show(), baseBrowse->show(), baseClear->show();
|
||||
slot1Label->show(), slot1File->show(), slot1Browse->show(), slot1Clear->show();
|
||||
slot2Label->hide(), slot2File->hide(), slot2Browse->hide(), slot2Clear->hide();
|
||||
|
@ -113,7 +114,7 @@ void LoaderWindow::loadBsxCartridge(const char *fileBase, const char *fileSlot1)
|
|||
}
|
||||
|
||||
void LoaderWindow::loadSufamiTurboCartridge(const char *fileBase, const char *fileSlot1, const char *fileSlot2) {
|
||||
hide();
|
||||
window->hide();
|
||||
baseLabel->show(), baseFile->show(), baseBrowse->show(), baseClear->show();
|
||||
slot1Label->show(), slot1File->show(), slot1Browse->show(), slot1Clear->show();
|
||||
slot2Label->show(), slot2File->show(), slot2Browse->show(), slot2Clear->show();
|
||||
|
@ -131,7 +132,7 @@ void LoaderWindow::loadSufamiTurboCartridge(const char *fileBase, const char *fi
|
|||
}
|
||||
|
||||
void LoaderWindow::loadSuperGameBoyCartridge(const char *fileBase, const char *fileSlot1) {
|
||||
hide();
|
||||
window->hide();
|
||||
baseLabel->show(), baseFile->show(), baseBrowse->show(), baseClear->show();
|
||||
slot1Label->show(), slot1File->show(), slot1Browse->show(), slot1Clear->show();
|
||||
slot2Label->hide(), slot2File->hide(), slot2Browse->hide(), slot2Clear->hide();
|
||||
|
@ -147,9 +148,9 @@ void LoaderWindow::loadSuperGameBoyCartridge(const char *fileBase, const char *f
|
|||
}
|
||||
|
||||
void LoaderWindow::showWindow(const char *title) {
|
||||
setWindowTitle(title);
|
||||
showAt(0.0, 0.0);
|
||||
setFocus();
|
||||
window->setWindowTitle(title);
|
||||
window->show();
|
||||
window->shrink();
|
||||
load->setFocus();
|
||||
}
|
||||
|
||||
|
@ -207,7 +208,7 @@ void LoaderWindow::clearSlot2Cartridge() {
|
|||
}
|
||||
|
||||
void LoaderWindow::onLoad() {
|
||||
hide();
|
||||
window->hide();
|
||||
string base = baseFile->text().toUtf8().data();
|
||||
string slot1 = slot1File->text().toUtf8().data();
|
||||
string slot2 = slot2File->text().toUtf8().data();
|
||||
|
@ -235,5 +236,5 @@ void LoaderWindow::onLoad() {
|
|||
}
|
||||
|
||||
void LoaderWindow::onCancel() {
|
||||
hide();
|
||||
window->hide();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
class LoaderWindow : public QbWindow {
|
||||
class LoaderWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QbWindow *window;
|
||||
QVBoxLayout *layout;
|
||||
QGridLayout *grid;
|
||||
QLabel *baseLabel;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
MainWindow::MainWindow() {
|
||||
setObjectName("main-window");
|
||||
setWindowTitle(utf8() << bsnesTitle << " v" << bsnesVersion);
|
||||
window = new Window(config.geometry.mainWindow);
|
||||
window->setObjectName("main-window");
|
||||
window->setWindowTitle(utf8() << bsnesTitle << " v" << bsnesVersion);
|
||||
|
||||
//menu bar
|
||||
#if defined(PLATFORM_OSX)
|
||||
|
@ -12,45 +13,37 @@ MainWindow::MainWindow() {
|
|||
system = menuBar->addMenu("System");
|
||||
system_load = system->addAction("Load Cartridge ...");
|
||||
system_load->setIcon(QIcon(":/16x16/document-open.png"));
|
||||
system_loadSpecial = system->addMenu("Load Special");
|
||||
system_loadSpecial->setIcon(QIcon(":/16x16/document-open.png"));
|
||||
system_loadSpecial_bsxSlotted = system_loadSpecial->addAction("Load BS-X Slotted Cartridge ...");
|
||||
system_loadSpecial_bsxSlotted->setIcon(QIcon(":/16x16/document-open.png"));
|
||||
system_loadSpecial_bsx = system_loadSpecial->addAction("Load BS-X Cartridge ...");
|
||||
system_loadSpecial_bsx->setIcon(QIcon(":/16x16/document-open.png"));
|
||||
system_loadSpecial_sufamiTurbo = system_loadSpecial->addAction("Load Sufami Turbo Cartridge ...");
|
||||
system_loadSpecial_sufamiTurbo->setIcon(QIcon(":/16x16/document-open.png"));
|
||||
system_loadSpecial_superGameBoy = system_loadSpecial->addAction("Load Super Game Boy Cartridge ...");
|
||||
system_loadSpecial_superGameBoy->setIcon(QIcon(":/16x16/document-open.png"));
|
||||
system_loadSpecial_superGameBoy->setVisible(false);
|
||||
system->addSeparator();
|
||||
system_power = system->addAction("Power");
|
||||
system_power->setIcon(QIcon(":/16x16/system-shutdown.png"));
|
||||
system_power->setCheckable(true);
|
||||
system->addAction(system_power = new QbCheckAction("Power", 0));
|
||||
system_reset = system->addAction("Reset");
|
||||
system_reset->setIcon(QIcon(":/16x16/view-refresh.png"));
|
||||
system->addSeparator();
|
||||
system_port1 = system->addMenu("Controller Port 1");
|
||||
system_port1->setIcon(QIcon(":/16x16/input-gaming.png"));
|
||||
system_port1_none = system_port1->addAction("None");
|
||||
system_port1_none->setCheckable(true);
|
||||
system_port1_joypad = system_port1->addAction("Joypad");
|
||||
system_port1_joypad->setIcon(QIcon(":/16x16/input-gaming.png"));
|
||||
system_port1_joypad->setCheckable(true);
|
||||
system_port1_multitap = system_port1->addAction("Multitap");
|
||||
system_port1_multitap->setIcon(QIcon(":/16x16/input-gaming.png"));
|
||||
system_port1_multitap->setCheckable(true);
|
||||
system_port1_mouse = system_port1->addAction("Mouse");
|
||||
system_port1_mouse->setIcon(QIcon(":/16x16/input-mouse.png"));
|
||||
system_port1_mouse->setCheckable(true);
|
||||
system_port1->addAction(system_port1_none = new QbRadioAction("None", 0));
|
||||
system_port1->addAction(system_port1_joypad = new QbRadioAction("Joypad", 0));
|
||||
system_port1->addAction(system_port1_multitap = new QbRadioAction("Multitap", 0));
|
||||
system_port1->addAction(system_port1_mouse = new QbRadioAction("Mouse", 0));
|
||||
system_port2 = system->addMenu("Controller Port 2");
|
||||
system_port2->setIcon(QIcon(":/16x16/input-gaming.png"));
|
||||
system_port2_none = system_port2->addAction("None");
|
||||
system_port2_none->setCheckable(true);
|
||||
system_port2_joypad = system_port2->addAction("Joypad");
|
||||
system_port2_joypad->setIcon(QIcon(":/16x16/input-gaming.png"));
|
||||
system_port2_joypad->setCheckable(true);
|
||||
system_port2_multitap = system_port2->addAction("Multitap");
|
||||
system_port2_multitap->setIcon(QIcon(":/16x16/input-gaming.png"));
|
||||
system_port2_multitap->setCheckable(true);
|
||||
system_port2_mouse = system_port2->addAction("Mouse");
|
||||
system_port2_mouse->setIcon(QIcon(":/16x16/input-mouse.png"));
|
||||
system_port2_mouse->setCheckable(true);
|
||||
system_port2_superscope = system_port2->addAction("Super Scope");
|
||||
system_port2_superscope->setCheckable(true);
|
||||
system_port2_justifier = system_port2->addAction("Justifier");
|
||||
system_port2_justifier->setCheckable(true);
|
||||
system_port2_justifiers = system_port2->addAction("Two Justifiers");
|
||||
system_port2_justifiers->setCheckable(true);
|
||||
system_port2->addAction(system_port2_none = new QbRadioAction("None", 0));
|
||||
system_port2->addAction(system_port2_joypad = new QbRadioAction("Joypad", 0));
|
||||
system_port2->addAction(system_port2_multitap = new QbRadioAction("Multitap", 0));
|
||||
system_port2->addAction(system_port2_mouse = new QbRadioAction("Mouse", 0));
|
||||
system_port2->addAction(system_port2_superscope = new QbRadioAction("Super Scope", 0));
|
||||
system_port2->addAction(system_port2_justifier = new QbRadioAction("Justifier", 0));
|
||||
system_port2->addAction(system_port2_justifiers = new QbRadioAction("Two Justifiers", 0));
|
||||
#if !defined(PLATFORM_OSX)
|
||||
system->addSeparator();
|
||||
#endif
|
||||
|
@ -61,70 +54,52 @@ MainWindow::MainWindow() {
|
|||
settings = menuBar->addMenu("Settings");
|
||||
settings_videoMode = settings->addMenu("Video Mode");
|
||||
settings_videoMode->setIcon(QIcon(":/16x16/video-display.png"));
|
||||
settings_videoMode_1x = settings_videoMode->addAction("Scale 1x");
|
||||
settings_videoMode_1x->setCheckable(true);
|
||||
settings_videoMode_2x = settings_videoMode->addAction("Scale 2x");
|
||||
settings_videoMode_2x->setCheckable(true);
|
||||
settings_videoMode_3x = settings_videoMode->addAction("Scale 3x");
|
||||
settings_videoMode_3x->setCheckable(true);
|
||||
settings_videoMode_4x = settings_videoMode->addAction("Scale 4x");
|
||||
settings_videoMode_4x->setCheckable(true);
|
||||
settings_videoMode_max = settings_videoMode->addAction("Scale Max");
|
||||
settings_videoMode_max->setCheckable(true);
|
||||
settings_videoMode->addAction(settings_videoMode_1x = new QbRadioAction("Scale 1x", 0));
|
||||
settings_videoMode->addAction(settings_videoMode_2x = new QbRadioAction("Scale 2x", 0));
|
||||
settings_videoMode->addAction(settings_videoMode_3x = new QbRadioAction("Scale 3x", 0));
|
||||
settings_videoMode->addAction(settings_videoMode_4x = new QbRadioAction("Scale 4x", 0));
|
||||
settings_videoMode->addAction(settings_videoMode_max = new QbRadioAction("Scale Max", 0));
|
||||
settings_videoMode->addSeparator();
|
||||
settings_videoMode_correctAspectRatio = settings_videoMode->addAction("Correct Aspect Ratio");
|
||||
settings_videoMode_correctAspectRatio->setCheckable(true);
|
||||
settings_videoMode_fullscreen = settings_videoMode->addAction("Fullscreen");
|
||||
settings_videoMode_fullscreen->setIcon(QIcon(":/16x16/view-fullscreen.png"));
|
||||
settings_videoMode_fullscreen->setCheckable(true);
|
||||
settings_videoMode->addAction(settings_videoMode_correctAspectRatio = new QbCheckAction("Correct Aspect Ratio", 0));
|
||||
settings_videoMode->addAction(settings_videoMode_fullscreen = new QbCheckAction("Fullscreen", 0));
|
||||
settings_videoMode->addSeparator();
|
||||
settings_videoMode_ntsc = settings_videoMode->addAction("NTSC");
|
||||
settings_videoMode_ntsc->setCheckable(true);
|
||||
settings_videoMode_pal = settings_videoMode->addAction("PAL");
|
||||
settings_videoMode_pal->setCheckable(true);
|
||||
settings_videoFilter = settings->addMenu("Video Filter");
|
||||
settings_videoFilter->setIcon(QIcon(":/16x16/image-x-generic.png"));
|
||||
settings_videoFilter_point = settings_videoFilter->addAction("Point");
|
||||
settings_videoFilter_point->setCheckable(true);
|
||||
settings_videoFilter_linear = settings_videoFilter->addAction("Linear");
|
||||
settings_videoFilter_linear->setCheckable(true);
|
||||
settings_videoMode->addAction(settings_videoMode_ntsc = new QbRadioAction("NTSC", 0));
|
||||
settings_videoMode->addAction(settings_videoMode_pal = new QbRadioAction("PAL", 0));
|
||||
|
||||
if(filter.opened()) {
|
||||
settings_videoFilter = settings->addMenu("Video Filter");
|
||||
settings_videoFilter->setIcon(QIcon(":/16x16/image-x-generic.png"));
|
||||
|
||||
settings_videoFilter_configure = settings_videoFilter->addAction("Configure Active Filter ...");
|
||||
settings_videoFilter_configure->setIcon(QIcon(":/16x16/preferences-desktop.png"));
|
||||
settings_videoFilter->addSeparator();
|
||||
settings_videoFilter_none = settings_videoFilter->addAction("None");
|
||||
settings_videoFilter_none->setCheckable(true);
|
||||
settings_videoFilter_scanline = settings_videoFilter->addAction("Scanline");
|
||||
settings_videoFilter_scanline->setCheckable(true);
|
||||
settings_videoFilter_scale2x = settings_videoFilter->addAction("Scale2x");
|
||||
settings_videoFilter_scale2x->setCheckable(true);
|
||||
settings_videoFilter_lq2x = settings_videoFilter->addAction("LQ2x");
|
||||
settings_videoFilter_lq2x->setCheckable(true);
|
||||
settings_videoFilter_hq2x = settings_videoFilter->addAction("HQ2x");
|
||||
settings_videoFilter_hq2x->setCheckable(true);
|
||||
settings_videoFilter_ntsc = settings_videoFilter->addAction("NTSC");
|
||||
settings_videoFilter_ntsc->setCheckable(true);
|
||||
|
||||
settings_videoFilter->addAction(settings_videoFilter_none = new QbRadioAction("None", 0));
|
||||
settings_videoFilter_list.add(settings_videoFilter_none);
|
||||
|
||||
lstring filterlist;
|
||||
filterlist.split(";", filter.dl_supported());
|
||||
for(unsigned i = 0; i < filterlist.size(); i++) {
|
||||
QbRadioAction *action = new QbRadioAction(utf8() << filterlist[i], 0);
|
||||
settings_videoFilter->addAction(action);
|
||||
settings_videoFilter_list.add(action);
|
||||
}
|
||||
}
|
||||
|
||||
settings->addAction(settings_smoothVideo = new QbCheckAction("Smooth Video Output", 0));
|
||||
settings->addSeparator();
|
||||
settings_muteAudio = settings->addAction("Mute Audio Output");
|
||||
settings_muteAudio->setIcon(QIcon(":/16x16/audio-volume-muted.png"));
|
||||
settings_muteAudio->setCheckable(true);
|
||||
settings->addAction(settings_muteAudio = new QbCheckAction("Mute Audio Output", 0));
|
||||
settings->addSeparator();
|
||||
settings_emulationSpeed = settings->addMenu("Emulation Speed");
|
||||
settings_emulationSpeed->setIcon(QIcon(":/16x16/appointment-new.png"));
|
||||
settings_emulationSpeed_slowest = settings_emulationSpeed->addAction("50%");
|
||||
settings_emulationSpeed_slowest->setCheckable(true);
|
||||
settings_emulationSpeed_slow = settings_emulationSpeed->addAction("75%");
|
||||
settings_emulationSpeed_slow->setCheckable(true);
|
||||
settings_emulationSpeed_normal = settings_emulationSpeed->addAction("100%");
|
||||
settings_emulationSpeed_normal->setCheckable(true);
|
||||
settings_emulationSpeed_fast = settings_emulationSpeed->addAction("150%");
|
||||
settings_emulationSpeed_fast->setCheckable(true);
|
||||
settings_emulationSpeed_fastest = settings_emulationSpeed->addAction("200%");
|
||||
settings_emulationSpeed_fastest->setCheckable(true);
|
||||
settings_emulationSpeed->addAction(settings_emulationSpeed_slowest = new QbRadioAction("50%", 0));
|
||||
settings_emulationSpeed->addAction(settings_emulationSpeed_slow = new QbRadioAction("75%", 0));
|
||||
settings_emulationSpeed->addAction(settings_emulationSpeed_normal = new QbRadioAction("100%", 0));
|
||||
settings_emulationSpeed->addAction(settings_emulationSpeed_fast = new QbRadioAction("150%", 0));
|
||||
settings_emulationSpeed->addAction(settings_emulationSpeed_fastest = new QbRadioAction("200%", 0));
|
||||
settings_emulationSpeed->addSeparator();
|
||||
settings_emulationSpeed_syncVideo = settings_emulationSpeed->addAction("Sync Video");
|
||||
settings_emulationSpeed_syncVideo->setIcon(QIcon(":/16x16/video-display.png"));
|
||||
settings_emulationSpeed_syncVideo->setCheckable(true);
|
||||
settings_emulationSpeed_syncAudio = settings_emulationSpeed->addAction("Sync Audio");
|
||||
settings_emulationSpeed_syncAudio->setIcon(QIcon(":/16x16/audio-volume-high.png"));
|
||||
settings_emulationSpeed_syncAudio->setCheckable(true);
|
||||
settings_emulationSpeed->addAction(settings_emulationSpeed_syncVideo = new QbCheckAction("Sync Video", 0));
|
||||
settings_emulationSpeed->addAction(settings_emulationSpeed_syncAudio = new QbCheckAction("Sync Audio", 0));
|
||||
settings_configuration = settings->addAction("Configuration ...");
|
||||
settings_configuration->setIcon(QIcon(":/16x16/preferences-desktop.png"));
|
||||
settings_configuration->setMenuRole(QAction::PreferencesRole);
|
||||
|
@ -132,6 +107,8 @@ MainWindow::MainWindow() {
|
|||
tools = menuBar->addMenu("Tools");
|
||||
tools_cheatEditor = tools->addAction("Cheat Editor ...");
|
||||
tools_cheatEditor->setIcon(QIcon(":/16x16/accessories-text-editor.png"));
|
||||
tools_cheatFinder = tools->addAction("Cheat Finder ...");
|
||||
tools_cheatFinder->setIcon(QIcon(":/16x16/system-search.png"));
|
||||
tools_stateManager = tools->addAction("State Manager ...");
|
||||
tools_stateManager->setIcon(QIcon(":/16x16/system-file-manager.png"));
|
||||
#if defined(DEBUGGER)
|
||||
|
@ -194,10 +171,14 @@ MainWindow::MainWindow() {
|
|||
#endif
|
||||
layout->addWidget(canvasContainer);
|
||||
layout->addWidget(statusBar);
|
||||
setLayout(layout);
|
||||
window->setLayout(layout);
|
||||
|
||||
//slots
|
||||
connect(system_load, SIGNAL(triggered()), this, SLOT(loadCartridge()));
|
||||
connect(system_loadSpecial_bsxSlotted, SIGNAL(triggered()), this, SLOT(loadBsxSlottedCartridge()));
|
||||
connect(system_loadSpecial_bsx, SIGNAL(triggered()), this, SLOT(loadBsxCartridge()));
|
||||
connect(system_loadSpecial_sufamiTurbo, SIGNAL(triggered()), this, SLOT(loadSufamiTurboCartridge()));
|
||||
connect(system_loadSpecial_superGameBoy, SIGNAL(triggered()), this, SLOT(loadSuperGameBoyCartridge()));
|
||||
connect(system_power, SIGNAL(triggered()), this, SLOT(power()));
|
||||
connect(system_reset, SIGNAL(triggered()), this, SLOT(reset()));
|
||||
connect(system_port1_none, SIGNAL(triggered()), this, SLOT(setPort1None()));
|
||||
|
@ -221,14 +202,13 @@ MainWindow::MainWindow() {
|
|||
connect(settings_videoMode_fullscreen, SIGNAL(triggered()), this, SLOT(toggleFullscreen()));
|
||||
connect(settings_videoMode_ntsc, SIGNAL(triggered()), this, SLOT(setVideoNtsc()));
|
||||
connect(settings_videoMode_pal, SIGNAL(triggered()), this, SLOT(setVideoPal()));
|
||||
connect(settings_videoFilter_point, SIGNAL(triggered()), this, SLOT(setPointFilter()));
|
||||
connect(settings_videoFilter_linear, SIGNAL(triggered()), this, SLOT(setLinearFilter()));
|
||||
connect(settings_videoFilter_none, SIGNAL(triggered()), this, SLOT(setNoFilter()));
|
||||
connect(settings_videoFilter_scanline, SIGNAL(triggered()), this, SLOT(setScanlineFilter()));
|
||||
connect(settings_videoFilter_scale2x, SIGNAL(triggered()), this, SLOT(setScale2xFilter()));
|
||||
connect(settings_videoFilter_lq2x, SIGNAL(triggered()), this, SLOT(setLq2xFilter()));
|
||||
connect(settings_videoFilter_hq2x, SIGNAL(triggered()), this, SLOT(setHq2xFilter()));
|
||||
connect(settings_videoFilter_ntsc, SIGNAL(triggered()), this, SLOT(setNtscFilter()));
|
||||
if(filter.opened()) {
|
||||
connect(settings_videoFilter_configure, SIGNAL(triggered()), this, SLOT(configureFilter()));
|
||||
for(unsigned i = 0; i < settings_videoFilter_list.size(); i++) {
|
||||
connect(settings_videoFilter_list[i], SIGNAL(triggered()), this, SLOT(setFilter()));
|
||||
}
|
||||
}
|
||||
connect(settings_smoothVideo, SIGNAL(triggered()), this, SLOT(toggleSmoothVideo()));
|
||||
connect(settings_muteAudio, SIGNAL(triggered()), this, SLOT(muteAudio()));
|
||||
connect(settings_emulationSpeed_slowest, SIGNAL(triggered()), this, SLOT(setSpeedSlowest()));
|
||||
connect(settings_emulationSpeed_slow, SIGNAL(triggered()), this, SLOT(setSpeedSlow()));
|
||||
|
@ -239,6 +219,7 @@ MainWindow::MainWindow() {
|
|||
connect(settings_emulationSpeed_syncAudio, SIGNAL(triggered()), this, SLOT(syncAudio()));
|
||||
connect(settings_configuration, SIGNAL(triggered()), this, SLOT(showConfigWindow()));
|
||||
connect(tools_cheatEditor, SIGNAL(triggered()), this, SLOT(showCheatEditor()));
|
||||
connect(tools_cheatFinder, SIGNAL(triggered()), this, SLOT(showCheatFinder()));
|
||||
connect(tools_stateManager, SIGNAL(triggered()), this, SLOT(showStateManager()));
|
||||
connect(tools_debugger, SIGNAL(triggered()), this, SLOT(showDebugger()));
|
||||
connect(help_documentation, SIGNAL(triggered()), this, SLOT(showDocumentation()));
|
||||
|
@ -277,15 +258,16 @@ void MainWindow::syncUi() {
|
|||
settings_videoMode_ntsc->setChecked(config.video.context->region == 0);
|
||||
settings_videoMode_pal->setChecked (config.video.context->region == 1);
|
||||
|
||||
settings_videoFilter_point->setChecked (config.video.context->hwFilter == 0);
|
||||
settings_videoFilter_linear->setChecked (config.video.context->hwFilter == 1);
|
||||
settings_videoFilter_none->setChecked (config.video.context->swFilter == 0);
|
||||
settings_videoFilter_scanline->setChecked(config.video.context->swFilter == 1);
|
||||
settings_videoFilter_scale2x->setChecked (config.video.context->swFilter == 2);
|
||||
settings_videoFilter_lq2x->setChecked (config.video.context->swFilter == 3);
|
||||
settings_videoFilter_hq2x->setChecked (config.video.context->swFilter == 4);
|
||||
settings_videoFilter_ntsc->setChecked (config.video.context->swFilter == 5);
|
||||
if(filter.opened()) {
|
||||
//only enable configuration option if the active filter supports it ...
|
||||
settings_videoFilter_configure->setEnabled(filter.settings());
|
||||
|
||||
for(unsigned i = 0; i < settings_videoFilter_list.size(); i++) {
|
||||
settings_videoFilter_list[i]->setChecked(config.video.context->swFilter == i);
|
||||
}
|
||||
}
|
||||
|
||||
settings_smoothVideo->setChecked(config.video.context->hwFilter == 1);
|
||||
settings_muteAudio->setChecked(config.audio.mute);
|
||||
|
||||
settings_emulationSpeed_slowest->setChecked(config.system.speed == 0);
|
||||
|
@ -299,14 +281,31 @@ void MainWindow::syncUi() {
|
|||
}
|
||||
|
||||
bool MainWindow::isActive() {
|
||||
return isActiveWindow() && !isMinimized();
|
||||
return window->isActiveWindow() && !window->isMinimized();
|
||||
}
|
||||
|
||||
void MainWindow::loadCartridge() {
|
||||
diskBrowser->loadAnyCartridge();
|
||||
diskBrowser->loadCartridge();
|
||||
}
|
||||
|
||||
void MainWindow::loadBsxSlottedCartridge() {
|
||||
loaderWindow->loadBsxSlottedCartridge("", "");
|
||||
}
|
||||
|
||||
void MainWindow::loadBsxCartridge() {
|
||||
loaderWindow->loadBsxCartridge(config.path.bsx, "");
|
||||
}
|
||||
|
||||
void MainWindow::loadSufamiTurboCartridge() {
|
||||
loaderWindow->loadSufamiTurboCartridge(config.path.st, "", "");
|
||||
}
|
||||
|
||||
void MainWindow::loadSuperGameBoyCartridge() {
|
||||
loaderWindow->loadSuperGameBoyCartridge(config.path.sgb, "");
|
||||
}
|
||||
|
||||
void MainWindow::power() {
|
||||
system_power->toggleChecked();
|
||||
if(system_power->isChecked()) {
|
||||
utility.modifySystemState(Utility::PowerOn);
|
||||
} else {
|
||||
|
@ -331,6 +330,7 @@ void MainWindow::setPort2Justifier() { SNES::config.controller_port2 = SNES::In
|
|||
void MainWindow::setPort2Justifiers() { SNES::config.controller_port2 = SNES::Input::DeviceJustifiers; utility.updateControllers(); syncUi(); }
|
||||
|
||||
void MainWindow::quit() {
|
||||
window->hide();
|
||||
application.terminate = true;
|
||||
}
|
||||
|
||||
|
@ -341,11 +341,13 @@ void MainWindow::setVideoMode4x() { config.video.context->multiplier = 4; utili
|
|||
void MainWindow::setVideoModeMax() { config.video.context->multiplier = 9; utility.resizeMainWindow(); syncUi(); }
|
||||
|
||||
void MainWindow::toggleAspectCorrection() {
|
||||
settings_videoMode_correctAspectRatio->toggleChecked();
|
||||
config.video.context->correctAspectRatio = settings_videoMode_correctAspectRatio->isChecked();
|
||||
utility.resizeMainWindow();
|
||||
}
|
||||
|
||||
void MainWindow::toggleFullscreen() {
|
||||
settings_videoMode_fullscreen->toggleChecked();
|
||||
config.video.isFullscreen = settings_videoMode_fullscreen->isChecked();
|
||||
utility.updateFullscreenState();
|
||||
utility.resizeMainWindow();
|
||||
|
@ -355,16 +357,37 @@ void MainWindow::toggleFullscreen() {
|
|||
void MainWindow::setVideoNtsc() { config.video.context->region = 0; utility.updateVideoMode(); utility.resizeMainWindow(); syncUi(); }
|
||||
void MainWindow::setVideoPal() { config.video.context->region = 1; utility.updateVideoMode(); utility.resizeMainWindow(); syncUi(); }
|
||||
|
||||
void MainWindow::setPointFilter() { config.video.context->hwFilter = 0; utility.updateHardwareFilter(); syncUi(); }
|
||||
void MainWindow::setLinearFilter() { config.video.context->hwFilter = 1; utility.updateHardwareFilter(); syncUi(); }
|
||||
void MainWindow::setNoFilter() { config.video.context->swFilter = 0; utility.updateSoftwareFilter(); syncUi(); }
|
||||
void MainWindow::setScanlineFilter() { config.video.context->swFilter = 1; utility.updateSoftwareFilter(); syncUi(); }
|
||||
void MainWindow::setScale2xFilter() { config.video.context->swFilter = 2; utility.updateSoftwareFilter(); syncUi(); }
|
||||
void MainWindow::setLq2xFilter() { config.video.context->swFilter = 3; utility.updateSoftwareFilter(); syncUi(); }
|
||||
void MainWindow::setHq2xFilter() { config.video.context->swFilter = 4; utility.updateSoftwareFilter(); syncUi(); }
|
||||
void MainWindow::setNtscFilter() { config.video.context->swFilter = 5; utility.updateSoftwareFilter(); syncUi(); }
|
||||
void MainWindow::toggleSmoothVideo() {
|
||||
settings_smoothVideo->toggleChecked();
|
||||
config.video.context->hwFilter = settings_smoothVideo->isChecked();
|
||||
utility.updateHardwareFilter();
|
||||
syncUi();
|
||||
}
|
||||
|
||||
void MainWindow::muteAudio() { config.audio.mute = settings_muteAudio->isChecked(); }
|
||||
void MainWindow::configureFilter() {
|
||||
QWidget *widget = filter.settings();
|
||||
if(widget) {
|
||||
widget->show();
|
||||
widget->activateWindow();
|
||||
widget->raise();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::setFilter() {
|
||||
for(unsigned i = 0; i < settings_videoFilter_list.size(); i++) {
|
||||
if(sender() == settings_videoFilter_list[i]) {
|
||||
config.video.context->swFilter = i;
|
||||
utility.updateSoftwareFilter();
|
||||
syncUi();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::muteAudio() {
|
||||
settings_muteAudio->toggleChecked();
|
||||
config.audio.mute = settings_muteAudio->isChecked();
|
||||
}
|
||||
|
||||
void MainWindow::setSpeedSlowest() { config.system.speed = 0; utility.updateEmulationSpeed(); syncUi(); }
|
||||
void MainWindow::setSpeedSlow() { config.system.speed = 1; utility.updateEmulationSpeed(); syncUi(); }
|
||||
|
@ -372,17 +395,24 @@ void MainWindow::setSpeedNormal() { config.system.speed = 2; utility.updateEmul
|
|||
void MainWindow::setSpeedFast() { config.system.speed = 3; utility.updateEmulationSpeed(); syncUi(); }
|
||||
void MainWindow::setSpeedFastest() { config.system.speed = 4; utility.updateEmulationSpeed(); syncUi(); }
|
||||
|
||||
void MainWindow::syncVideo() { config.video.synchronize = settings_emulationSpeed_syncVideo->isChecked(); utility.updateAvSync(); }
|
||||
void MainWindow::syncAudio() { config.audio.synchronize = settings_emulationSpeed_syncAudio->isChecked(); utility.updateAvSync(); }
|
||||
|
||||
void MainWindow::showConfigWindow() {
|
||||
settingsWindow->showAt(-1.0, 1.0);
|
||||
settingsWindow->setFocus();
|
||||
void MainWindow::syncVideo() {
|
||||
settings_emulationSpeed_syncVideo->toggleChecked();
|
||||
config.video.synchronize = settings_emulationSpeed_syncVideo->isChecked();
|
||||
utility.updateAvSync();
|
||||
}
|
||||
|
||||
void MainWindow::showCheatEditor() { toolsWindow->showCheatEditor(); }
|
||||
void MainWindow::syncAudio() {
|
||||
settings_emulationSpeed_syncAudio->toggleChecked();
|
||||
config.audio.synchronize = settings_emulationSpeed_syncAudio->isChecked();
|
||||
utility.updateAvSync();
|
||||
}
|
||||
|
||||
void MainWindow::showConfigWindow() { settingsWindow->window->show(); }
|
||||
|
||||
void MainWindow::showCheatEditor() { toolsWindow->showCheatEditor(); }
|
||||
void MainWindow::showCheatFinder() { toolsWindow->showCheatFinder(); }
|
||||
void MainWindow::showStateManager() { toolsWindow->showStateManager(); }
|
||||
void MainWindow::showDebugger() { debugger->show(); }
|
||||
void MainWindow::showDebugger() { debugger->window->show(); }
|
||||
|
||||
void MainWindow::showDocumentation() {
|
||||
QFile file(":/documentation.html");
|
||||
|
@ -400,12 +430,14 @@ void MainWindow::showLicense() {
|
|||
}
|
||||
}
|
||||
void MainWindow::showAbout() {
|
||||
aboutWindow->showAt(0.0, 0.0);
|
||||
aboutWindow->setFocus();
|
||||
aboutWindow->window->show();
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent*) {
|
||||
quit();
|
||||
void MainWindow::Window::closeEvent(QCloseEvent*) {
|
||||
mainWindow->quit();
|
||||
}
|
||||
|
||||
MainWindow::Window::Window(string &geometry) : QbWindow(geometry) {
|
||||
}
|
||||
|
||||
//============
|
||||
|
@ -425,7 +457,7 @@ void CanvasObject::dragEnterEvent(QDragEnterEvent *event) {
|
|||
void CanvasObject::dropEvent(QDropEvent *event) {
|
||||
if(event->mimeData()->hasUrls()) {
|
||||
QList<QUrl> list = event->mimeData()->urls();
|
||||
if(list.count() == 1) utility.loadCartridge(list.at(0).toLocalFile().toUtf8().constData());
|
||||
if(list.count() == 1) utility.loadCartridgeNormal(list.at(0).toLocalFile().toUtf8().constData());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,65 +13,69 @@ public:
|
|||
void paintEvent(QPaintEvent*);
|
||||
};
|
||||
|
||||
class MainWindow : public QbWindow {
|
||||
class MainWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
struct Window : public QbWindow {
|
||||
void closeEvent(QCloseEvent*);
|
||||
Window(string&);
|
||||
} *window;
|
||||
QMenuBar *menuBar;
|
||||
QStatusBar *statusBar;
|
||||
void closeEvent(QCloseEvent*);
|
||||
|
||||
QVBoxLayout *layout;
|
||||
QMenu *system;
|
||||
QAction *system_load;
|
||||
QAction *system_power;
|
||||
QMenu *system_loadSpecial;
|
||||
QAction *system_loadSpecial_bsxSlotted;
|
||||
QAction *system_loadSpecial_bsx;
|
||||
QAction *system_loadSpecial_sufamiTurbo;
|
||||
QAction *system_loadSpecial_superGameBoy;
|
||||
QbCheckAction *system_power;
|
||||
QAction *system_reset;
|
||||
QMenu *system_port1;
|
||||
QAction *system_port1_none;
|
||||
QAction *system_port1_joypad;
|
||||
QAction *system_port1_multitap;
|
||||
QAction *system_port1_mouse;
|
||||
QbRadioAction *system_port1_none;
|
||||
QbRadioAction *system_port1_joypad;
|
||||
QbRadioAction *system_port1_multitap;
|
||||
QbRadioAction *system_port1_mouse;
|
||||
QMenu *system_port2;
|
||||
QAction *system_port2_none;
|
||||
QAction *system_port2_joypad;
|
||||
QAction *system_port2_multitap;
|
||||
QAction *system_port2_mouse;
|
||||
QAction *system_port2_superscope;
|
||||
QAction *system_port2_justifier;
|
||||
QAction *system_port2_justifiers;
|
||||
QbRadioAction *system_port2_none;
|
||||
QbRadioAction *system_port2_joypad;
|
||||
QbRadioAction *system_port2_multitap;
|
||||
QbRadioAction *system_port2_mouse;
|
||||
QbRadioAction *system_port2_superscope;
|
||||
QbRadioAction *system_port2_justifier;
|
||||
QbRadioAction *system_port2_justifiers;
|
||||
QAction *system_exit;
|
||||
QMenu *settings;
|
||||
QMenu *settings_videoMode;
|
||||
QAction *settings_videoMode_1x;
|
||||
QAction *settings_videoMode_2x;
|
||||
QAction *settings_videoMode_3x;
|
||||
QAction *settings_videoMode_4x;
|
||||
QAction *settings_videoMode_max;
|
||||
QAction *settings_videoMode_correctAspectRatio;
|
||||
QAction *settings_videoMode_fullscreen;
|
||||
QAction *settings_videoMode_ntsc;
|
||||
QAction *settings_videoMode_pal;
|
||||
QbRadioAction *settings_videoMode_1x;
|
||||
QbRadioAction *settings_videoMode_2x;
|
||||
QbRadioAction *settings_videoMode_3x;
|
||||
QbRadioAction *settings_videoMode_4x;
|
||||
QbRadioAction *settings_videoMode_max;
|
||||
QbCheckAction *settings_videoMode_correctAspectRatio;
|
||||
QbCheckAction *settings_videoMode_fullscreen;
|
||||
QbRadioAction *settings_videoMode_ntsc;
|
||||
QbRadioAction *settings_videoMode_pal;
|
||||
QMenu *settings_videoFilter;
|
||||
QAction *settings_videoFilter_point;
|
||||
QAction *settings_videoFilter_linear;
|
||||
QAction *settings_videoFilter_none;
|
||||
QAction *settings_videoFilter_scanline;
|
||||
QAction *settings_videoFilter_scale2x;
|
||||
QAction *settings_videoFilter_lq2x;
|
||||
QAction *settings_videoFilter_hq2x;
|
||||
QAction *settings_videoFilter_ntsc;
|
||||
QAction *settings_muteAudio;
|
||||
QAction *settings_videoFilter_configure;
|
||||
QbRadioAction *settings_videoFilter_none;
|
||||
array<QbRadioAction*> settings_videoFilter_list;
|
||||
QbCheckAction *settings_smoothVideo;
|
||||
QbCheckAction *settings_muteAudio;
|
||||
QMenu *settings_emulationSpeed;
|
||||
QAction *settings_emulationSpeed_slowest;
|
||||
QAction *settings_emulationSpeed_slow;
|
||||
QAction *settings_emulationSpeed_normal;
|
||||
QAction *settings_emulationSpeed_fast;
|
||||
QAction *settings_emulationSpeed_fastest;
|
||||
QAction *settings_emulationSpeed_syncVideo;
|
||||
QAction *settings_emulationSpeed_syncAudio;
|
||||
QbRadioAction *settings_emulationSpeed_slowest;
|
||||
QbRadioAction *settings_emulationSpeed_slow;
|
||||
QbRadioAction *settings_emulationSpeed_normal;
|
||||
QbRadioAction *settings_emulationSpeed_fast;
|
||||
QbRadioAction *settings_emulationSpeed_fastest;
|
||||
QbCheckAction *settings_emulationSpeed_syncVideo;
|
||||
QbCheckAction *settings_emulationSpeed_syncAudio;
|
||||
QAction *settings_configuration;
|
||||
QMenu *tools;
|
||||
QAction *tools_cheatEditor;
|
||||
QAction *tools_cheatFinder;
|
||||
QAction *tools_stateManager;
|
||||
QAction *tools_debugger;
|
||||
QMenu *help;
|
||||
|
@ -90,6 +94,10 @@ public:
|
|||
|
||||
public slots:
|
||||
void loadCartridge();
|
||||
void loadBsxSlottedCartridge();
|
||||
void loadBsxCartridge();
|
||||
void loadSufamiTurboCartridge();
|
||||
void loadSuperGameBoyCartridge();
|
||||
void power();
|
||||
void reset();
|
||||
void setPort1None();
|
||||
|
@ -113,14 +121,9 @@ public slots:
|
|||
void toggleFullscreen();
|
||||
void setVideoNtsc();
|
||||
void setVideoPal();
|
||||
void setPointFilter();
|
||||
void setLinearFilter();
|
||||
void setNoFilter();
|
||||
void setScanlineFilter();
|
||||
void setScale2xFilter();
|
||||
void setLq2xFilter();
|
||||
void setHq2xFilter();
|
||||
void setNtscFilter();
|
||||
void configureFilter();
|
||||
void setFilter();
|
||||
void toggleSmoothVideo();
|
||||
void muteAudio();
|
||||
void setSpeedSlowest();
|
||||
void setSpeedSlow();
|
||||
|
@ -131,6 +134,7 @@ public slots:
|
|||
void syncAudio();
|
||||
void showConfigWindow();
|
||||
void showCheatEditor();
|
||||
void showCheatFinder();
|
||||
void showStateManager();
|
||||
void showDebugger();
|
||||
void showDocumentation();
|
||||
|
|
|
@ -60,26 +60,25 @@ Configuration::Configuration() {
|
|||
attach(file.autodetect_type = false, "file.autodetectType");
|
||||
attach(file.bypass_patch_crc32 = false, "file.bypassPatchCrc32");
|
||||
|
||||
attach(path.rom = "", "path.rom");
|
||||
attach(path.save = "", "path.save");
|
||||
attach(path.state = "", "path.state");
|
||||
attach(path.patch = "", "path.patch");
|
||||
attach(path.cheat = "", "path.cheat");
|
||||
attach(path.data = "", "path.data");
|
||||
attach(path.bsx = "", "path.bsx");
|
||||
attach(path.st = "", "path.st");
|
||||
attach(path.sgb = "", "path.sgb");
|
||||
attach(path.current = "", "path.current");
|
||||
attach(path.rom = "", "path.rom");
|
||||
attach(path.save = "", "path.save");
|
||||
attach(path.state = "", "path.state");
|
||||
attach(path.patch = "", "path.patch");
|
||||
attach(path.cheat = "", "path.cheat");
|
||||
attach(path.data = "", "path.data");
|
||||
attach(path.bsx = "", "path.bsx");
|
||||
attach(path.st = "", "path.st");
|
||||
attach(path.sgb = "", "path.sgb");
|
||||
|
||||
video.context = &video.windowed;
|
||||
attach(video.isFullscreen = false, "video.isFullscreen");
|
||||
attach(video.synchronize = false, "video.synchronize");
|
||||
|
||||
attach(video.contrastAdjust = 0, "video.contrastAdjust");
|
||||
attach(video.brightnessAdjust = 0, "video.brightnessAdjust");
|
||||
attach(video.gammaAdjust = 0, "video.gammaAdjust");
|
||||
|
||||
attach(video.enableGammaRamp = true, "video.enableGammaRamp");
|
||||
attach(video.enableNtscMergeFields = false, "video.enableNtscMergeFields");
|
||||
attach(video.contrastAdjust = 0, "video.contrastAdjust");
|
||||
attach(video.brightnessAdjust = 0, "video.brightnessAdjust");
|
||||
attach(video.gammaAdjust = 0, "video.gammaAdjust");
|
||||
attach(video.enableGammaRamp = true, "video.enableGammaRamp");
|
||||
|
||||
attach(video.ntscAspectRatio = 54.0 / 47.0, "video.ntscAspectRatio", "NTSC aspect ratio (x / y)");
|
||||
attach(video.palAspectRatio = 32.0 / 23.0, "video.palAspectRatio", "PAL aspect ratio (x / y)");
|
||||
|
@ -184,4 +183,20 @@ Configuration::Configuration() {
|
|||
attach(input.uiGeneral.toggleMenu = "keyboard00.escape", "input.uiGeneral.toggleMenu");
|
||||
attach(input.uiGeneral.toggleStatus = "keyboard00.escape", "input.uiGeneral.toggleStatus");
|
||||
attach(input.uiGeneral.exitEmulator = "none", "input.uiGeneral.exitEmulator");
|
||||
|
||||
attach(geometry.mainWindow = "", "geometry.mainWindow");
|
||||
attach(geometry.loaderWindow = "", "geometry.loaderWindow");
|
||||
attach(geometry.htmlViewerWindow = "", "geometry.htmlViewerWindow");
|
||||
attach(geometry.aboutWindow = "", "geometry.aboutWindow");
|
||||
attach(geometry.diskBrowser = "", "geometry.diskBrowser");
|
||||
attach(geometry.folderCreator = "", "geometry.folderCreator");
|
||||
attach(geometry.settingsWindow = "", "geometry.settingsWindow");
|
||||
attach(geometry.inputCaptureWindow = "", "geometry.inputCaptureWindow");
|
||||
attach(geometry.inputMouseCaptureWindow = "", "geometry.inputMouseCaptureWindow");
|
||||
attach(geometry.inputCalibrationWindow = "", "geometry.inputCalibrationWindow");
|
||||
attach(geometry.toolsWindow = "", "geometry.toolsWindow");
|
||||
attach(geometry.debugger = "", "geometry.debugger");
|
||||
attach(geometry.breakpointEditor = "", "geometry.breakpointEditor");
|
||||
attach(geometry.memoryEditor = "", "geometry.memoryEditor");
|
||||
attach(geometry.vramViewer = "", "geometry.vramViewer");
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ public:
|
|||
bool synchronize;
|
||||
signed contrastAdjust, brightnessAdjust, gammaAdjust;
|
||||
bool enableGammaRamp;
|
||||
bool enableNtscMergeFields;
|
||||
double ntscAspectRatio, palAspectRatio;
|
||||
|
||||
struct Context {
|
||||
|
@ -88,6 +87,27 @@ public:
|
|||
} uiGeneral;
|
||||
} input;
|
||||
|
||||
struct Geometry {
|
||||
string mainWindow;
|
||||
string loaderWindow;
|
||||
string htmlViewerWindow;
|
||||
string aboutWindow;
|
||||
string diskBrowser;
|
||||
string folderCreator;
|
||||
|
||||
string settingsWindow;
|
||||
string inputCaptureWindow;
|
||||
string inputMouseCaptureWindow;
|
||||
string inputCalibrationWindow;
|
||||
|
||||
string toolsWindow;
|
||||
|
||||
string debugger;
|
||||
string breakpointEditor;
|
||||
string memoryEditor;
|
||||
string vramViewer;
|
||||
} geometry;
|
||||
|
||||
bool load(const char *filename);
|
||||
void attachJoypad(Configuration::Input::Joypad &joypad, const char *name);
|
||||
Configuration();
|
||||
|
|
|
@ -59,19 +59,18 @@ void BreakpointItem::toggle() {
|
|||
}
|
||||
|
||||
BreakpointEditor::BreakpointEditor() {
|
||||
setObjectName("breakpoint-editor");
|
||||
setWindowTitle("Breakpoint Editor");
|
||||
window = new QbWindow(config.geometry.breakpointEditor);
|
||||
window->setObjectName("breakpoint-editor");
|
||||
window->setWindowTitle("Breakpoint Editor");
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(Style::WidgetSpacing);
|
||||
setLayout(layout);
|
||||
window->setLayout(layout);
|
||||
|
||||
for(unsigned n = 0; n < SNES::Debugger::Breakpoints; n++) {
|
||||
breakpoint[n] = new BreakpointItem(n);
|
||||
layout->addWidget(breakpoint[n]);
|
||||
}
|
||||
|
||||
resize(0, 0);
|
||||
}
|
||||
|
|
|
@ -17,10 +17,11 @@ private:
|
|||
const unsigned id;
|
||||
};
|
||||
|
||||
class BreakpointEditor : public QbWindow {
|
||||
class BreakpointEditor : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QbWindow *window;
|
||||
QVBoxLayout *layout;
|
||||
BreakpointItem *breakpoint[SNES::Debugger::Breakpoints];
|
||||
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
#include "hexeditor.cpp"
|
||||
#include "breakpoint.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "vramviewer.cpp"
|
||||
|
||||
Debugger::Debugger() {
|
||||
setObjectName("debugger");
|
||||
setWindowTitle("Debugger");
|
||||
window = new QbWindow(config.geometry.debugger);
|
||||
window->setObjectName("debugger");
|
||||
window->setWindowTitle("Debugger");
|
||||
|
||||
layout = new QHBoxLayout;
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(Style::WidgetSpacing);
|
||||
setLayout(layout);
|
||||
window->setLayout(layout);
|
||||
|
||||
menu = new QMenuBar;
|
||||
layout->setMenuBar(menu);
|
||||
|
@ -67,8 +69,6 @@ Debugger::Debugger() {
|
|||
spacer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||
controlLayout->addWidget(spacer);
|
||||
|
||||
resize(855, 425);
|
||||
|
||||
connect(tools_breakpoint, SIGNAL(triggered()), this, SLOT(showBreakpointEditor()));
|
||||
connect(tools_memory, SIGNAL(triggered()), this, SLOT(showMemoryEditor()));
|
||||
connect(tools_vramViewer, SIGNAL(triggered()), this, SLOT(showVramViewer()));
|
||||
|
@ -87,6 +87,7 @@ Debugger::Debugger() {
|
|||
|
||||
frameCounter = 0;
|
||||
synchronize();
|
||||
window->resize(855, 425);
|
||||
}
|
||||
|
||||
void Debugger::synchronize() {
|
||||
|
@ -98,11 +99,6 @@ void Debugger::synchronize() {
|
|||
memoryEditor->synchronize();
|
||||
}
|
||||
|
||||
void Debugger::show() {
|
||||
showAt(-1.0, +1.0);
|
||||
setFocus();
|
||||
}
|
||||
|
||||
void Debugger::echo(const char *message) {
|
||||
console->moveCursor(QTextCursor::End);
|
||||
console->insertHtml(message);
|
||||
|
@ -113,18 +109,15 @@ void Debugger::clear() {
|
|||
}
|
||||
|
||||
void Debugger::showBreakpointEditor() {
|
||||
breakpointEditor->showAt(-1.0, -1.0);
|
||||
breakpointEditor->setFocus();
|
||||
breakpointEditor->window->show();
|
||||
}
|
||||
|
||||
void Debugger::showMemoryEditor() {
|
||||
memoryEditor->showAt(0.0, -1.0);
|
||||
memoryEditor->setFocus();
|
||||
memoryEditor->window->show();
|
||||
}
|
||||
|
||||
void Debugger::showVramViewer() {
|
||||
vramViewer->showAt(+1.0, -1.0);
|
||||
vramViewer->setFocus();
|
||||
vramViewer->window->show();
|
||||
vramViewer->refresh();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
#include "hexeditor.moc"
|
||||
#include "breakpoint.moc"
|
||||
#include "memory.moc"
|
||||
#include "vramviewer.moc"
|
||||
|
||||
class Debugger : public QbWindow {
|
||||
class Debugger : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QbWindow *window;
|
||||
QMenuBar *menu;
|
||||
QMenu *tools;
|
||||
QAction *tools_breakpoint;
|
||||
|
@ -25,7 +27,6 @@ public:
|
|||
QCheckBox *traceSMP;
|
||||
QWidget *spacer;
|
||||
|
||||
void show();
|
||||
void echo(const char *message);
|
||||
void tracerUpdate();
|
||||
void event();
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
void HexEditor::keyPressEvent(QKeyEvent *event) {
|
||||
QTextCursor cursor = textCursor();
|
||||
unsigned x = cursor.position() % 56;
|
||||
unsigned y = cursor.position() / 56;
|
||||
|
||||
int hexCode = -1;
|
||||
switch(event->key()) {
|
||||
case Qt::Key_0: hexCode = 0; break;
|
||||
case Qt::Key_1: hexCode = 1; break;
|
||||
case Qt::Key_2: hexCode = 2; break;
|
||||
case Qt::Key_3: hexCode = 3; break;
|
||||
case Qt::Key_4: hexCode = 4; break;
|
||||
case Qt::Key_5: hexCode = 5; break;
|
||||
case Qt::Key_6: hexCode = 6; break;
|
||||
case Qt::Key_7: hexCode = 7; break;
|
||||
case Qt::Key_8: hexCode = 8; break;
|
||||
case Qt::Key_9: hexCode = 9; break;
|
||||
case Qt::Key_A: hexCode = 10; break;
|
||||
case Qt::Key_B: hexCode = 11; break;
|
||||
case Qt::Key_C: hexCode = 12; break;
|
||||
case Qt::Key_D: hexCode = 13; break;
|
||||
case Qt::Key_E: hexCode = 14; break;
|
||||
case Qt::Key_F: hexCode = 15; break;
|
||||
}
|
||||
|
||||
if(cursor.hasSelection() == false && hexCode != -1) {
|
||||
bool cursorOffsetValid = (x >= 8 && ((x - 8) % 3) != 2);
|
||||
if(cursorOffsetValid) {
|
||||
bool nibble = (x - 8) % 3; //0 = top nibble, 1 = bottom nibble
|
||||
unsigned cursorOffset = y * 16 + ((x - 8) / 3);
|
||||
unsigned effectiveOffset = hexOffset + cursorOffset;
|
||||
if(effectiveOffset >= hexSize) effectiveOffset %= hexSize;
|
||||
|
||||
uint8 data = reader(effectiveOffset);
|
||||
data &= (nibble == 0 ? 0x0f : 0xf0);
|
||||
data |= (nibble == 0 ? (hexCode << 4) : (hexCode << 0));
|
||||
writer(effectiveOffset, data);
|
||||
update();
|
||||
|
||||
cursor.setPosition(y * 56 + x + 1); //advance cursor
|
||||
setTextCursor(cursor);
|
||||
}
|
||||
} else {
|
||||
//allow navigation keys to move cursor, but block text input
|
||||
setTextInteractionFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse);
|
||||
QTextEdit::keyPressEvent(event);
|
||||
setTextInteractionFlags(Qt::TextEditorInteraction);
|
||||
}
|
||||
}
|
||||
|
||||
void HexEditor::setOffset(unsigned newOffset) {
|
||||
hexOffset = newOffset;
|
||||
scrollbar->setSliderPosition(hexOffset / 16);
|
||||
}
|
||||
|
||||
void HexEditor::setSize(unsigned newSize) {
|
||||
hexSize = newSize;
|
||||
scrollbar->setRange(0, hexSize / 16 - 16);
|
||||
}
|
||||
|
||||
void HexEditor::update() {
|
||||
string output;
|
||||
char temp[256];
|
||||
unsigned offset = hexOffset;
|
||||
|
||||
for(unsigned y = 0; y < 16; y++) {
|
||||
if(offset >= hexSize) break;
|
||||
sprintf(temp, "%.6x", offset & 0xffffff);
|
||||
output << "<font color='#606060'>" << temp << "</font> ";
|
||||
|
||||
for(unsigned x = 0; x < 16; x++) {
|
||||
if(offset >= hexSize) break;
|
||||
sprintf(temp, "%.2x", reader(offset++));
|
||||
output << "<font color='" << ((x & 1) ? "#000080" : "#0000ff") << "'>" << temp << "</font>";
|
||||
if(x != 15) output << " ";
|
||||
}
|
||||
|
||||
if(y != 15) output << "<br>";
|
||||
}
|
||||
|
||||
setHtml(utf8() << output);
|
||||
}
|
||||
|
||||
void HexEditor::sliderMoved() {
|
||||
unsigned offset = scrollbar->sliderPosition();
|
||||
hexOffset = offset * 16;
|
||||
update();
|
||||
}
|
||||
|
||||
HexEditor::HexEditor() {
|
||||
QFont font(Style::Monospace);
|
||||
setFont(font);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
setMinimumWidth((57 + 2) * fontMetrics().width(' '));
|
||||
setMinimumHeight((16 + 1) * fontMetrics().height());
|
||||
|
||||
layout = new QHBoxLayout;
|
||||
layout->setAlignment(Qt::AlignRight);
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
setLayout(layout);
|
||||
|
||||
scrollbar = new QScrollBar(Qt::Vertical);
|
||||
scrollbar->setSingleStep(1);
|
||||
scrollbar->setPageStep(16);
|
||||
layout->addWidget(scrollbar);
|
||||
|
||||
connect(scrollbar, SIGNAL(actionTriggered(int)), this, SLOT(sliderMoved()));
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
class HexEditor : public QTextEdit {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QHBoxLayout *layout;
|
||||
QScrollBar *scrollbar;
|
||||
|
||||
function<uint8 (unsigned)> reader;
|
||||
function<void (unsigned, uint8)> writer;
|
||||
void keyPressEvent(QKeyEvent*);
|
||||
unsigned hexOffset;
|
||||
unsigned hexSize;
|
||||
|
||||
void setOffset(unsigned newOffset);
|
||||
void setSize(unsigned newSize);
|
||||
|
||||
void update();
|
||||
HexEditor();
|
||||
|
||||
public slots:
|
||||
void sliderMoved();
|
||||
};
|
|
@ -1,11 +1,12 @@
|
|||
MemoryEditor::MemoryEditor() {
|
||||
setObjectName("memory-editor");
|
||||
setWindowTitle("Memory Editor");
|
||||
window = new QbWindow(config.geometry.memoryEditor);
|
||||
window->setObjectName("memory-editor");
|
||||
window->setWindowTitle("Memory Editor");
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(Style::WidgetSpacing);
|
||||
setLayout(layout);
|
||||
window->setLayout(layout);
|
||||
|
||||
controlLayout = new QHBoxLayout;
|
||||
controlLayout->setSpacing(Style::WidgetSpacing);
|
||||
|
@ -33,21 +34,13 @@ MemoryEditor::MemoryEditor() {
|
|||
refreshButton = new QPushButton("Refresh");
|
||||
controlLayout->addWidget(refreshButton);
|
||||
|
||||
editor = new QbHexEdit;
|
||||
editor->setFont(QFont(Style::Monospace));
|
||||
editor->setMinimumWidth((58 + 2) * editor->fontMetrics().width(' '));
|
||||
editor->setMinimumHeight((16 + 1) * editor->fontMetrics().height());
|
||||
|
||||
editor = new HexEditor;
|
||||
editor->reader = bind(&MemoryEditor::reader, this);
|
||||
editor->writer = bind(&MemoryEditor::writer, this);
|
||||
editor->setColumns(16);
|
||||
editor->setRows(16);
|
||||
editor->setDocumentSize(16 * 1024 * 1024);
|
||||
editor->setDocumentOffset(0);
|
||||
editor->setSize(16 * 1024 * 1024);
|
||||
memorySource = SNES::Debugger::CPUBus;
|
||||
layout->addWidget(editor);
|
||||
|
||||
resize(0, 0);
|
||||
connect(addr, SIGNAL(textEdited(const QString&)), this, SLOT(refresh()));
|
||||
connect(source, SIGNAL(currentIndexChanged(int)), this, SLOT(sourceChanged(int)));
|
||||
connect(refreshButton, SIGNAL(released()), this, SLOT(refresh()));
|
||||
|
@ -59,10 +52,18 @@ void MemoryEditor::autoUpdate() {
|
|||
|
||||
void MemoryEditor::synchronize() {
|
||||
if(SNES::cartridge.loaded() == false) {
|
||||
editor->setHtml("");
|
||||
addr->setEnabled(false);
|
||||
source->setEnabled(false);
|
||||
autoUpdateBox->setEnabled(false);
|
||||
refreshButton->setEnabled(false);
|
||||
editor->setHtml("");
|
||||
editor->scrollbar->setEnabled(false);
|
||||
} else {
|
||||
addr->setEnabled(true);
|
||||
source->setEnabled(true);
|
||||
autoUpdateBox->setEnabled(true);
|
||||
refreshButton->setEnabled(true);
|
||||
editor->scrollbar->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,20 +73,20 @@ void MemoryEditor::refresh() {
|
|||
return;
|
||||
}
|
||||
|
||||
editor->setDocumentOffset(strhex(addr->text().toUtf8().data()));
|
||||
editor->setOffset(strhex(addr->text().toUtf8().data()));
|
||||
editor->update();
|
||||
}
|
||||
|
||||
void MemoryEditor::sourceChanged(int index) {
|
||||
switch(index) { default:
|
||||
case 0: memorySource = SNES::Debugger::CPUBus; editor->setDocumentSize(16 * 1024 * 1024); break;
|
||||
case 1: memorySource = SNES::Debugger::APURAM; editor->setDocumentSize(64 * 1024); break;
|
||||
case 2: memorySource = SNES::Debugger::VRAM; editor->setDocumentSize(64 * 1024); break;
|
||||
case 3: memorySource = SNES::Debugger::OAM; editor->setDocumentSize(544); break;
|
||||
case 4: memorySource = SNES::Debugger::CGRAM; editor->setDocumentSize(512); break;
|
||||
case 0: memorySource = SNES::Debugger::CPUBus; editor->setSize(16 * 1024 * 1024); break;
|
||||
case 1: memorySource = SNES::Debugger::APURAM; editor->setSize(64 * 1024); break;
|
||||
case 2: memorySource = SNES::Debugger::VRAM; editor->setSize(64 * 1024); break;
|
||||
case 3: memorySource = SNES::Debugger::OAM; editor->setSize(544); break;
|
||||
case 4: memorySource = SNES::Debugger::CGRAM; editor->setSize(512); break;
|
||||
}
|
||||
|
||||
editor->setDocumentOffset(strhex(addr->text().toUtf8().data()));
|
||||
editor->setOffset(strhex(addr->text().toUtf8().data()));
|
||||
editor->update();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
class MemoryEditor : public QbWindow {
|
||||
class MemoryEditor : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QbWindow *window;
|
||||
QVBoxLayout *layout;
|
||||
QHBoxLayout *controlLayout;
|
||||
QWidget *spacer;
|
||||
|
@ -9,7 +10,7 @@ public:
|
|||
QComboBox *source;
|
||||
QCheckBox *autoUpdateBox;
|
||||
QPushButton *refreshButton;
|
||||
QbHexEdit *editor;
|
||||
HexEditor *editor;
|
||||
|
||||
void autoUpdate();
|
||||
void synchronize();
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
VramViewer::VramViewer() {
|
||||
setObjectName("vram-viewer");
|
||||
setWindowTitle("Video RAM Viewer");
|
||||
window = new QbWindow(config.geometry.vramViewer);
|
||||
window->setObjectName("vram-viewer");
|
||||
window->setWindowTitle("Video RAM Viewer");
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
layout->setAlignment(Qt::AlignCenter);
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(Style::WidgetSpacing);
|
||||
setLayout(layout);
|
||||
window->setLayout(layout);
|
||||
|
||||
controlLayout = new QHBoxLayout;
|
||||
controlLayout->setSizeConstraint(QLayout::SetMinimumSize);
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
class VramViewer : public QbWindow {
|
||||
class VramViewer : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QbWindow *window;
|
||||
QVBoxLayout *layout;
|
||||
QHBoxLayout *controlLayout;
|
||||
QRadioButton *depth2bpp;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
void Interface::video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height) {
|
||||
uint32_t *output;
|
||||
unsigned outwidth, outheight, outpitch;
|
||||
libfilter::filter.size(outwidth, outheight, width, height);
|
||||
filter.size(outwidth, outheight, width, height);
|
||||
|
||||
if(video.lock(output, outpitch, outwidth, outheight) == true) {
|
||||
libfilter::filter.render(output, outpitch, data, pitch, line, width, height);
|
||||
filter.render(output, outpitch, data, pitch, line, width, height);
|
||||
video.unlock();
|
||||
video.refresh();
|
||||
if(saveScreenshot == true) captureScreenshot(output, outpitch, outwidth, outheight);
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
#include "qbwindow.cpp"
|
||||
#include "qbhexedit.cpp"
|
||||
#include "qbdiskbrowser.cpp"
|
|
@ -1,3 +0,0 @@
|
|||
#include "qbwindow.moc"
|
||||
#include "qbhexedit.moc"
|
||||
#include "qbdiskbrowser.moc"
|
|
@ -1,178 +0,0 @@
|
|||
void QbDiskBrowserView::keyPressEvent(QKeyEvent *event) {
|
||||
//enhance consistency: OS X by default doesn't activate items for these keypresses
|
||||
if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
|
||||
parent.activateItem(currentIndex());
|
||||
return;
|
||||
}
|
||||
|
||||
//simulate popular file managers; backspace = go up one directory
|
||||
if(event->key() == Qt::Key_Backspace) {
|
||||
emit cdUp();
|
||||
return;
|
||||
}
|
||||
|
||||
//fallback: unrecognized keypresses get handled by the widget itself
|
||||
QTreeView::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void QbDiskBrowserView::keyReleaseEvent(QKeyEvent *event) {
|
||||
//act like a modal dialog; close window on escape keypress
|
||||
if(event->key() == Qt::Key_Escape) {
|
||||
parent.close();
|
||||
return;
|
||||
}
|
||||
|
||||
//fallback: unrecognized keypresses get handled by the widget itself
|
||||
QTreeView::keyReleaseEvent(event);
|
||||
}
|
||||
|
||||
void QbDiskBrowserView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) {
|
||||
QAbstractItemView::currentChanged(current, previous);
|
||||
emit changed(current);
|
||||
}
|
||||
|
||||
QbDiskBrowserView::QbDiskBrowserView(QbDiskBrowser &parent_) : parent(parent_) {
|
||||
}
|
||||
|
||||
QGroupBox *QbDiskBrowser::panel() { return group; }
|
||||
QPushButton *QbDiskBrowser::acceptButton() { return load; }
|
||||
|
||||
void QbDiskBrowser::setPath(const QString &reqPath) {
|
||||
disconnect(path, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePath()));
|
||||
|
||||
QString effectivePath = reqPath;
|
||||
if(effectivePath == "<root>") effectivePath = "";
|
||||
path->clear();
|
||||
model->setRootPath(effectivePath);
|
||||
view->setRootIndex(model->index(effectivePath));
|
||||
view->setCurrentIndex(view->rootIndex());
|
||||
view->setFocus();
|
||||
|
||||
if(effectivePath.length() > 0) {
|
||||
QDir directory(effectivePath);
|
||||
while(true) {
|
||||
path->addItem(directory.absolutePath());
|
||||
if(directory.isRoot()) break;
|
||||
directory.cdUp();
|
||||
}
|
||||
}
|
||||
path->addItem("<root>");
|
||||
|
||||
connect(path, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePath()));
|
||||
}
|
||||
|
||||
void QbDiskBrowser::setNameFilters(const QString &filters) {
|
||||
disconnect(filter, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFilter()));
|
||||
|
||||
filter->clear();
|
||||
|
||||
string filterData = filters.toUtf8().constData();
|
||||
lstring filterPart;
|
||||
filterPart.split(";;", filterData);
|
||||
|
||||
for(unsigned i = 0; i < filterPart.size(); i++) {
|
||||
filter->addItem(utf8() << filterPart[i]);
|
||||
}
|
||||
|
||||
connect(filter, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFilter()));
|
||||
updateFilter();
|
||||
}
|
||||
|
||||
void QbDiskBrowser::cdUp() {
|
||||
//if we aren't already at the root node, select the second node, which is one path higher than the current
|
||||
if(path->count() > 1) path->setCurrentIndex(1);
|
||||
}
|
||||
|
||||
void QbDiskBrowser::updatePath() {
|
||||
setPath(path->currentText());
|
||||
}
|
||||
|
||||
void QbDiskBrowser::updateFilter() {
|
||||
QString currentText = filter->currentText();
|
||||
if(currentText.length() == 0) {
|
||||
model->setNameFilters(QStringList() << "*");
|
||||
} else {
|
||||
string filters = currentText.toUtf8().constData();
|
||||
filters = substr(filters, strpos(filters, "("));
|
||||
ltrim(filters, "(");
|
||||
rtrim(filters, ")");
|
||||
lstring filterPart;
|
||||
filterPart.split(" ", filters);
|
||||
QStringList filterList;
|
||||
for(unsigned i = 0; i < filterPart.size(); i++) filterList << (const char*)filterPart[i];
|
||||
model->setNameFilters(filterList);
|
||||
}
|
||||
}
|
||||
|
||||
void QbDiskBrowser::activateItem(const QModelIndex &item) {
|
||||
emit itemActivated(model->filePath(item), model->isDir(item));
|
||||
}
|
||||
|
||||
void QbDiskBrowser::changeItem(const QModelIndex &item) {
|
||||
emit itemChanged(model->filePath(item), model->isDir(item));
|
||||
}
|
||||
|
||||
QbDiskBrowser::QbDiskBrowser() {
|
||||
layout = new QVBoxLayout;
|
||||
setLayout(layout);
|
||||
|
||||
topLayout = new QHBoxLayout;
|
||||
layout->addLayout(topLayout);
|
||||
|
||||
browseLayout = new QVBoxLayout;
|
||||
topLayout->addLayout(browseLayout);
|
||||
|
||||
path = new QComboBox;
|
||||
browseLayout->addWidget(path);
|
||||
|
||||
view = new QbDiskBrowserView(*this);
|
||||
view->setIconSize(QSize(16, 16));
|
||||
browseLayout->addWidget(view);
|
||||
|
||||
panelLayout = new QVBoxLayout;
|
||||
topLayout->addLayout(panelLayout);
|
||||
|
||||
group = new QGroupBox;
|
||||
group->hide();
|
||||
panelLayout->addWidget(group);
|
||||
|
||||
controlLayout = new QHBoxLayout;
|
||||
layout->addLayout(controlLayout);
|
||||
|
||||
filter = new QComboBox;
|
||||
filter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
controlLayout->addWidget(filter);
|
||||
|
||||
options = new QPushButton("Options");
|
||||
options->setEnabled(false);
|
||||
controlLayout->addWidget(options);
|
||||
|
||||
QMenu *menu = new QMenu;
|
||||
options->setMenu(menu);
|
||||
|
||||
load = new QPushButton("Load");
|
||||
load->setEnabled(false);
|
||||
controlLayout->addWidget(load);
|
||||
|
||||
cancel = new QPushButton("Cancel");
|
||||
controlLayout->addWidget(cancel);
|
||||
|
||||
model = new QFileSystemModel;
|
||||
model->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
|
||||
model->setNameFilterDisables(false);
|
||||
|
||||
view->setModel(model);
|
||||
view->setAllColumnsShowFocus(true);
|
||||
view->setUniformRowHeights(true);
|
||||
view->setSortingEnabled(true);
|
||||
view->sortByColumn(0, Qt::AscendingOrder); //sort files by name
|
||||
view->setColumnHidden(1, true); //hide size
|
||||
view->setColumnHidden(2, true); //hide type
|
||||
view->setColumnHidden(3, true); //hide last modified date
|
||||
view->setHeaderHidden(true);
|
||||
|
||||
connect(view, SIGNAL(cdUp()), this, SLOT(cdUp()));
|
||||
connect(view, SIGNAL(activated(const QModelIndex&)), this, SLOT(activateItem(const QModelIndex&)));
|
||||
connect(view, SIGNAL(changed(const QModelIndex&)), this, SLOT(changeItem(const QModelIndex&)));
|
||||
connect(cancel, SIGNAL(released()), this, SLOT(hide()));
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
class QbDiskBrowser;
|
||||
|
||||
class QbDiskBrowserView : public QTreeView {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QbDiskBrowser &parent;
|
||||
void keyPressEvent(QKeyEvent*);
|
||||
void keyReleaseEvent(QKeyEvent*);
|
||||
QbDiskBrowserView(QbDiskBrowser&);
|
||||
|
||||
signals:
|
||||
void cdUp();
|
||||
void changed(const QModelIndex&);
|
||||
|
||||
public slots:
|
||||
void currentChanged(const QModelIndex&, const QModelIndex&);
|
||||
};
|
||||
|
||||
class QbDiskBrowser : public QbWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QGroupBox *panel();
|
||||
QPushButton *acceptButton();
|
||||
|
||||
void setPath(const QString&);
|
||||
void setNameFilters(const QString&);
|
||||
|
||||
QbDiskBrowser();
|
||||
|
||||
signals:
|
||||
void itemActivated(const QString&, bool);
|
||||
void itemChanged(const QString&, bool);
|
||||
|
||||
public slots:
|
||||
void cdUp();
|
||||
void updatePath();
|
||||
void updateFilter();
|
||||
void activateItem(const QModelIndex&);
|
||||
void changeItem(const QModelIndex&);
|
||||
|
||||
protected:
|
||||
QVBoxLayout *layout;
|
||||
QHBoxLayout *topLayout;
|
||||
QVBoxLayout *browseLayout;
|
||||
QComboBox *path;
|
||||
QbDiskBrowserView *view;
|
||||
QVBoxLayout *panelLayout;
|
||||
QGroupBox *group;
|
||||
QHBoxLayout *controlLayout;
|
||||
QComboBox *filter;
|
||||
QPushButton *options;
|
||||
QPushButton *load;
|
||||
QPushButton *cancel;
|
||||
|
||||
QFileSystemModel *model;
|
||||
|
||||
friend class QbDiskBrowserListView;
|
||||
};
|
|
@ -1,157 +0,0 @@
|
|||
void QbHexEdit::keyPressEvent(QKeyEvent *event) {
|
||||
event->accept();
|
||||
|
||||
QTextCursor cursor = textCursor();
|
||||
unsigned x = cursor.position() % (editorLineWidth + 1); //+1 includes right-most cursor position
|
||||
unsigned y = cursor.position() / (editorLineWidth + 1); //eg '0123' has five possible cursor locations
|
||||
|
||||
int hexCode = -1;
|
||||
switch(event->key()) {
|
||||
case Qt::Key_0: hexCode = 0; break;
|
||||
case Qt::Key_1: hexCode = 1; break;
|
||||
case Qt::Key_2: hexCode = 2; break;
|
||||
case Qt::Key_3: hexCode = 3; break;
|
||||
case Qt::Key_4: hexCode = 4; break;
|
||||
case Qt::Key_5: hexCode = 5; break;
|
||||
case Qt::Key_6: hexCode = 6; break;
|
||||
case Qt::Key_7: hexCode = 7; break;
|
||||
case Qt::Key_8: hexCode = 8; break;
|
||||
case Qt::Key_9: hexCode = 9; break;
|
||||
case Qt::Key_A: hexCode = 10; break;
|
||||
case Qt::Key_B: hexCode = 11; break;
|
||||
case Qt::Key_C: hexCode = 12; break;
|
||||
case Qt::Key_D: hexCode = 13; break;
|
||||
case Qt::Key_E: hexCode = 14; break;
|
||||
case Qt::Key_F: hexCode = 15; break;
|
||||
}
|
||||
|
||||
if(hexCode != -1) {
|
||||
bool cursorOffsetValid = (x >= 11 && ((x - 11) % 3) != 2); //cannot input hex on top of offset or blank spaces
|
||||
if(cursorOffsetValid) {
|
||||
bool nibble = (x - 11) % 3; //0 = top nibble, 1 = bottom nibble
|
||||
unsigned cursorOffset = y * editorColumns + ((x - 11) / 3);
|
||||
unsigned effectiveOffset = documentOffset + cursorOffset;
|
||||
if(effectiveOffset >= documentSize) effectiveOffset %= documentSize;
|
||||
|
||||
uint8 data = reader(effectiveOffset);
|
||||
data &= (nibble == 0 ? 0x0f : 0xf0);
|
||||
data |= (nibble == 0 ? (hexCode << 4) : (hexCode << 0));
|
||||
writer(effectiveOffset, data);
|
||||
update();
|
||||
|
||||
x += (nibble == 0 ? 1 : 2);
|
||||
}
|
||||
}
|
||||
|
||||
if(event->key() == Qt::Key_Up) {
|
||||
if(y > 0) {
|
||||
y--; //move cursor up one row
|
||||
} else {
|
||||
//scroll document up one row
|
||||
if(documentOffset >= editorColumns) {
|
||||
documentOffset -= editorColumns;
|
||||
} else {
|
||||
documentOffset = 0;
|
||||
}
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
if(event->key() == Qt::Key_Down) {
|
||||
if(y < editorRows - 1) {
|
||||
y++; //move cursor down one row
|
||||
} else {
|
||||
//scroll document down one row
|
||||
if(documentOffset + editorRows * (editorColumns + 1) < documentSize) {
|
||||
documentOffset += editorColumns;
|
||||
} else {
|
||||
documentOffset = documentSize - (editorColumns * editorColumns);
|
||||
}
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
if(event->key() == Qt::Key_Left) {
|
||||
if(x < 11) {
|
||||
x = 11;
|
||||
} else if(x == 11) {
|
||||
//wrap cursor from left-most to right-most of previous row
|
||||
x = editorLineWidth - 1;
|
||||
if(y > 0) y--;
|
||||
} else {
|
||||
x -= (((x - 11) % 3) == 0) ? 2 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(event->key() == Qt::Key_Right) {
|
||||
if(x < 11) {
|
||||
x = 11;
|
||||
} else if(x >= editorLineWidth - 1) {
|
||||
//wrap cursor from right-most to left-most of next line
|
||||
x = 11;
|
||||
if(y < editorRows - 1) y++;
|
||||
} else {
|
||||
x += (((x - 11) % 3) == 1) ? 2 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
//normalize cursor to point to hex values
|
||||
if(x < 11) x = 11;
|
||||
|
||||
cursor.setPosition(y * (editorLineWidth + 1) + x);
|
||||
setTextCursor(cursor);
|
||||
}
|
||||
|
||||
void QbHexEdit::setDocumentSize(unsigned size) {
|
||||
documentSize = size;
|
||||
}
|
||||
|
||||
void QbHexEdit::setDocumentOffset(unsigned offset) {
|
||||
documentOffset = offset;
|
||||
if(documentOffset >= documentSize) documentOffset %= documentSize;
|
||||
}
|
||||
|
||||
void QbHexEdit::setColumns(unsigned columns) {
|
||||
editorColumns = columns;
|
||||
editorLineWidth = 11 + 3 * columns - 1; //"0123:4567 00 01 02 03 ... 0f"
|
||||
}
|
||||
|
||||
void QbHexEdit::setRows(unsigned rows) {
|
||||
editorRows = rows;
|
||||
}
|
||||
|
||||
void QbHexEdit::update() {
|
||||
string output;
|
||||
char temp[256];
|
||||
unsigned offset = documentOffset;
|
||||
|
||||
for(unsigned y = 0; y < editorRows; y++) {
|
||||
sprintf(temp, "%.4x:%.4x", (uint16)(offset >> 16), (uint16)(offset >> 0));
|
||||
output << "<font color='#606060'>" << temp << "</font> ";
|
||||
|
||||
for(unsigned x = 0; x < editorColumns; x++) {
|
||||
sprintf(temp, "%.2x", reader(offset++));
|
||||
output << "<font color='" << ((x & 1) ? "#000080" : "#0000ff") << "'>" << temp << "</font>";
|
||||
if(offset >= documentSize) goto endOfDocument;
|
||||
if(x != editorColumns - 1) output << " ";
|
||||
}
|
||||
|
||||
if(y != editorRows - 1) output << "<br>";
|
||||
} endOfDocument:
|
||||
|
||||
setHtml((const char*)output);
|
||||
|
||||
unsigned scrollMin = 0;
|
||||
unsigned scrollMax = documentSize / editorColumns;
|
||||
unsigned scrollPos = documentOffset / editorColumns;
|
||||
scrollBar->setRange(scrollMin, scrollMax);
|
||||
scrollBar->setSliderPosition(0); //(scrollPos);
|
||||
}
|
||||
|
||||
QbHexEdit::QbHexEdit() {
|
||||
scrollBar = new QbHexEditScrollBar;
|
||||
scrollBar->setEnabled(false);
|
||||
setVerticalScrollBar(scrollBar);
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
class QbHexEditScrollBar : public QScrollBar {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
};
|
||||
|
||||
class QbHexEdit : public QTextEdit {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
function<uint8 (unsigned)> reader;
|
||||
function<void (unsigned, uint8)> writer;
|
||||
|
||||
void keyPressEvent(QKeyEvent*);
|
||||
|
||||
void setDocumentSize(unsigned);
|
||||
void setDocumentOffset(unsigned);
|
||||
|
||||
void setColumns(unsigned);
|
||||
void setRows(unsigned);
|
||||
|
||||
void update();
|
||||
QbHexEdit();
|
||||
|
||||
private:
|
||||
QbHexEditScrollBar *scrollBar;
|
||||
|
||||
unsigned documentSize;
|
||||
unsigned documentOffset;
|
||||
|
||||
unsigned editorColumns;
|
||||
unsigned editorRows;
|
||||
unsigned editorLineWidth;
|
||||
};
|
|
@ -1,39 +0,0 @@
|
|||
void QbWindow::showHidden(bool state) {
|
||||
#if !defined(PLATFORM_X)
|
||||
if(state) {
|
||||
setWindowOpacity(0.0);
|
||||
} else {
|
||||
setWindowOpacity(1.0);
|
||||
}
|
||||
showNormal();
|
||||
#endif
|
||||
}
|
||||
|
||||
void QbWindow::showAt(double x, double y) {
|
||||
#if defined(PLATFORM_X)
|
||||
resize(0, 0);
|
||||
showNormal();
|
||||
#else
|
||||
if(isVisible() == false) showHidden(true);
|
||||
resize(0, 0);
|
||||
QApplication::processEvents();
|
||||
|
||||
QRect deskRect = QApplication::desktop()->availableGeometry(this);
|
||||
|
||||
//convert cartesian coordinates to linear scale (-1.0 -> 0.0, 0.0 -> 0.5, +1.0 -> +1.0)
|
||||
x = (x / 2.0) + 0.5;
|
||||
y = (y / 2.0) + 0.5;
|
||||
|
||||
unsigned availableWidth = deskRect.width() - frameSize().width();
|
||||
unsigned availableHeight = deskRect.height() - frameSize().height();
|
||||
|
||||
move(deskRect.left() + availableWidth * x, deskRect.top() + availableHeight * y);
|
||||
showHidden(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void QbWindow::setFocus() {
|
||||
QApplication::processEvents();
|
||||
activateWindow();
|
||||
raise();
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
class QbWindow : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
void showHidden(bool);
|
||||
void showAt(double x, double y);
|
||||
void setFocus();
|
||||
};
|
|
@ -1,16 +1,28 @@
|
|||
Colortable colortable;
|
||||
Filter filter;
|
||||
|
||||
void Colortable::set_format(Format format_) { format = format_; }
|
||||
void Colortable::set_contrast(signed contrast_) { contrast = contrast_; }
|
||||
void Colortable::set_brightness(signed brightness_) { brightness = brightness_; }
|
||||
void Colortable::set_gamma(signed gamma_) { gamma = gamma_; }
|
||||
const uint8_t Filter::gamma_ramp_table[32] = {
|
||||
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
|
||||
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
|
||||
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
|
||||
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
|
||||
};
|
||||
|
||||
void Colortable::enable_gamma_ramp(bool value) { gamma_ramp = value; }
|
||||
void Colortable::enable_sepia(bool value) { sepia = value; }
|
||||
void Colortable::enable_grayscale(bool value) { grayscale = value; }
|
||||
void Colortable::enable_invert(bool value) { invert = value; }
|
||||
uint8_t Filter::contrast_adjust(uint8_t input) {
|
||||
signed result = input - contrast + (2 * contrast * input + 127) / 255;
|
||||
return max(0, min(255, result));
|
||||
}
|
||||
|
||||
void Colortable::update() {
|
||||
uint8_t Filter::brightness_adjust(uint8_t input) {
|
||||
signed result = input + brightness;
|
||||
return max(0, min(255, result));
|
||||
}
|
||||
|
||||
uint8_t Filter::gamma_adjust(uint8_t input) {
|
||||
signed result = (signed)(pow(((double)input / 255.0), (double)gamma / 100.0) * 255.0 + 0.5);
|
||||
return max(0, min(255, result));
|
||||
}
|
||||
|
||||
void Filter::colortable_update() {
|
||||
double kr = 0.2126, kb = 0.0722, kg = (1.0 - kr - kb); //luminance weights
|
||||
|
||||
for(unsigned i = 0; i < 32768; i++) {
|
||||
|
@ -73,34 +85,61 @@ void Colortable::update() {
|
|||
b ^= 0xff;
|
||||
}
|
||||
|
||||
switch(format) {
|
||||
case RGB555: {
|
||||
r >>= 3;
|
||||
g >>= 3;
|
||||
b >>= 3;
|
||||
table[i] = (r << 10) | (g << 5) | (b);
|
||||
} break;
|
||||
|
||||
case RGB565: {
|
||||
r >>= 3;
|
||||
g >>= 2;
|
||||
b >>= 3;
|
||||
table[i] = (r << 11) | (g << 5) | (b);
|
||||
} break;
|
||||
|
||||
case RGB888: {
|
||||
table[i] = (r << 16) | (g << 8) | (b);
|
||||
} break;
|
||||
|
||||
default: {
|
||||
table[i] = ~0;
|
||||
} break;
|
||||
}
|
||||
colortable[i] = (r << 16) | (g << 8) | (b);
|
||||
}
|
||||
}
|
||||
|
||||
Colortable::Colortable() {
|
||||
table = new uint32_t[32768];
|
||||
void Filter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) {
|
||||
if(renderer > 0) {
|
||||
return dl_size(renderer - 1, outwidth, outheight, width, height);
|
||||
}
|
||||
|
||||
outwidth = width;
|
||||
outheight = height;
|
||||
}
|
||||
|
||||
void Filter::render(
|
||||
uint32_t *output, unsigned outpitch,
|
||||
const uint16_t *input, unsigned pitch,
|
||||
const unsigned *line, unsigned width, unsigned height
|
||||
) {
|
||||
if(renderer > 0) {
|
||||
return dl_render(renderer - 1, output, outpitch, input, pitch, line, width, height);
|
||||
}
|
||||
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
if(width == 512 && line[y] == 256) {
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
uint16_t p = *input++;
|
||||
*output++ = colortable[p];
|
||||
*output++ = colortable[p];
|
||||
}
|
||||
input += 256;
|
||||
} else {
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint16_t p = *input++;
|
||||
*output++ = colortable[p];
|
||||
}
|
||||
}
|
||||
input += pitch - width;
|
||||
output += outpitch - width;
|
||||
}
|
||||
}
|
||||
|
||||
QWidget* Filter::settings() {
|
||||
if(renderer > 0) {
|
||||
return dl_settings(renderer - 1);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Filter::Filter() {
|
||||
renderer = 0;
|
||||
|
||||
contrast = 0;
|
||||
brightness = 0;
|
||||
gamma = 100;
|
||||
|
@ -109,30 +148,26 @@ Colortable::Colortable() {
|
|||
sepia = false;
|
||||
grayscale = false;
|
||||
invert = false;
|
||||
|
||||
colortable = new uint32_t[32768];
|
||||
colortable_update();
|
||||
|
||||
if(open("snesfilter")) {
|
||||
dl_supported = sym("snesfilter_supported");
|
||||
dl_colortable = sym("snesfilter_colortable");
|
||||
dl_configuration = sym("snesfilter_configuration");
|
||||
dl_size = sym("snesfilter_size");
|
||||
dl_render = sym("snesfilter_render");
|
||||
dl_settings = sym("snesfilter_settings");
|
||||
|
||||
dl_colortable(colortable);
|
||||
dl_configuration(config);
|
||||
} else {
|
||||
config.video.windowed.swFilter = 0;
|
||||
config.video.fullscreen.swFilter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Colortable::~Colortable() {
|
||||
delete[] table;
|
||||
}
|
||||
|
||||
const uint8_t Colortable::gamma_ramp_table[32] = {
|
||||
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
|
||||
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
|
||||
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
|
||||
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
|
||||
};
|
||||
|
||||
uint8_t Colortable::contrast_adjust(uint8_t input) {
|
||||
signed result = input - contrast + (2 * contrast * input + 127) / 255;
|
||||
return max(0, min(255, result));
|
||||
}
|
||||
|
||||
uint8_t Colortable::brightness_adjust(uint8_t input) {
|
||||
signed result = input + brightness;
|
||||
return max(0, min(255, result));
|
||||
}
|
||||
|
||||
uint8_t Colortable::gamma_adjust(uint8_t input) {
|
||||
signed result = (signed)(pow(((double)input / 255.0), (double)gamma / 100.0) * 255.0 + 0.5);
|
||||
return max(0, min(255, result));
|
||||
Filter::~Filter() {
|
||||
delete[] colortable;
|
||||
}
|