mirror of
https://github.com/isledecomp/isle-portable.git
synced 2026-01-11 18:41:14 +00:00
clean up
This commit is contained in:
parent
8f9f7d55fe
commit
214f4f8584
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -48,7 +48,7 @@ jobs:
|
||||
- { name: 'Nintendo 3DS', os: 'ubuntu-latest', generator: 'Ninja', dx5: false, config: false, n3ds: true, werror: true, clang-tidy: false, container: 'devkitpro/devkitarm:latest', cmake-args: '-DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/3DS.cmake' }
|
||||
- { name: 'Xbox One', os: 'windows-latest', generator: 'Visual Studio 17 2022', dx5: false, config: false, msvc: true, werror: false, clang-tidy: false, vc-arch: 'amd64', cmake-args: '-DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0.26100.0', xbox-one: true}
|
||||
- { name: 'Android', os: 'ubuntu-latest', generator: 'Ninja', dx5: false, config: false, android: true, werror: true, clang-tidy: false,}
|
||||
- { name: 'Vita', os: 'ubuntu-latest', generator: 'Ninja', dx5: false, config: false, vita: true, werror: true, clang-tidy: false, cmake-args: '--preset vita'}
|
||||
- { name: 'Vita', os: 'ubuntu-latest', generator: 'Ninja', dx5: false, config: false, vita: true, werror: true, clang-tidy: false, cmake-args: '--toolchain /usr/local/vitasdk/share/vita.toolchain.cmake'}
|
||||
steps:
|
||||
- name: Setup vcvars
|
||||
if: ${{ !!matrix.msvc }}
|
||||
@ -122,7 +122,7 @@ jobs:
|
||||
export VITASDK=/usr/local/vitasdk
|
||||
export PATH=$VITASDK/bin:$PATH
|
||||
echo "VITASDK=/usr/local/vitasdk" >> $GITHUB_ENV
|
||||
echo "PATH=$VITASDK/bin:$PATH" >> $GITHUB_ENV
|
||||
echo "$VITASDK/bin" >> $GITHUB_PATH
|
||||
./install-all.sh
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.25...4.0 FATAL_ERROR)
|
||||
|
||||
project(isle LANGUAGES CXX C VERSION 0.1)
|
||||
project(isle LANGUAGES CXX C ASM VERSION 0.1)
|
||||
|
||||
if (IOS)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO)
|
||||
@ -903,7 +903,6 @@ if(VITA)
|
||||
|
||||
set(VPK_FILE_ARGS "")
|
||||
file(GLOB_RECURSE SCE_SYS_FILES packaging/vita/sce_sys/*)
|
||||
file(GLOB_RECURSE SCE_MODULE_FILES packaging/vita/sce_module/*)
|
||||
foreach(FILE ${SCE_SYS_FILES} ${SCE_MODULE_FILES})
|
||||
file(RELATIVE_PATH REL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/packaging/vita/ ${FILE})
|
||||
list(APPEND VPK_FILE_ARGS "FILE")
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
{
|
||||
"version": 5,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 22,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "base",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"name": "release",
|
||||
"inherits": ["base"],
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "debug",
|
||||
"inherits": ["base"],
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "mingw64",
|
||||
"inherits": ["release"],
|
||||
"toolchainFile": "CMake/cmake-toolchain-mingw64-x86_64.cmake"
|
||||
},
|
||||
{
|
||||
"name": "mingw64-debug",
|
||||
"inherits": ["debug", "mingw64"]
|
||||
},
|
||||
|
||||
{
|
||||
"name": "vita",
|
||||
"inherits": ["release"],
|
||||
"toolchainFile": "$env{VITASDK}/share/vita.toolchain.cmake"
|
||||
},
|
||||
{
|
||||
"name": "vita-debug",
|
||||
"inherits": ["debug", "vita"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -13,31 +13,24 @@ FetchContent_Declare(
|
||||
)
|
||||
FetchContent_MakeAvailable(ScePaf_External)
|
||||
|
||||
add_library(iniparser_paf
|
||||
iniparser_paf/src/dictionary.c
|
||||
iniparser_paf/src/iniparser.c
|
||||
)
|
||||
|
||||
target_compile_options(iniparser_paf PRIVATE -O3)
|
||||
|
||||
target_link_libraries(iniparser_paf
|
||||
ScePafStdc_stub
|
||||
SceLibKernel_stub
|
||||
SceLibc_stub
|
||||
SceFios2_stub
|
||||
)
|
||||
|
||||
target_include_directories(iniparser_paf PUBLIC
|
||||
iniparser_paf/src/
|
||||
)
|
||||
|
||||
target_compile_options(iniparser_paf PRIVATE
|
||||
-Wl,-q -Wall -fno-builtin -fshort-wchar -Wno-unused-function -Wno-sign-compare
|
||||
FetchContent_Declare(
|
||||
iniparser_paf
|
||||
GIT_REPOSITORY "https://gitlab.com/iniparser/iniparser.git"
|
||||
GIT_TAG "v4.2.6"
|
||||
UPDATE_DISCONNECTED TRUE
|
||||
EXCLUDE_FROM_ALL
|
||||
PATCH_COMMAND patch -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/iniparser_paf.patch
|
||||
)
|
||||
block()
|
||||
set(BUILD_DOCS off)
|
||||
set(BUILD_SHARED_LIBS off)
|
||||
FetchContent_MakeAvailable(iniparser_paf)
|
||||
endblock()
|
||||
|
||||
add_executable(isle-config
|
||||
src/paf_runtime.cpp
|
||||
src/app.cpp
|
||||
src/main.cpp
|
||||
)
|
||||
|
||||
set_target_properties(isle-config PROPERTIES
|
||||
@ -66,7 +59,7 @@ target_link_libraries(isle-config PRIVATE
|
||||
SceFios2_stub
|
||||
SceLibc_stub
|
||||
|
||||
iniparser_paf
|
||||
iniparser_paf-static
|
||||
)
|
||||
|
||||
vita_create_self(isle-config.self isle-config
|
||||
|
||||
53
CONFIG/vita/iniparser_paf.patch
Normal file
53
CONFIG/vita/iniparser_paf.patch
Normal file
@ -0,0 +1,53 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 4f7fdba..d388fc8 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
|
||||
project(
|
||||
- iniparser
|
||||
+ iniparser_paf
|
||||
DESCRIPTION "C library for parsing INI-style files"
|
||||
HOMEPAGE_URL https://gitlab.com/iniparser/iniparser/
|
||||
LANGUAGES C
|
||||
@@ -108,6 +108,19 @@ foreach(TARGET_TYPE ${TARGET_TYPES})
|
||||
-pedantic)
|
||||
endif(UNIX)
|
||||
|
||||
+ if(VITA)
|
||||
+ target_compile_options(
|
||||
+ ${TARGET_NAME}
|
||||
+ PRIVATE -Wl,-q -Wall -fshort-wchar)
|
||||
+ target_link_libraries(
|
||||
+ ${TARGET_NAME}
|
||||
+ PRIVATE SceLibc_stub
|
||||
+ )
|
||||
+ target_link_options(
|
||||
+ ${TARGET_NAME}
|
||||
+ PUBLIC -nostartfiles -nostdlib)
|
||||
+ endif()
|
||||
+
|
||||
# install targets
|
||||
install(
|
||||
TARGETS ${TARGET_NAME}
|
||||
diff --git a/src/iniparser.c b/src/iniparser.c
|
||||
index 1086b46..8e0e9c2 100644
|
||||
--- a/src/iniparser.c
|
||||
+++ b/src/iniparser.c
|
||||
@@ -14,6 +14,15 @@
|
||||
#include <inttypes.h>
|
||||
#include "iniparser.h"
|
||||
|
||||
+#ifdef __vita__
|
||||
+extern FILE* _Stderr;
|
||||
+#undef stderr
|
||||
+#define stderr _Stderr
|
||||
+extern const char _Ctype[];
|
||||
+#undef __locale_ctype_ptr
|
||||
+#define __locale_ctype_ptr() _Ctype
|
||||
+#endif
|
||||
+
|
||||
/*---------------------------- Defines -------------------------------------*/
|
||||
#define ASCIILINESZ (1024)
|
||||
#define INI_INVALID_KEY ((char*)-1)
|
||||
@ -1,381 +0,0 @@
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@file dictionary.c
|
||||
@author N. Devillard
|
||||
@brief Implements a dictionary for string variables.
|
||||
|
||||
This module implements a simple dictionary object, i.e. a list
|
||||
of string/string associations. This object is useful to store e.g.
|
||||
informations retrieved from a configuration file (ini files).
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Includes
|
||||
---------------------------------------------------------------------------*/
|
||||
#include "dictionary.h"
|
||||
|
||||
#include "pafstd.h"
|
||||
|
||||
/** Minimal allocated number of entries in a dictionary */
|
||||
#define DICTMINSZ 128
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Private functions
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Duplicate a string
|
||||
@param s String to duplicate
|
||||
@return Pointer to a newly allocated string, to be freed with free()
|
||||
|
||||
This is a replacement for strdup(). This implementation is provided
|
||||
for systems that do not have it.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
static char * xstrdup(const char * s)
|
||||
{
|
||||
char * t ;
|
||||
size_t len ;
|
||||
if (!s)
|
||||
return NULL ;
|
||||
|
||||
len = strlen(s) + 1 ;
|
||||
t = (char*) malloc(len) ;
|
||||
if (t) {
|
||||
memcpy(t, s, len) ;
|
||||
}
|
||||
return t ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Double the size of the dictionary
|
||||
@param d Dictionary to grow
|
||||
@return This function returns non-zero in case of failure
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
static int dictionary_grow(dictionary * d)
|
||||
{
|
||||
char ** new_val ;
|
||||
char ** new_key ;
|
||||
unsigned * new_hash ;
|
||||
|
||||
new_val = (char**) calloc(d->size * 2, sizeof *d->val);
|
||||
new_key = (char**) calloc(d->size * 2, sizeof *d->key);
|
||||
new_hash = (unsigned*) calloc(d->size * 2, sizeof *d->hash);
|
||||
if (!new_val || !new_key || !new_hash) {
|
||||
/* An allocation failed, leave the dictionary unchanged */
|
||||
if (new_val)
|
||||
free(new_val);
|
||||
if (new_key)
|
||||
free(new_key);
|
||||
if (new_hash)
|
||||
free(new_hash);
|
||||
return -1 ;
|
||||
}
|
||||
/* Initialize the newly allocated space */
|
||||
memcpy(new_val, d->val, d->size * sizeof(char *));
|
||||
memcpy(new_key, d->key, d->size * sizeof(char *));
|
||||
memcpy(new_hash, d->hash, d->size * sizeof(unsigned));
|
||||
/* Delete previous data */
|
||||
free(d->val);
|
||||
free(d->key);
|
||||
free(d->hash);
|
||||
/* Actually update the dictionary */
|
||||
d->size *= 2 ;
|
||||
d->val = new_val;
|
||||
d->key = new_key;
|
||||
d->hash = new_hash;
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Function codes
|
||||
---------------------------------------------------------------------------*/
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Compute the hash key for a string.
|
||||
@param key Character string to use for key.
|
||||
@return 1 unsigned int on at least 32 bits.
|
||||
|
||||
This hash function has been taken from an Article in Dr Dobbs Journal.
|
||||
This is normally a collision-free function, distributing keys evenly.
|
||||
The key is stored anyway in the struct so that collision can be avoided
|
||||
by comparing the key itself in last resort.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
unsigned dictionary_hash(const char * key)
|
||||
{
|
||||
size_t len ;
|
||||
unsigned hash ;
|
||||
size_t i ;
|
||||
|
||||
if (!key)
|
||||
return 0 ;
|
||||
|
||||
len = strlen(key);
|
||||
for (hash=0, i=0 ; i<len ; i++) {
|
||||
hash += (unsigned)key[i] ;
|
||||
hash += (hash<<10);
|
||||
hash ^= (hash>>6) ;
|
||||
}
|
||||
hash += (hash <<3);
|
||||
hash ^= (hash >>11);
|
||||
hash += (hash <<15);
|
||||
return hash ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Create a new dictionary object.
|
||||
@param size Optional initial size of the dictionary.
|
||||
@return 1 newly allocated dictionary object.
|
||||
|
||||
This function allocates a new dictionary object of given size and returns
|
||||
it. If you do not know in advance (roughly) the number of entries in the
|
||||
dictionary, give size=0.
|
||||
*/
|
||||
/*-------------------------------------------------------------------------*/
|
||||
dictionary * dictionary_new(size_t size)
|
||||
{
|
||||
dictionary * d ;
|
||||
|
||||
/* If no size was specified, allocate space for DICTMINSZ */
|
||||
if (size<DICTMINSZ) size=DICTMINSZ ;
|
||||
|
||||
d = (dictionary*) calloc(1, sizeof *d) ;
|
||||
|
||||
if (d) {
|
||||
d->size = size ;
|
||||
d->val = (char**) calloc(size, sizeof *d->val);
|
||||
d->key = (char**) calloc(size, sizeof *d->key);
|
||||
d->hash = (unsigned*) calloc(size, sizeof *d->hash);
|
||||
if (!d->size || !d->val || !d->hash) {
|
||||
free((void *) d->size);
|
||||
free((void *) d->val);
|
||||
free((void *) d->hash);
|
||||
free(d);
|
||||
d = NULL;
|
||||
}
|
||||
}
|
||||
return d ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Delete a dictionary object
|
||||
@param d dictionary object to deallocate.
|
||||
@return void
|
||||
|
||||
Deallocate a dictionary object and all memory associated to it.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void dictionary_del(dictionary * d)
|
||||
{
|
||||
size_t i ;
|
||||
|
||||
if (d==NULL) return ;
|
||||
for (i=0 ; i<d->size ; i++) {
|
||||
if (d->key[i]!=NULL)
|
||||
free(d->key[i]);
|
||||
if (d->val[i]!=NULL)
|
||||
free(d->val[i]);
|
||||
}
|
||||
free(d->val);
|
||||
free(d->key);
|
||||
free(d->hash);
|
||||
free(d);
|
||||
return ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get a value from a dictionary.
|
||||
@param d dictionary object to search.
|
||||
@param key Key to look for in the dictionary.
|
||||
@param def Default value to return if key not found.
|
||||
@return 1 pointer to internally allocated character string.
|
||||
|
||||
This function locates a key in a dictionary and returns a pointer to its
|
||||
value, or the passed 'def' pointer if no such key can be found in
|
||||
dictionary. The returned character pointer points to data internal to the
|
||||
dictionary object, you should not try to free it or modify it.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
const char * dictionary_get(const dictionary * d, const char * key, const char * def)
|
||||
{
|
||||
unsigned hash ;
|
||||
size_t i ;
|
||||
|
||||
if(d == NULL || key == NULL)
|
||||
return def ;
|
||||
|
||||
hash = dictionary_hash(key);
|
||||
for (i=0 ; i<d->size ; i++) {
|
||||
if (d->key[i]==NULL)
|
||||
continue ;
|
||||
/* Compare hash */
|
||||
if (hash==d->hash[i]) {
|
||||
/* Compare string, to avoid hash collisions */
|
||||
if (!strcmp(key, d->key[i])) {
|
||||
return d->val[i] ;
|
||||
}
|
||||
}
|
||||
}
|
||||
return def ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Set a value in a dictionary.
|
||||
@param d dictionary object to modify.
|
||||
@param key Key to modify or add.
|
||||
@param val Value to add.
|
||||
@return int 0 if Ok, anything else otherwise
|
||||
|
||||
If the given key is found in the dictionary, the associated value is
|
||||
replaced by the provided one. If the key cannot be found in the
|
||||
dictionary, it is added to it.
|
||||
|
||||
It is Ok to provide a NULL value for val, but NULL values for the dictionary
|
||||
or the key are considered as errors: the function will return immediately
|
||||
in such a case.
|
||||
|
||||
Notice that if you dictionary_set a variable to NULL, a call to
|
||||
dictionary_get will return a NULL value: the variable will be found, and
|
||||
its value (NULL) is returned. In other words, setting the variable
|
||||
content to NULL is equivalent to deleting the variable from the
|
||||
dictionary. It is not possible (in this implementation) to have a key in
|
||||
the dictionary without value.
|
||||
|
||||
This function returns non-zero in case of failure.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int dictionary_set(dictionary * d, const char * key, const char * val)
|
||||
{
|
||||
size_t i ;
|
||||
unsigned hash ;
|
||||
|
||||
if (d==NULL || key==NULL) return -1 ;
|
||||
|
||||
/* Compute hash for this key */
|
||||
hash = dictionary_hash(key) ;
|
||||
/* Find if value is already in dictionary */
|
||||
if (d->n>0) {
|
||||
for (i=0 ; i<d->size ; i++) {
|
||||
if (d->key[i]==NULL)
|
||||
continue ;
|
||||
if (hash==d->hash[i]) { /* Same hash value */
|
||||
if (!strcmp(key, d->key[i])) { /* Same key */
|
||||
/* Found a value: modify and return */
|
||||
if (d->val[i]!=NULL)
|
||||
free(d->val[i]);
|
||||
d->val[i] = (val ? xstrdup(val) : NULL);
|
||||
/* Value has been modified: return */
|
||||
return 0 ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Add a new value */
|
||||
/* See if dictionary needs to grow */
|
||||
if (d->n==d->size) {
|
||||
/* Reached maximum size: reallocate dictionary */
|
||||
if (dictionary_grow(d) != 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Insert key in the first empty slot. Start at d->n and wrap at
|
||||
d->size. Because d->n < d->size this will necessarily
|
||||
terminate. */
|
||||
for (i=d->n ; d->key[i] ; ) {
|
||||
if(++i == d->size) i = 0;
|
||||
}
|
||||
/* Copy key */
|
||||
d->key[i] = xstrdup(key);
|
||||
d->val[i] = (val ? xstrdup(val) : NULL) ;
|
||||
d->hash[i] = hash;
|
||||
d->n ++ ;
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Delete a key in a dictionary
|
||||
@param d dictionary object to modify.
|
||||
@param key Key to remove.
|
||||
@return void
|
||||
|
||||
This function deletes a key in a dictionary. Nothing is done if the
|
||||
key cannot be found.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void dictionary_unset(dictionary * d, const char * key)
|
||||
{
|
||||
unsigned hash ;
|
||||
size_t i ;
|
||||
|
||||
if (key == NULL || d == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
hash = dictionary_hash(key);
|
||||
for (i=0 ; i<d->size ; i++) {
|
||||
if (d->key[i]==NULL)
|
||||
continue ;
|
||||
/* Compare hash */
|
||||
if (hash==d->hash[i]) {
|
||||
/* Compare string, to avoid hash collisions */
|
||||
if (!strcmp(key, d->key[i])) {
|
||||
/* Found key */
|
||||
break ;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i>=d->size)
|
||||
/* Key not found */
|
||||
return ;
|
||||
|
||||
free(d->key[i]);
|
||||
d->key[i] = NULL ;
|
||||
if (d->val[i]!=NULL) {
|
||||
free(d->val[i]);
|
||||
d->val[i] = NULL ;
|
||||
}
|
||||
d->hash[i] = 0 ;
|
||||
d->n -- ;
|
||||
return ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Dump a dictionary to an opened file pointer.
|
||||
@param d Dictionary to dump
|
||||
@param f Opened file pointer.
|
||||
@return void
|
||||
|
||||
Dumps a dictionary onto an opened file pointer. Key pairs are printed out
|
||||
as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
|
||||
output file pointers.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void dictionary_dump(const dictionary * d, FILE * out)
|
||||
{
|
||||
size_t i ;
|
||||
|
||||
if (d==NULL || out==NULL) return ;
|
||||
if (d->n<1) {
|
||||
fprintf(out, "empty dictionary\n");
|
||||
return ;
|
||||
}
|
||||
for (i=0 ; i<d->size ; i++) {
|
||||
if (d->key[i]) {
|
||||
fprintf(out, "%20s\t[%s]\n",
|
||||
d->key[i],
|
||||
d->val[i] ? d->val[i] : "UNDEF");
|
||||
}
|
||||
}
|
||||
return ;
|
||||
}
|
||||
@ -1,167 +0,0 @@
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@file dictionary.h
|
||||
@author N. Devillard
|
||||
@brief Implements a dictionary for string variables.
|
||||
|
||||
This module implements a simple dictionary object, i.e. a list
|
||||
of string/string associations. This object is useful to store e.g.
|
||||
informations retrieved from a configuration file (ini files).
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _DICTIONARY_H_
|
||||
#define _DICTIONARY_H_
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Includes
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
#include "pafstd.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
New types
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Dictionary object
|
||||
|
||||
This object contains a list of string/string associations. Each
|
||||
association is identified by a unique string key. Looking up values
|
||||
in the dictionary is speeded up by the use of a (hopefully collision-free)
|
||||
hash function.
|
||||
*/
|
||||
/*-------------------------------------------------------------------------*/
|
||||
typedef struct _dictionary_ {
|
||||
unsigned n; /** Number of entries in dictionary */
|
||||
size_t size; /** Storage size */
|
||||
char** val; /** List of string values */
|
||||
char** key; /** List of string keys */
|
||||
unsigned* hash; /** List of hash values for keys */
|
||||
} dictionary;
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Function prototypes
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Compute the hash key for a string.
|
||||
@param key Character string to use for key.
|
||||
@return 1 unsigned int on at least 32 bits.
|
||||
|
||||
This hash function has been taken from an Article in Dr Dobbs Journal.
|
||||
This is normally a collision-free function, distributing keys evenly.
|
||||
The key is stored anyway in the struct so that collision can be avoided
|
||||
by comparing the key itself in last resort.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
unsigned dictionary_hash(const char* key);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Create a new dictionary object.
|
||||
@param size Optional initial size of the dictionary.
|
||||
@return 1 newly allocated dictionary object.
|
||||
|
||||
This function allocates a new dictionary object of given size and returns
|
||||
it. If you do not know in advance (roughly) the number of entries in the
|
||||
dictionary, give size=0.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
dictionary* dictionary_new(size_t size);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Delete a dictionary object
|
||||
@param d dictionary object to deallocate.
|
||||
@return void
|
||||
|
||||
Deallocate a dictionary object and all memory associated to it.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void dictionary_del(dictionary* vd);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get a value from a dictionary.
|
||||
@param d dictionary object to search.
|
||||
@param key Key to look for in the dictionary.
|
||||
@param def Default value to return if key not found.
|
||||
@return 1 pointer to internally allocated character string.
|
||||
|
||||
This function locates a key in a dictionary and returns a pointer to its
|
||||
value, or the passed 'def' pointer if no such key can be found in
|
||||
dictionary. The returned character pointer points to data internal to the
|
||||
dictionary object, you should not try to free it or modify it.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
const char* dictionary_get(const dictionary* d, const char* key, const char* def);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Set a value in a dictionary.
|
||||
@param d dictionary object to modify.
|
||||
@param key Key to modify or add.
|
||||
@param val Value to add.
|
||||
@return int 0 if Ok, anything else otherwise
|
||||
|
||||
If the given key is found in the dictionary, the associated value is
|
||||
replaced by the provided one. If the key cannot be found in the
|
||||
dictionary, it is added to it.
|
||||
|
||||
It is Ok to provide a NULL value for val, but NULL values for the dictionary
|
||||
or the key are considered as errors: the function will return immediately
|
||||
in such a case.
|
||||
|
||||
Notice that if you dictionary_set a variable to NULL, a call to
|
||||
dictionary_get will return a NULL value: the variable will be found, and
|
||||
its value (NULL) is returned. In other words, setting the variable
|
||||
content to NULL is equivalent to deleting the variable from the
|
||||
dictionary. It is not possible (in this implementation) to have a key in
|
||||
the dictionary without value.
|
||||
|
||||
This function returns non-zero in case of failure.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int dictionary_set(dictionary* vd, const char* key, const char* val);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Delete a key in a dictionary
|
||||
@param d dictionary object to modify.
|
||||
@param key Key to remove.
|
||||
@return void
|
||||
|
||||
This function deletes a key in a dictionary. Nothing is done if the
|
||||
key cannot be found.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void dictionary_unset(dictionary* d, const char* key);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Dump a dictionary to an opened file pointer.
|
||||
@param d Dictionary to dump
|
||||
@param f Opened file pointer.
|
||||
@return void
|
||||
|
||||
Dumps a dictionary onto an opened file pointer. Key pairs are printed out
|
||||
as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
|
||||
output file pointers.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void dictionary_dump(const dictionary* d, FILE* out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -1,958 +0,0 @@
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@file iniparser.c
|
||||
@author N. Devillard
|
||||
@brief Parser for ini files.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
/*---------------------------- Includes ------------------------------------*/
|
||||
#include <ctype.h>
|
||||
#include "pafstd.h"
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include "iniparser.h"
|
||||
|
||||
/*---------------------------- Defines -------------------------------------*/
|
||||
#define ASCIILINESZ (1024)
|
||||
#define INI_INVALID_KEY ((char*)-1)
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Private to this module
|
||||
---------------------------------------------------------------------------*/
|
||||
/**
|
||||
* This enum stores the status for each parsed line (internal use only).
|
||||
*/
|
||||
typedef enum _line_status_ {
|
||||
LINE_UNPROCESSED,
|
||||
LINE_ERROR,
|
||||
LINE_EMPTY,
|
||||
LINE_COMMENT,
|
||||
LINE_SECTION,
|
||||
LINE_VALUE
|
||||
} line_status ;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Convert a string to lowercase.
|
||||
@param in String to convert.
|
||||
@param out Output buffer.
|
||||
@param len Size of the out buffer.
|
||||
@return ptr to the out buffer or NULL if an error occured.
|
||||
|
||||
This function convert a string into lowercase.
|
||||
At most len - 1 elements of the input string will be converted.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
static const char * strlwc(const char * in, char *out, unsigned len)
|
||||
{
|
||||
unsigned i ;
|
||||
|
||||
if (in==NULL || out == NULL || len==0) return NULL ;
|
||||
i=0 ;
|
||||
while (in[i] != '\0' && i < len-1) {
|
||||
out[i] = (char)tolower((int)in[i]);
|
||||
i++ ;
|
||||
}
|
||||
out[i] = '\0';
|
||||
return out ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Duplicate a string
|
||||
@param s String to duplicate
|
||||
@return Pointer to a newly allocated string, to be freed with free()
|
||||
|
||||
This is a replacement for strdup(). This implementation is provided
|
||||
for systems that do not have it.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
static char * xstrdup(const char * s)
|
||||
{
|
||||
char * t ;
|
||||
size_t len ;
|
||||
if (!s)
|
||||
return NULL ;
|
||||
|
||||
len = strlen(s) + 1 ;
|
||||
t = (char*) malloc(len) ;
|
||||
if (t) {
|
||||
memcpy(t, s, len) ;
|
||||
}
|
||||
return t ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Remove blanks at the beginning and the end of a string.
|
||||
@param str String to parse and alter.
|
||||
@return unsigned New size of the string.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
static unsigned strstrip(char * s)
|
||||
{
|
||||
char *last = NULL ;
|
||||
char *dest = s;
|
||||
|
||||
if (s==NULL) return 0;
|
||||
|
||||
last = s + strlen(s);
|
||||
while (isspace((unsigned char)*s) && *s) s++;
|
||||
while (last > s) {
|
||||
if (!isspace((unsigned char)*(last-1)))
|
||||
break ;
|
||||
last -- ;
|
||||
}
|
||||
*last = (char)0;
|
||||
|
||||
memmove(dest,s,last - s + 1);
|
||||
return last - s;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Default error callback for iniparser: wraps `fprintf(stderr, ...)`.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
static int default_error_callback(const char *format, ...)
|
||||
{
|
||||
int ret = 0;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int (*iniparser_error_callback)(const char*, ...) = default_error_callback;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Configure a function to receive the error messages.
|
||||
@param errback Function to call.
|
||||
|
||||
By default, the error will be printed on stderr. If a null pointer is passed
|
||||
as errback the error callback will be switched back to default.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_set_error_callback(int (*errback)(const char *, ...))
|
||||
{
|
||||
if (errback) {
|
||||
iniparser_error_callback = errback;
|
||||
} else {
|
||||
iniparser_error_callback = default_error_callback;
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get number of sections in a dictionary
|
||||
@param d Dictionary to examine
|
||||
@return int Number of sections found in dictionary
|
||||
|
||||
This function returns the number of sections found in a dictionary.
|
||||
The test to recognize sections is done on the string stored in the
|
||||
dictionary: a section name is given as "section" whereas a key is
|
||||
stored as "section:key", thus the test looks for entries that do not
|
||||
contain a colon.
|
||||
|
||||
This clearly fails in the case a section name contains a colon, but
|
||||
this should simply be avoided.
|
||||
|
||||
This function returns -1 in case of error.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_getnsec(const dictionary * d)
|
||||
{
|
||||
size_t i ;
|
||||
int nsec ;
|
||||
|
||||
if (d==NULL) return -1 ;
|
||||
nsec=0 ;
|
||||
for (i=0 ; i<d->size ; i++) {
|
||||
if (d->key[i]==NULL)
|
||||
continue ;
|
||||
if (strchr(d->key[i], ':')==NULL) {
|
||||
nsec ++ ;
|
||||
}
|
||||
}
|
||||
return nsec ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get name for section n in a dictionary.
|
||||
@param d Dictionary to examine
|
||||
@param n Section number (from 0 to nsec-1).
|
||||
@return Pointer to char string
|
||||
|
||||
This function locates the n-th section in a dictionary and returns
|
||||
its name as a pointer to a string statically allocated inside the
|
||||
dictionary. Do not free or modify the returned string!
|
||||
|
||||
This function returns NULL in case of error.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
const char * iniparser_getsecname(const dictionary * d, int n)
|
||||
{
|
||||
size_t i ;
|
||||
int foundsec ;
|
||||
|
||||
if (d==NULL || n<0) return NULL ;
|
||||
foundsec=0 ;
|
||||
for (i=0 ; i<d->size ; i++) {
|
||||
if (d->key[i]==NULL)
|
||||
continue ;
|
||||
if (strchr(d->key[i], ':')==NULL) {
|
||||
foundsec++ ;
|
||||
if (foundsec>n)
|
||||
break ;
|
||||
}
|
||||
}
|
||||
if (foundsec<=n) {
|
||||
return NULL ;
|
||||
}
|
||||
return d->key[i] ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Dump a dictionary to an opened file pointer.
|
||||
@param d Dictionary to dump.
|
||||
@param f Opened file pointer to dump to.
|
||||
@return void
|
||||
|
||||
This function prints out the contents of a dictionary, one element by
|
||||
line, onto the provided file pointer. It is OK to specify @c stderr
|
||||
or @c stdout as output files. This function is meant for debugging
|
||||
purposes mostly.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_dump(const dictionary * d, FILE * f)
|
||||
{
|
||||
size_t i ;
|
||||
|
||||
if (d==NULL || f==NULL) return ;
|
||||
for (i=0 ; i<d->size ; i++) {
|
||||
if (d->key[i]==NULL)
|
||||
continue ;
|
||||
if (d->val[i]!=NULL) {
|
||||
fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
|
||||
} else {
|
||||
fprintf(f, "[%s]=UNDEF\n", d->key[i]);
|
||||
}
|
||||
}
|
||||
return ;
|
||||
}
|
||||
|
||||
static void escape_value(char *escaped, char *value) {
|
||||
char c;
|
||||
int v = 0;
|
||||
int e = 0;
|
||||
|
||||
if(!escaped || !value)
|
||||
return;
|
||||
|
||||
while((c = value[v]) != '\0') {
|
||||
if(c == '\\' || c == '"') {
|
||||
escaped[e] = '\\';
|
||||
e++;
|
||||
}
|
||||
escaped[e] = c;
|
||||
v++;
|
||||
e++;
|
||||
}
|
||||
escaped[e] = '\0';
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Save a dictionary to a loadable ini file
|
||||
@param d Dictionary to dump
|
||||
@param f Opened file pointer to dump to
|
||||
@return void
|
||||
|
||||
This function dumps a given dictionary into a loadable ini file.
|
||||
It is Ok to specify @c stderr or @c stdout as output files.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_dump_ini(const dictionary * d, FILE * f)
|
||||
{
|
||||
size_t i ;
|
||||
size_t nsec ;
|
||||
const char * secname ;
|
||||
char escaped[(ASCIILINESZ * 2) + 2] = "";
|
||||
|
||||
if (d==NULL || f==NULL) return ;
|
||||
|
||||
nsec = iniparser_getnsec(d);
|
||||
if (nsec<1) {
|
||||
/* No section in file: dump all keys as they are */
|
||||
for (i=0 ; i<d->size ; i++) {
|
||||
if (d->key[i]==NULL)
|
||||
continue ;
|
||||
escape_value(escaped, d->val[i]);
|
||||
fprintf(f, "%s = \"%s\"\n", d->key[i], escaped);
|
||||
}
|
||||
return ;
|
||||
}
|
||||
for (i=0 ; i<nsec ; i++) {
|
||||
secname = iniparser_getsecname(d, i) ;
|
||||
iniparser_dumpsection_ini(d, secname, f);
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
return ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Save a dictionary section to a loadable ini file
|
||||
@param d Dictionary to dump
|
||||
@param s Section name of dictionary to dump
|
||||
@param f Opened file pointer to dump to
|
||||
@return void
|
||||
|
||||
This function dumps a given section of a given dictionary into a loadable ini
|
||||
file. It is Ok to specify @c stderr or @c stdout as output files.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_dumpsection_ini(const dictionary * d, const char * s, FILE * f)
|
||||
{
|
||||
size_t j ;
|
||||
char keym[ASCIILINESZ+1];
|
||||
int seclen ;
|
||||
char escaped[(ASCIILINESZ * 2) + 2] = "";
|
||||
|
||||
if (d==NULL || f==NULL) return;
|
||||
if (! iniparser_find_entry(d, s)) return;
|
||||
if (strlen(s) > sizeof(keym)) return;
|
||||
|
||||
seclen = (int)strlen(s);
|
||||
fprintf(f, "\n[%s]\n", s);
|
||||
sprintf(keym, "%s:", s);
|
||||
for (j=0 ; j<d->size ; j++) {
|
||||
if (d->key[j]==NULL)
|
||||
continue ;
|
||||
if (!strncmp(d->key[j], keym, seclen+1)) {
|
||||
escape_value(escaped, d->val[j]);
|
||||
fprintf(f, "%-30s = \"%s\"\n", d->key[j]+seclen+1, escaped);
|
||||
}
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
return ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the number of keys in a section of a dictionary.
|
||||
@param d Dictionary to examine
|
||||
@param s Section name of dictionary to examine
|
||||
@return Number of keys in section
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_getsecnkeys(const dictionary * d, const char * s)
|
||||
{
|
||||
int seclen, nkeys ;
|
||||
char keym[ASCIILINESZ+1];
|
||||
size_t j ;
|
||||
|
||||
nkeys = 0;
|
||||
|
||||
if (d==NULL) return nkeys;
|
||||
if (! iniparser_find_entry(d, s)) return nkeys;
|
||||
|
||||
seclen = (int)strlen(s);
|
||||
strlwc(s, keym, sizeof(keym));
|
||||
keym[seclen] = ':';
|
||||
|
||||
for (j=0 ; j<d->size ; j++) {
|
||||
if (d->key[j]==NULL)
|
||||
continue ;
|
||||
if (!strncmp(d->key[j], keym, seclen+1))
|
||||
nkeys++;
|
||||
}
|
||||
|
||||
return nkeys;
|
||||
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the number of keys in a section of a dictionary.
|
||||
@param d Dictionary to examine
|
||||
@param s Section name of dictionary to examine
|
||||
@param keys Already allocated array to store the keys in
|
||||
@return The pointer passed as `keys` argument or NULL in case of error
|
||||
|
||||
This function queries a dictionary and finds all keys in a given section.
|
||||
The keys argument should be an array of pointers which size has been
|
||||
determined by calling `iniparser_getsecnkeys` function prior to this one.
|
||||
|
||||
Each pointer in the returned char pointer-to-pointer is pointing to
|
||||
a string allocated in the dictionary; do not free or modify them.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys)
|
||||
{
|
||||
size_t i, j, seclen ;
|
||||
char keym[ASCIILINESZ+1];
|
||||
|
||||
if (d==NULL || keys==NULL) return NULL;
|
||||
if (! iniparser_find_entry(d, s)) return NULL;
|
||||
|
||||
seclen = strlen(s);
|
||||
strlwc(s, keym, sizeof(keym));
|
||||
keym[seclen] = ':';
|
||||
|
||||
i = 0;
|
||||
|
||||
for (j=0 ; j<d->size ; j++) {
|
||||
if (d->key[j]==NULL)
|
||||
continue ;
|
||||
if (!strncmp(d->key[j], keym, seclen+1)) {
|
||||
keys[i] = d->key[j];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the string associated to a key
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param def Default value to return if key not found.
|
||||
@return pointer to statically allocated character string
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the pointer passed as 'def' is returned.
|
||||
The returned char pointer is pointing to a string allocated in
|
||||
the dictionary, do not free or modify it.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
const char * iniparser_getstring(const dictionary * d, const char * key, const char * def)
|
||||
{
|
||||
const char * lc_key ;
|
||||
const char * sval ;
|
||||
char tmp_str[ASCIILINESZ+1];
|
||||
|
||||
if (d==NULL || key==NULL)
|
||||
return def ;
|
||||
|
||||
lc_key = strlwc(key, tmp_str, sizeof(tmp_str));
|
||||
sval = dictionary_get(d, lc_key, def);
|
||||
return sval ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to an long int
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return long integer
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
|
||||
Supported values for integers include the usual C notation
|
||||
so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
|
||||
are supported. Examples:
|
||||
|
||||
"42" -> 42
|
||||
"042" -> 34 (octal -> decimal)
|
||||
"0x42" -> 66 (hexa -> decimal)
|
||||
|
||||
Warning: the conversion may overflow in various ways. Conversion is
|
||||
totally outsourced to strtol(), see the associated man page for overflow
|
||||
handling.
|
||||
|
||||
Credits: Thanks to A. Becker for suggesting strtol()
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound)
|
||||
{
|
||||
const char * str ;
|
||||
|
||||
str = iniparser_getstring(d, key, INI_INVALID_KEY);
|
||||
if (str==NULL || str==INI_INVALID_KEY) return notfound ;
|
||||
return strtol(str, NULL, 0);
|
||||
}
|
||||
|
||||
int64_t iniparser_getint64(const dictionary * d, const char * key, int64_t notfound)
|
||||
{
|
||||
const char * str ;
|
||||
|
||||
str = iniparser_getstring(d, key, INI_INVALID_KEY);
|
||||
if (str==NULL || str==INI_INVALID_KEY) return notfound ;
|
||||
return strtoimax(str, NULL, 0);
|
||||
}
|
||||
|
||||
uint64_t iniparser_getuint64(const dictionary * d, const char * key, uint64_t notfound)
|
||||
{
|
||||
const char * str ;
|
||||
|
||||
str = iniparser_getstring(d, key, INI_INVALID_KEY);
|
||||
if (str==NULL || str==INI_INVALID_KEY) return notfound ;
|
||||
return strtoumax(str, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to an int
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return integer
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
|
||||
Supported values for integers include the usual C notation
|
||||
so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
|
||||
are supported. Examples:
|
||||
|
||||
"42" -> 42
|
||||
"042" -> 34 (octal -> decimal)
|
||||
"0x42" -> 66 (hexa -> decimal)
|
||||
|
||||
Warning: the conversion may overflow in various ways. Conversion is
|
||||
totally outsourced to strtol(), see the associated man page for overflow
|
||||
handling.
|
||||
|
||||
Credits: Thanks to A. Becker for suggesting strtol()
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_getint(const dictionary * d, const char * key, int notfound)
|
||||
{
|
||||
return (int)iniparser_getlongint(d, key, notfound);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to a double
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return double
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
double iniparser_getdouble(const dictionary * d, const char * key, double notfound)
|
||||
{
|
||||
const char * str ;
|
||||
|
||||
str = iniparser_getstring(d, key, INI_INVALID_KEY);
|
||||
if (str==NULL || str==INI_INVALID_KEY) return notfound ;
|
||||
return atof(str);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to a boolean
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return integer
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
|
||||
A true boolean is found if one of the following is matched:
|
||||
|
||||
- A string starting with 'y'
|
||||
- A string starting with 'Y'
|
||||
- A string starting with 't'
|
||||
- A string starting with 'T'
|
||||
- A string starting with '1'
|
||||
|
||||
A false boolean is found if one of the following is matched:
|
||||
|
||||
- A string starting with 'n'
|
||||
- A string starting with 'N'
|
||||
- A string starting with 'f'
|
||||
- A string starting with 'F'
|
||||
- A string starting with '0'
|
||||
|
||||
The notfound value returned if no boolean is identified, does not
|
||||
necessarily have to be 0 or 1.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_getboolean(const dictionary * d, const char * key, int notfound)
|
||||
{
|
||||
int ret ;
|
||||
const char * c ;
|
||||
|
||||
c = iniparser_getstring(d, key, INI_INVALID_KEY);
|
||||
if (c==NULL || c==INI_INVALID_KEY) return notfound ;
|
||||
if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
|
||||
ret = 1 ;
|
||||
} else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
|
||||
ret = 0 ;
|
||||
} else {
|
||||
ret = notfound ;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Finds out if a given entry exists in a dictionary
|
||||
@param ini Dictionary to search
|
||||
@param entry Name of the entry to look for
|
||||
@return integer 1 if entry exists, 0 otherwise
|
||||
|
||||
Finds out if a given entry exists in the dictionary. Since sections
|
||||
are stored as keys with NULL associated values, this is the only way
|
||||
of querying for the presence of sections in a dictionary.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_find_entry(const dictionary * ini, const char * entry)
|
||||
{
|
||||
int found=0 ;
|
||||
if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
|
||||
found = 1 ;
|
||||
}
|
||||
return found ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Set an entry in a dictionary.
|
||||
@param ini Dictionary to modify.
|
||||
@param entry Entry to modify (entry name)
|
||||
@param val New value to associate to the entry.
|
||||
@return int 0 if Ok, -1 otherwise.
|
||||
|
||||
If the given entry can be found in the dictionary, it is modified to
|
||||
contain the provided value. If it cannot be found, the entry is created.
|
||||
It is Ok to set val to NULL.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_set(dictionary * ini, const char * entry, const char * val)
|
||||
{
|
||||
char tmp_key[ASCIILINESZ+1] = {0};
|
||||
char tmp_val[ASCIILINESZ+1] = {0};
|
||||
size_t len;
|
||||
|
||||
if(val) {
|
||||
len = strlen(val);
|
||||
len = len > ASCIILINESZ ? ASCIILINESZ : len;
|
||||
memcpy(tmp_val, val, len) ;
|
||||
val = tmp_val;
|
||||
}
|
||||
return dictionary_set(ini, strlwc(entry, tmp_key, sizeof(tmp_key)), val);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Delete an entry in a dictionary
|
||||
@param ini Dictionary to modify
|
||||
@param entry Entry to delete (entry name)
|
||||
@return void
|
||||
|
||||
If the given entry can be found, it is deleted from the dictionary.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_unset(dictionary * ini, const char * entry)
|
||||
{
|
||||
char tmp_str[ASCIILINESZ+1];
|
||||
dictionary_unset(ini, strlwc(entry, tmp_str, sizeof(tmp_str)));
|
||||
}
|
||||
|
||||
static void parse_quoted_value(char *value, char quote) {
|
||||
char c;
|
||||
char *quoted;
|
||||
int q = 0, v = 0;
|
||||
int esc = 0;
|
||||
|
||||
if(!value)
|
||||
return;
|
||||
|
||||
quoted = xstrdup(value);
|
||||
|
||||
if(!quoted) {
|
||||
iniparser_error_callback("iniparser: memory allocation failure\n");
|
||||
goto end_of_value;
|
||||
}
|
||||
|
||||
while((c = quoted[q]) != '\0') {
|
||||
if(!esc) {
|
||||
if(c == '\\') {
|
||||
esc = 1;
|
||||
q++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(c == quote) {
|
||||
goto end_of_value;
|
||||
}
|
||||
}
|
||||
esc = 0;
|
||||
value[v] = c;
|
||||
v++;
|
||||
q++;
|
||||
}
|
||||
end_of_value:
|
||||
value[v] = '\0';
|
||||
free(quoted);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Load a single line from an INI file
|
||||
@param input_line Input line, may be concatenated multi-line input
|
||||
@param section Output space to store section
|
||||
@param key Output space to store key
|
||||
@param value Output space to store value
|
||||
@return line_status value
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
static line_status iniparser_line(
|
||||
const char * input_line,
|
||||
char * section,
|
||||
char * key,
|
||||
char * value)
|
||||
{
|
||||
line_status sta ;
|
||||
char * line = NULL;
|
||||
size_t len ;
|
||||
int d_quote;
|
||||
|
||||
line = xstrdup(input_line);
|
||||
len = strstrip(line);
|
||||
|
||||
sta = LINE_UNPROCESSED ;
|
||||
if (len<1) {
|
||||
/* Empty line */
|
||||
sta = LINE_EMPTY ;
|
||||
} else if (line[0]=='#' || line[0]==';') {
|
||||
/* Comment line */
|
||||
sta = LINE_COMMENT ;
|
||||
} else if (line[0]=='[' && line[len-1]==']') {
|
||||
/* Section name without opening square bracket */
|
||||
sscanf(line, "[%[^\n]", section);
|
||||
len = strlen(section);
|
||||
/* Section name without closing square bracket */
|
||||
if(section[len-1] == ']')
|
||||
{
|
||||
section[len-1] = '\0';
|
||||
}
|
||||
strstrip(section);
|
||||
strlwc(section, section, len);
|
||||
sta = LINE_SECTION ;
|
||||
} else if ((d_quote = sscanf (line, "%[^=] = \"%[^\n]\"", key, value)) == 2
|
||||
|| sscanf (line, "%[^=] = '%[^\n]'", key, value) == 2) {
|
||||
/* Usual key=value with quotes, with or without comments */
|
||||
strstrip(key);
|
||||
strlwc(key, key, len);
|
||||
if(d_quote == 2)
|
||||
parse_quoted_value(value, '"');
|
||||
else
|
||||
parse_quoted_value(value, '\'');
|
||||
/* Don't strip spaces from values surrounded with quotes */
|
||||
sta = LINE_VALUE ;
|
||||
} else if (sscanf (line, "%[^=] = %[^;#]", key, value) == 2) {
|
||||
/* Usual key=value without quotes, with or without comments */
|
||||
strstrip(key);
|
||||
strlwc(key, key, len);
|
||||
strstrip(value);
|
||||
/*
|
||||
* sscanf cannot handle '' or "" as empty values
|
||||
* this is done here
|
||||
*/
|
||||
if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
|
||||
value[0]=0 ;
|
||||
}
|
||||
sta = LINE_VALUE ;
|
||||
} else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
|
||||
|| sscanf(line, "%[^=] %[=]", key, value) == 2) {
|
||||
/*
|
||||
* Special cases:
|
||||
* key=
|
||||
* key=;
|
||||
* key=#
|
||||
*/
|
||||
strstrip(key);
|
||||
strlwc(key, key, len);
|
||||
value[0]=0 ;
|
||||
sta = LINE_VALUE ;
|
||||
} else {
|
||||
/* Generate syntax error */
|
||||
sta = LINE_ERROR ;
|
||||
}
|
||||
|
||||
free(line);
|
||||
return sta ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Parse an ini file and return an allocated dictionary object
|
||||
@param in File to read.
|
||||
@param ininame Name of the ini file to read (only used for nicer error messages)
|
||||
@return Pointer to newly allocated dictionary
|
||||
|
||||
This is the parser for ini files. This function is called, providing
|
||||
the file to be read. It returns a dictionary object that should not
|
||||
be accessed directly, but through accessor functions instead.
|
||||
|
||||
The returned dictionary must be freed using iniparser_freedict().
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
dictionary * iniparser_load_file(FILE * in, const char * ininame)
|
||||
{
|
||||
char line [ASCIILINESZ+1] ;
|
||||
char section [ASCIILINESZ+1] ;
|
||||
char key [ASCIILINESZ+1] ;
|
||||
char tmp [(ASCIILINESZ * 2) + 2] ;
|
||||
char val [ASCIILINESZ+1] ;
|
||||
|
||||
int last=0 ;
|
||||
int len ;
|
||||
int lineno=0 ;
|
||||
int errs=0;
|
||||
int mem_err=0;
|
||||
|
||||
dictionary * dict ;
|
||||
|
||||
dict = dictionary_new(0) ;
|
||||
if (!dict) {
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
memset(line, 0, ASCIILINESZ);
|
||||
memset(section, 0, ASCIILINESZ);
|
||||
memset(key, 0, ASCIILINESZ);
|
||||
memset(val, 0, ASCIILINESZ);
|
||||
last=0 ;
|
||||
|
||||
while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
|
||||
lineno++ ;
|
||||
len = (int)strlen(line)-1;
|
||||
if (len<=0)
|
||||
continue;
|
||||
/* Safety check against buffer overflows */
|
||||
if (line[len]!='\n' && !feof(in)) {
|
||||
iniparser_error_callback(
|
||||
"iniparser: input line too long in %s (%d)\n",
|
||||
ininame,
|
||||
lineno);
|
||||
dictionary_del(dict);
|
||||
return NULL ;
|
||||
}
|
||||
/* Get rid of \n and spaces at end of line */
|
||||
while ((len>=0) &&
|
||||
((line[len]=='\n') || (isspace((unsigned char)line[len])))) {
|
||||
line[len]=0 ;
|
||||
len-- ;
|
||||
}
|
||||
if (len < 0) { /* Line was entirely \n and/or spaces */
|
||||
len = 0;
|
||||
}
|
||||
/* Detect multi-line */
|
||||
if (line[len]=='\\') {
|
||||
/* Multi-line value */
|
||||
last=len ;
|
||||
continue ;
|
||||
} else {
|
||||
last=0 ;
|
||||
}
|
||||
switch (iniparser_line(line, section, key, val)) {
|
||||
case LINE_EMPTY:
|
||||
case LINE_COMMENT:
|
||||
break ;
|
||||
|
||||
case LINE_SECTION:
|
||||
mem_err = dictionary_set(dict, section, NULL);
|
||||
break ;
|
||||
|
||||
case LINE_VALUE:
|
||||
sprintf(tmp, "%s:%s", section, key);
|
||||
mem_err = dictionary_set(dict, tmp, val);
|
||||
break ;
|
||||
|
||||
case LINE_ERROR:
|
||||
iniparser_error_callback(
|
||||
"iniparser: syntax error in %s (%d):\n-> %s\n",
|
||||
ininame,
|
||||
lineno,
|
||||
line);
|
||||
errs++ ;
|
||||
break;
|
||||
|
||||
default:
|
||||
break ;
|
||||
}
|
||||
memset(line, 0, ASCIILINESZ);
|
||||
last=0;
|
||||
if (mem_err<0) {
|
||||
iniparser_error_callback("iniparser: memory allocation failure\n");
|
||||
break ;
|
||||
}
|
||||
}
|
||||
if (errs) {
|
||||
dictionary_del(dict);
|
||||
dict = NULL ;
|
||||
}
|
||||
return dict ;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Parse an ini file and return an allocated dictionary object
|
||||
@param ininame Name of the ini file to read.
|
||||
@return Pointer to newly allocated dictionary
|
||||
|
||||
This is the parser for ini files. This function is called, providing
|
||||
the name of the file to be read. It returns a dictionary object that
|
||||
should not be accessed directly, but through accessor functions
|
||||
instead.
|
||||
|
||||
The returned dictionary must be freed using iniparser_freedict().
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
dictionary * iniparser_load(const char * ininame)
|
||||
{
|
||||
FILE * in ;
|
||||
dictionary * dict ;
|
||||
|
||||
if ((in=fopen(ininame, "r"))==NULL) {
|
||||
iniparser_error_callback("iniparser: cannot open %s\n", ininame);
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
dict = iniparser_load_file(in, ininame);
|
||||
fclose(in);
|
||||
|
||||
return dict ;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Free all memory associated to an ini dictionary
|
||||
@param d Dictionary to free
|
||||
@return void
|
||||
|
||||
Free all memory associated to an ini dictionary.
|
||||
It is mandatory to call this function before the dictionary object
|
||||
gets out of the current context.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_freedict(dictionary * d)
|
||||
{
|
||||
dictionary_del(d);
|
||||
}
|
||||
@ -1,443 +0,0 @@
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@file iniparser.h
|
||||
@author N. Devillard
|
||||
@brief Parser for ini files.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _INIPARSER_H_
|
||||
#define _INIPARSER_H_
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Includes
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
#include "dictionary.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Configure a function to receive the error messages.
|
||||
@param errback Function to call.
|
||||
|
||||
By default, the error will be printed on stderr. If a null pointer is passed
|
||||
as errback the error callback will be switched back to default.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
void iniparser_set_error_callback(int (*errback)(const char*, ...));
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get number of sections in a dictionary
|
||||
@param d Dictionary to examine
|
||||
@return int Number of sections found in dictionary
|
||||
|
||||
This function returns the number of sections found in a dictionary.
|
||||
The test to recognize sections is done on the string stored in the
|
||||
dictionary: a section name is given as "section" whereas a key is
|
||||
stored as "section:key", thus the test looks for entries that do not
|
||||
contain a colon.
|
||||
|
||||
This clearly fails in the case a section name contains a colon, but
|
||||
this should simply be avoided.
|
||||
|
||||
This function returns -1 in case of error.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
int iniparser_getnsec(const dictionary* d);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get name for section n in a dictionary.
|
||||
@param d Dictionary to examine
|
||||
@param n Section number (from 0 to nsec-1).
|
||||
@return Pointer to char string
|
||||
|
||||
This function locates the n-th section in a dictionary and returns
|
||||
its name as a pointer to a string statically allocated inside the
|
||||
dictionary. Do not free or modify the returned string!
|
||||
|
||||
This function returns NULL in case of error.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
const char* iniparser_getsecname(const dictionary* d, int n);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Save a dictionary to a loadable ini file
|
||||
@param d Dictionary to dump
|
||||
@param f Opened file pointer to dump to
|
||||
|
||||
This function dumps a given dictionary into a loadable ini file.
|
||||
It is Ok to specify @c stderr or @c stdout as output files.
|
||||
|
||||
All values are quoted, these charecters are escaped:
|
||||
|
||||
- ' : the quote character (e.g. "String with \"Quotes\"")
|
||||
- \ : the backslash character (e.g. "C:\\tmp")
|
||||
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
void iniparser_dump_ini(const dictionary* d, FILE* f);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Save a dictionary section to a loadable ini file
|
||||
@param d Dictionary to dump
|
||||
@param s Section name of dictionary to dump
|
||||
@param f Opened file pointer to dump to
|
||||
|
||||
This function dumps a given section of a given dictionary into a loadable ini
|
||||
file. It is Ok to specify @c stderr or @c stdout as output files.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
void iniparser_dumpsection_ini(const dictionary* d, const char* s, FILE* f);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Dump a dictionary to an opened file pointer.
|
||||
@param d Dictionary to dump.
|
||||
@param f Opened file pointer to dump to.
|
||||
|
||||
This function prints out the contents of a dictionary, one element by
|
||||
line, onto the provided file pointer. It is OK to specify @c stderr
|
||||
or @c stdout as output files. This function is meant for debugging
|
||||
purposes mostly.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_dump(const dictionary* d, FILE* f);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the number of keys in a section of a dictionary.
|
||||
@param d Dictionary to examine
|
||||
@param s Section name of dictionary to examine
|
||||
@return Number of keys in section
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_getsecnkeys(const dictionary* d, const char* s);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the number of keys in a section of a dictionary.
|
||||
@param d Dictionary to examine
|
||||
@param s Section name of dictionary to examine
|
||||
@param keys Already allocated array to store the keys in
|
||||
@return The pointer passed as `keys` argument or NULL in case of error
|
||||
|
||||
This function queries a dictionary and finds all keys in a given section.
|
||||
The keys argument should be an array of pointers which size has been
|
||||
determined by calling `iniparser_getsecnkeys` function prior to this one.
|
||||
|
||||
Each pointer in the returned char pointer-to-pointer is pointing to
|
||||
a string allocated in the dictionary; do not free or modify them.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
const char** iniparser_getseckeys(const dictionary* d, const char* s, const char** keys);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the string associated to a key
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param def Default value to return if key not found.
|
||||
@return pointer to statically allocated character string
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the pointer passed as 'def' is returned.
|
||||
The returned char pointer is pointing to a string allocated in
|
||||
the dictionary, do not free or modify it.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
const char* iniparser_getstring(const dictionary* d, const char* key, const char* def);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to an int
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return integer
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
|
||||
Supported values for integers include the usual C notation
|
||||
so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
|
||||
are supported. Examples:
|
||||
|
||||
- "42" -> 42
|
||||
- "042" -> 34 (octal -> decimal)
|
||||
- "0x42" -> 66 (hexa -> decimal)
|
||||
|
||||
Warning: the conversion may overflow in various ways. Conversion is
|
||||
totally outsourced to strtol(), see the associated man page for overflow
|
||||
handling.
|
||||
|
||||
Credits: Thanks to A. Becker for suggesting strtol()
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_getint(const dictionary* d, const char* key, int notfound);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to an long int
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return integer
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
|
||||
Supported values for integers include the usual C notation
|
||||
so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
|
||||
are supported. Examples:
|
||||
|
||||
- "42" -> 42
|
||||
- "042" -> 34 (octal -> decimal)
|
||||
- "0x42" -> 66 (hexa -> decimal)
|
||||
|
||||
Warning: the conversion may overflow in various ways. Conversion is
|
||||
totally outsourced to strtol(), see the associated man page for overflow
|
||||
handling.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
long int iniparser_getlongint(const dictionary* d, const char* key, long int notfound);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to an int64_t
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return integer
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
|
||||
Supported values for integers include the usual C notation
|
||||
so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
|
||||
are supported. Examples:
|
||||
|
||||
- "42" -> 42
|
||||
- "042" -> 34 (octal -> decimal)
|
||||
- "0x42" -> 66 (hexa -> decimal)
|
||||
|
||||
Warning: the conversion may overflow in various ways. Conversion is
|
||||
totally outsourced to strtoimax(), see the associated man page for overflow
|
||||
handling.
|
||||
|
||||
This function is usefull on 32bit architectures where `long int` is only
|
||||
32bit.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int64_t iniparser_getint64(const dictionary* d, const char* key, int64_t notfound);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to an uint64_t
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return integer
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
|
||||
Supported values for integers include the usual C notation
|
||||
so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
|
||||
are supported. Examples:
|
||||
|
||||
- "42" -> 42
|
||||
- "042" -> 34 (octal -> decimal)
|
||||
- "0x42" -> 66 (hexa -> decimal)
|
||||
|
||||
Warning: the conversion may overflow in various ways. Conversion is
|
||||
totally outsourced to strtoumax(), see the associated man page for overflow
|
||||
handling.
|
||||
|
||||
This function is usefull on 32bit architectures where `long int` is only
|
||||
32bit.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
uint64_t iniparser_getuint64(const dictionary* d, const char* key, uint64_t notfound);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to a double
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return double
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
double iniparser_getdouble(const dictionary* d, const char* key, double notfound);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to a boolean
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return integer
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
|
||||
A true boolean is found if one of the following is matched:
|
||||
|
||||
- A string starting with 'y'
|
||||
- A string starting with 'Y'
|
||||
- A string starting with 't'
|
||||
- A string starting with 'T'
|
||||
- A string starting with '1'
|
||||
|
||||
A false boolean is found if one of the following is matched:
|
||||
|
||||
- A string starting with 'n'
|
||||
- A string starting with 'N'
|
||||
- A string starting with 'f'
|
||||
- A string starting with 'F'
|
||||
- A string starting with '0'
|
||||
|
||||
The notfound value returned if no boolean is identified, does not
|
||||
necessarily have to be 0 or 1.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_getboolean(const dictionary* d, const char* key, int notfound);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Set an entry in a dictionary.
|
||||
@param ini Dictionary to modify.
|
||||
@param entry Entry to modify (entry name)
|
||||
@param val New value to associate to the entry.
|
||||
@return int 0 if Ok, -1 otherwise.
|
||||
|
||||
If the given entry can be found in the dictionary, it is modified to
|
||||
contain the provided value. If it cannot be found, the entry is created.
|
||||
It is Ok to set val to NULL.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_set(dictionary* ini, const char* entry, const char* val);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Delete an entry in a dictionary
|
||||
@param ini Dictionary to modify
|
||||
@param entry Entry to delete (entry name)
|
||||
|
||||
If the given entry can be found, it is deleted from the dictionary.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_unset(dictionary* ini, const char* entry);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Finds out if a given entry exists in a dictionary
|
||||
@param ini Dictionary to search
|
||||
@param entry Name of the entry to look for
|
||||
@return integer 1 if entry exists, 0 otherwise
|
||||
|
||||
Finds out if a given entry exists in the dictionary. Since sections
|
||||
are stored as keys with NULL associated values, this is the only way
|
||||
of querying for the presence of sections in a dictionary.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_find_entry(const dictionary* ini, const char* entry);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Parse an ini file and return an allocated dictionary object
|
||||
@param ininame Name of the ini file to read.
|
||||
@return Pointer to newly allocated dictionary
|
||||
|
||||
This is the parser for ini files. This function is called, providing
|
||||
the name of the file to be read. It returns a dictionary object that
|
||||
should not be accessed directly, but through accessor functions
|
||||
instead.
|
||||
|
||||
Iff the value is a quoted string it supports some escape sequences:
|
||||
|
||||
- \" or ' : the quote character
|
||||
(e.g. 'String with "Quotes"' or "String with 'Quotes'")
|
||||
- \ : the backslash character (e.g. "C:\tmp")
|
||||
|
||||
Escape sequences always start with a backslash. Additional escape sequences
|
||||
might be added in the future. Backslash characters must be escaped. Any other
|
||||
sequence then those outlined above is invalid and may lead to unpredictable
|
||||
results.
|
||||
|
||||
The returned dictionary must be freed using iniparser_freedict().
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
dictionary* iniparser_load(const char* ininame);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Parse an ini file and return an allocated dictionary object
|
||||
@param in File to read.
|
||||
@param ininame Name of the ini file to read (only used for nicer error messages)
|
||||
@return Pointer to newly allocated dictionary
|
||||
|
||||
This is the parser for ini files. This function is called, providing
|
||||
the file to be read. It returns a dictionary object that should not
|
||||
be accessed directly, but through accessor functions instead.
|
||||
|
||||
Iff the value is a quoted string it supports some escape sequences:
|
||||
|
||||
- \" or ' : the quote character
|
||||
(e.g. 'String with "Quotes"' or "String with 'Quotes'")
|
||||
- \ : the backslash character (e.g. "C:\tmp")
|
||||
|
||||
Escape sequences always start with a backslash. Additional escape sequences
|
||||
might be added in the future. Backslash characters must be escaped. Any other
|
||||
sequence then those outlined above is invalid and may lead to unpredictable
|
||||
results.
|
||||
|
||||
The returned dictionary must be freed using iniparser_freedict().
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
dictionary* iniparser_load_file(FILE* in, const char* ininame);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
@brief Free all memory associated to an ini dictionary
|
||||
@param d Dictionary to free
|
||||
|
||||
Free all memory associated to an ini dictionary.
|
||||
It is mandatory to call this function before the dictionary object
|
||||
gets out of the current context.
|
||||
*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_freedict(dictionary* d);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -1,47 +0,0 @@
|
||||
#ifndef __PAFSTD__
|
||||
#define __PAFSTD__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <paf/std/stdc.h>
|
||||
|
||||
#define malloc sce_paf_malloc
|
||||
#define memcpy sce_paf_memcpy
|
||||
#define memset sce_paf_memset
|
||||
#define memmove sce_paf_memmove
|
||||
#define free sce_paf_free
|
||||
#define calloc sce_paf_calloc
|
||||
#define strcmp sce_paf_strcmp
|
||||
#define strlen sce_paf_strlen
|
||||
#define strncmp sce_paf_strncmp
|
||||
#define strchr sce_paf_strchr
|
||||
#define strtol sce_paf_strtol
|
||||
#define atof sce_paf_atof
|
||||
|
||||
// _ctype_ isnt called that in scelibc, so just stub the functions that are needed
|
||||
|
||||
inline int _is_space(char c)
|
||||
{
|
||||
return (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v');
|
||||
}
|
||||
|
||||
inline int _to_lower(int c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
return c + ('a' - 'A');
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
inline int _is_digit(int c)
|
||||
{
|
||||
return (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
#undef isspace
|
||||
#undef tolower
|
||||
#undef isdigit
|
||||
#define isspace _is_space
|
||||
#define tolower _to_lower
|
||||
#define isdigit _is_digit
|
||||
|
||||
#endif
|
||||
@ -176,16 +176,19 @@ struct Config {
|
||||
case 's': {
|
||||
const char* value = iniparser_getstring(this->dict, m.key_ini, "");
|
||||
this->settings->SetString(m.key_app, value);
|
||||
sceClibPrintf("ini->settings %s = %s\n", m.key_app, value);
|
||||
break;
|
||||
}
|
||||
case 'i': {
|
||||
int32_t value = iniparser_getint(this->dict, m.key_ini, 0);
|
||||
this->settings->SetInt(m.key_app, value);
|
||||
sceClibPrintf("ini->settings %s = %d\n", m.key_app, value);
|
||||
break;
|
||||
}
|
||||
case 'b': {
|
||||
bool value = iniparser_getboolean(this->dict, m.key_ini, 0) == 1;
|
||||
this->settings->SetBool(m.key_app, value);
|
||||
sceClibPrintf("ini->settings %s = %s\n", m.key_app, value ? "true" : "false");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@ -206,18 +209,21 @@ struct Config {
|
||||
const char* def = iniparser_getstring(this->dict, m.key_ini, "");
|
||||
this->settings->GetString(m.key_app, this->buffer, sizeof(this->buffer), def);
|
||||
iniparser_set(this->dict, m.key_ini, buffer);
|
||||
sceClibPrintf("settings->ini %s = %s\n", m.key_ini, buffer);
|
||||
break;
|
||||
}
|
||||
case 'i': {
|
||||
int32_t value = iniparser_getint(this->dict, m.key_ini, 0);
|
||||
this->settings->GetInt(m.key_app, &value, value);
|
||||
SetDictInt(m.key_ini, value);
|
||||
sceClibPrintf("settings->ini %s = %d\n", m.key_ini, value);
|
||||
break;
|
||||
}
|
||||
case 'b': {
|
||||
bool value = iniparser_getboolean(this->dict, m.key_ini, 0) == 1;
|
||||
this->settings->GetBool(m.key_app, &value, value);
|
||||
SetDictBool(m.key_ini, value);
|
||||
sceClibPrintf("settings->ini %s = %s\n", m.key_ini, value ? "true" : "false");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@ -384,7 +390,7 @@ void open_settings()
|
||||
g_appSetIf->ShowFooter();
|
||||
}
|
||||
|
||||
int paf_main(void)
|
||||
int main()
|
||||
{
|
||||
paf::Framework::InitParam fwParam;
|
||||
fwParam.mode = paf::Framework::Mode_Normal;
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
#include <paf.h>
|
||||
#include <psp2/kernel/clib.h>
|
||||
#include <psp2/kernel/modulemgr.h>
|
||||
#include <psp2/kernel/processmgr.h>
|
||||
#include <psp2/sysmodule.h>
|
||||
|
||||
char sceUserMainThreadName[] = "isle_config";
|
||||
int sceUserMainThreadPriority = 0x10000100;
|
||||
int sceUserMainThreadCpuAffinityMask = 0x70000;
|
||||
SceSize sceUserMainThreadStackSize = 0x4000;
|
||||
|
||||
void operator delete(void* ptr, unsigned int n)
|
||||
{
|
||||
return sce_paf_free(ptr);
|
||||
}
|
||||
|
||||
int paf_main(void);
|
||||
|
||||
extern "C" int module_start(SceSize args, void* argp)
|
||||
{
|
||||
int load_res;
|
||||
ScePafInit init_param;
|
||||
SceSysmoduleOpt sysmodule_opt;
|
||||
|
||||
init_param.global_heap_size = 0x1000000;
|
||||
init_param.cdlg_mode = 0;
|
||||
init_param.global_heap_alignment = 0;
|
||||
init_param.global_heap_disable_assert_on_alloc_failure = 0;
|
||||
|
||||
load_res = 0xDEADBEEF;
|
||||
sysmodule_opt.flags = 0;
|
||||
sysmodule_opt.result = &load_res;
|
||||
|
||||
int res = sceSysmoduleLoadModuleInternalWithArg(
|
||||
SCE_SYSMODULE_INTERNAL_PAF,
|
||||
sizeof(init_param),
|
||||
&init_param,
|
||||
&sysmodule_opt
|
||||
);
|
||||
if ((res | load_res) != 0) {
|
||||
sceClibPrintf(
|
||||
"[PAF PRX Loader] Failed to load the PAF prx. (return value 0x%x, result code 0x%x )\n",
|
||||
res,
|
||||
load_res
|
||||
);
|
||||
}
|
||||
|
||||
res = paf_main();
|
||||
sceKernelExitProcess(res);
|
||||
return SCE_KERNEL_START_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C" void _start()
|
||||
{
|
||||
module_start(0, nullptr);
|
||||
}
|
||||
169
CONFIG/vita/src/paf_runtime.cpp
Normal file
169
CONFIG/vita/src/paf_runtime.cpp
Normal file
@ -0,0 +1,169 @@
|
||||
#include <paf.h>
|
||||
#include <psp2/kernel/clib.h>
|
||||
#include <psp2/kernel/modulemgr.h>
|
||||
#include <psp2/kernel/processmgr.h>
|
||||
#include <psp2/sysmodule.h>
|
||||
|
||||
char sceUserMainThreadName[] = "isle_config";
|
||||
int sceUserMainThreadPriority = 0x10000100;
|
||||
int sceUserMainThreadCpuAffinityMask = 0x70000;
|
||||
SceSize sceUserMainThreadStackSize = 0x4000;
|
||||
|
||||
void operator delete(void* ptr, unsigned int n)
|
||||
{
|
||||
return sce_paf_free(ptr);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void user_malloc_init()
|
||||
{
|
||||
}
|
||||
|
||||
void user_malloc_finalize(void)
|
||||
{
|
||||
}
|
||||
|
||||
void* user_malloc(size_t size)
|
||||
{
|
||||
return sce_paf_malloc(size);
|
||||
}
|
||||
|
||||
void user_free(void* ptr)
|
||||
{
|
||||
sce_paf_free(ptr);
|
||||
}
|
||||
|
||||
void* user_calloc(size_t nelem, size_t size)
|
||||
{
|
||||
return sce_paf_calloc(nelem, size);
|
||||
}
|
||||
|
||||
void* user_realloc(void* ptr, size_t new_size)
|
||||
{
|
||||
return sce_paf_realloc(ptr, new_size);
|
||||
}
|
||||
|
||||
void* user_memalign(size_t boundary, size_t size)
|
||||
{
|
||||
return sce_paf_memalign(boundary, size);
|
||||
}
|
||||
|
||||
void* user_reallocalign(void* ptr, size_t size, size_t boundary)
|
||||
{
|
||||
sceClibPrintf("[PAF2LIBC] reallocalign is not supported\n");
|
||||
abort();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int user_malloc_stats(struct malloc_managed_size* mmsize)
|
||||
{
|
||||
sceClibPrintf("malloc_stats\n");
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int user_malloc_stats_fast(struct malloc_managed_size* mmsize)
|
||||
{
|
||||
sceClibPrintf("user_malloc_stats_fast\n");
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t user_malloc_usable_size(void* ptr)
|
||||
{
|
||||
return sce_paf_musable_size(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void* user_new(std::size_t size)
|
||||
{
|
||||
return sce_paf_malloc(size);
|
||||
}
|
||||
|
||||
void* user_new(std::size_t size, const std::nothrow_t& x)
|
||||
{
|
||||
return sce_paf_malloc(size);
|
||||
}
|
||||
|
||||
void* user_new_array(std::size_t size)
|
||||
{
|
||||
return sce_paf_malloc(size);
|
||||
}
|
||||
|
||||
void* user_new_array(std::size_t size, const std::nothrow_t& x)
|
||||
{
|
||||
return sce_paf_malloc(size);
|
||||
}
|
||||
|
||||
void user_delete(void* ptr)
|
||||
{
|
||||
sce_paf_free(ptr);
|
||||
}
|
||||
|
||||
void user_delete(void* ptr, const std::nothrow_t& x)
|
||||
{
|
||||
sce_paf_free(ptr);
|
||||
}
|
||||
|
||||
void user_delete_array(void* ptr)
|
||||
{
|
||||
sce_paf_free(ptr);
|
||||
}
|
||||
|
||||
void user_delete_array(void* ptr, const std::nothrow_t& x)
|
||||
{
|
||||
sce_paf_free(ptr);
|
||||
}
|
||||
|
||||
int paf_init()
|
||||
{
|
||||
int load_res;
|
||||
ScePafInit init_param;
|
||||
SceSysmoduleOpt sysmodule_opt;
|
||||
|
||||
init_param.global_heap_size = 0x1000000;
|
||||
init_param.cdlg_mode = 0;
|
||||
init_param.global_heap_alignment = 0;
|
||||
init_param.global_heap_disable_assert_on_alloc_failure = 0;
|
||||
|
||||
load_res = 0xDEADBEEF;
|
||||
sysmodule_opt.flags = 0;
|
||||
sysmodule_opt.result = &load_res;
|
||||
|
||||
int res = sceSysmoduleLoadModuleInternalWithArg(
|
||||
SCE_SYSMODULE_INTERNAL_PAF,
|
||||
sizeof(init_param),
|
||||
&init_param,
|
||||
&sysmodule_opt
|
||||
);
|
||||
if ((res | load_res) != 0) {
|
||||
sceClibPrintf(
|
||||
"[PAF PRX Loader] Failed to load the PAF prx. (return value 0x%x, result code 0x%x )\n",
|
||||
res,
|
||||
load_res
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main();
|
||||
|
||||
extern "C" int module_start(SceSize args, void* argp)
|
||||
{
|
||||
int res = paf_init();
|
||||
if (res < 0) {
|
||||
sceKernelExitProcess(res);
|
||||
return SCE_KERNEL_START_FAILED;
|
||||
}
|
||||
|
||||
res = main();
|
||||
sceKernelExitProcess(res);
|
||||
return SCE_KERNEL_START_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C" void _start()
|
||||
{
|
||||
module_start(0, nullptr);
|
||||
}
|
||||
@ -70,9 +70,12 @@ if(NOT (VITA OR WINDOWS_STORE))
|
||||
endif()
|
||||
|
||||
if(VITA)
|
||||
add_subdirectory(src/d3drm/backends/gxm/shaders)
|
||||
|
||||
target_sources(miniwin PRIVATE
|
||||
src/d3drm/backends/gxm/renderer.cpp
|
||||
src/d3drm/backends/gxm/memory.cpp
|
||||
src/d3drm/backends/gxm/gxm_context.cpp
|
||||
src/d3drm/backends/gxm/gxm_renderer.cpp
|
||||
src/d3drm/backends/gxm/gxm_memory.cpp
|
||||
src/d3drm/backends/gxm/tlsf.c
|
||||
)
|
||||
target_link_libraries(miniwin PRIVATE
|
||||
@ -80,11 +83,10 @@ if(VITA)
|
||||
SceRazorCapture_stub
|
||||
SceRazorHud_stub
|
||||
taihen_stub
|
||||
gxm_shaders
|
||||
)
|
||||
list(APPEND GRAPHICS_BACKENDS USE_GXM)
|
||||
list(REMOVE_ITEM GRAPHICS_BACKENDS USE_SOFTWARE_RENDER USE_SDL_GPU)
|
||||
# for shaders incbin
|
||||
target_include_directories(miniwin PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/d3drm/backends/gxm)
|
||||
endif()
|
||||
|
||||
if(NINTENDO_3DS)
|
||||
|
||||
680
miniwin/src/d3drm/backends/gxm/gxm_context.cpp
Normal file
680
miniwin/src/d3drm/backends/gxm/gxm_context.cpp
Normal file
@ -0,0 +1,680 @@
|
||||
#include "gxm_context.h"
|
||||
|
||||
#include "gxm_memory.h"
|
||||
#include "shaders/gxm_shaders.h"
|
||||
#include "tlsf.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <psp2/common_dialog.h>
|
||||
#include <psp2/display.h>
|
||||
#include <psp2/gxm.h>
|
||||
#include <psp2/kernel/sysmem.h>
|
||||
|
||||
static bool gxm_initialized = false;
|
||||
|
||||
bool with_razor_capture;
|
||||
bool with_razor_hud;
|
||||
|
||||
#define CDRAM_POOL_SIZE 64 * 1024 * 1024
|
||||
#define VITA_GXM_COLOR_FORMAT SCE_GXM_COLOR_FORMAT_A8B8G8R8
|
||||
|
||||
typedef struct GXMDisplayData {
|
||||
void* address;
|
||||
int index;
|
||||
} GXMDisplayData;
|
||||
|
||||
static void display_callback(const void* callback_data)
|
||||
{
|
||||
const GXMDisplayData* display_data = (const GXMDisplayData*) callback_data;
|
||||
|
||||
SceDisplayFrameBuf framebuf;
|
||||
SDL_memset(&framebuf, 0x00, sizeof(SceDisplayFrameBuf));
|
||||
framebuf.size = sizeof(SceDisplayFrameBuf);
|
||||
framebuf.base = display_data->address;
|
||||
framebuf.pitch = VITA_GXM_SCREEN_STRIDE;
|
||||
framebuf.pixelformat = VITA_GXM_PIXEL_FORMAT;
|
||||
framebuf.width = VITA_GXM_SCREEN_WIDTH;
|
||||
framebuf.height = VITA_GXM_SCREEN_HEIGHT;
|
||||
sceDisplaySetFrameBuf(&framebuf, SCE_DISPLAY_SETBUF_NEXTFRAME);
|
||||
sceDisplayWaitSetFrameBuf();
|
||||
}
|
||||
|
||||
#ifdef GXM_WITH_RAZOR
|
||||
static int load_suprx(const char* name)
|
||||
{
|
||||
sceClibPrintf("loading %s\n", name);
|
||||
int modid = _sceKernelLoadModule(name, 0, nullptr);
|
||||
if (modid < 0) {
|
||||
sceClibPrintf("%s load: 0x%08x\n", name, modid);
|
||||
return modid;
|
||||
}
|
||||
int status;
|
||||
int ret = sceKernelStartModule(modid, 0, nullptr, 0, nullptr, &status);
|
||||
if (ret < 0) {
|
||||
sceClibPrintf("%s start: 0x%08x\n", name, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void load_razor()
|
||||
{
|
||||
with_razor_capture = false;
|
||||
with_razor_hud = false;
|
||||
if (load_suprx("app0:librazorcapture_es4.suprx") >= 0) {
|
||||
with_razor_capture = true;
|
||||
}
|
||||
|
||||
if (with_razor_capture) {
|
||||
// sceRazorGpuCaptureEnableSalvage("ux0:data/gpu_crash.sgx");
|
||||
}
|
||||
|
||||
if (with_razor_hud) {
|
||||
sceRazorGpuTraceSetFilename("ux0:data/gpu_trace", 3);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int gxm_library_init()
|
||||
{
|
||||
if (gxm_initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef GXM_WITH_RAZOR
|
||||
load_razor();
|
||||
#endif
|
||||
|
||||
SceGxmInitializeParams initializeParams;
|
||||
SDL_memset(&initializeParams, 0, sizeof(SceGxmInitializeParams));
|
||||
initializeParams.flags = 0;
|
||||
initializeParams.displayQueueMaxPendingCount = VITA_GXM_PENDING_SWAPS;
|
||||
initializeParams.displayQueueCallback = display_callback;
|
||||
initializeParams.displayQueueCallbackDataSize = sizeof(GXMDisplayData);
|
||||
initializeParams.parameterBufferSize = SCE_GXM_DEFAULT_PARAMETER_BUFFER_SIZE;
|
||||
|
||||
int err = sceGxmInitialize(&initializeParams);
|
||||
if (err != 0) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "gxm init failed: %d", err);
|
||||
return err;
|
||||
}
|
||||
gxm_initialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
GXMContext* gxm;
|
||||
|
||||
void GXMContext::init_cdram_allocator()
|
||||
{
|
||||
// allocator
|
||||
this->cdramMem = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
|
||||
CDRAM_POOL_SIZE,
|
||||
16,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
||||
&this->cdramUID,
|
||||
"cdram_pool"
|
||||
);
|
||||
this->cdramPool = SDL_malloc(tlsf_size());
|
||||
tlsf_create(this->cdramPool);
|
||||
tlsf_add_pool(this->cdramPool, this->cdramMem, CDRAM_POOL_SIZE);
|
||||
}
|
||||
|
||||
int GXMContext::init_context()
|
||||
{
|
||||
int ret;
|
||||
|
||||
const unsigned int patcherBufferSize = 64 * 1024;
|
||||
const unsigned int patcherVertexUsseSize = 64 * 1024;
|
||||
const unsigned int patcherFragmentUsseSize = 64 * 1024;
|
||||
|
||||
// allocate buffers
|
||||
this->vdmRingBuffer = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
||||
SCE_GXM_DEFAULT_VDM_RING_BUFFER_SIZE,
|
||||
4,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ,
|
||||
&this->vdmRingBufferUid,
|
||||
"vdmRingBuffer"
|
||||
);
|
||||
|
||||
this->vertexRingBuffer = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
||||
SCE_GXM_DEFAULT_VERTEX_RING_BUFFER_SIZE,
|
||||
4,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ,
|
||||
&this->vertexRingBufferUid,
|
||||
"vertexRingBuffer"
|
||||
);
|
||||
|
||||
this->fragmentRingBuffer = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
||||
SCE_GXM_DEFAULT_FRAGMENT_RING_BUFFER_SIZE,
|
||||
4,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ,
|
||||
&this->fragmentRingBufferUid,
|
||||
"fragmentRingBuffer"
|
||||
);
|
||||
|
||||
this->fragmentUsseRingBuffer = vita_mem_fragment_usse_alloc(
|
||||
SCE_GXM_DEFAULT_FRAGMENT_USSE_RING_BUFFER_SIZE,
|
||||
&this->fragmentUsseRingBufferUid,
|
||||
&this->fragmentUsseRingBufferOffset
|
||||
);
|
||||
|
||||
// create context
|
||||
SceGxmContextParams contextParams;
|
||||
memset(&contextParams, 0, sizeof(SceGxmContextParams));
|
||||
contextParams.hostMem = SDL_malloc(SCE_GXM_MINIMUM_CONTEXT_HOST_MEM_SIZE);
|
||||
contextParams.hostMemSize = SCE_GXM_MINIMUM_CONTEXT_HOST_MEM_SIZE;
|
||||
contextParams.vdmRingBufferMem = this->vdmRingBuffer;
|
||||
contextParams.vdmRingBufferMemSize = SCE_GXM_DEFAULT_VDM_RING_BUFFER_SIZE;
|
||||
contextParams.vertexRingBufferMem = this->vertexRingBuffer;
|
||||
contextParams.vertexRingBufferMemSize = SCE_GXM_DEFAULT_VERTEX_RING_BUFFER_SIZE;
|
||||
contextParams.fragmentRingBufferMem = this->fragmentRingBuffer;
|
||||
contextParams.fragmentRingBufferMemSize = SCE_GXM_DEFAULT_FRAGMENT_RING_BUFFER_SIZE;
|
||||
contextParams.fragmentUsseRingBufferMem = this->fragmentUsseRingBuffer;
|
||||
contextParams.fragmentUsseRingBufferMemSize = SCE_GXM_DEFAULT_FRAGMENT_USSE_RING_BUFFER_SIZE;
|
||||
contextParams.fragmentUsseRingBufferOffset = this->fragmentUsseRingBufferOffset;
|
||||
|
||||
ret = SCE_ERR(sceGxmCreateContext, &contextParams, &this->context);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
this->contextHostMem = contextParams.hostMem;
|
||||
|
||||
// shader patcher
|
||||
this->patcherBuffer = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
||||
patcherBufferSize,
|
||||
4,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
||||
&this->patcherBufferUid,
|
||||
"patcherBuffer"
|
||||
);
|
||||
|
||||
this->patcherVertexUsse =
|
||||
vita_mem_vertex_usse_alloc(patcherVertexUsseSize, &this->patcherVertexUsseUid, &this->patcherVertexUsseOffset);
|
||||
|
||||
this->patcherFragmentUsse = vita_mem_fragment_usse_alloc(
|
||||
patcherFragmentUsseSize,
|
||||
&this->patcherFragmentUsseUid,
|
||||
&this->patcherFragmentUsseOffset
|
||||
);
|
||||
|
||||
SceGxmShaderPatcherParams patcherParams;
|
||||
memset(&patcherParams, 0, sizeof(SceGxmShaderPatcherParams));
|
||||
patcherParams.userData = NULL;
|
||||
patcherParams.hostAllocCallback = &patcher_host_alloc;
|
||||
patcherParams.hostFreeCallback = &patcher_host_free;
|
||||
patcherParams.bufferAllocCallback = NULL;
|
||||
patcherParams.bufferFreeCallback = NULL;
|
||||
patcherParams.bufferMem = this->patcherBuffer;
|
||||
patcherParams.bufferMemSize = patcherBufferSize;
|
||||
patcherParams.vertexUsseAllocCallback = NULL;
|
||||
patcherParams.vertexUsseFreeCallback = NULL;
|
||||
patcherParams.vertexUsseMem = this->patcherVertexUsse;
|
||||
patcherParams.vertexUsseMemSize = patcherVertexUsseSize;
|
||||
patcherParams.vertexUsseOffset = this->patcherVertexUsseOffset;
|
||||
patcherParams.fragmentUsseAllocCallback = NULL;
|
||||
patcherParams.fragmentUsseFreeCallback = NULL;
|
||||
patcherParams.fragmentUsseMem = this->patcherFragmentUsse;
|
||||
patcherParams.fragmentUsseMemSize = patcherFragmentUsseSize;
|
||||
patcherParams.fragmentUsseOffset = this->patcherFragmentUsseOffset;
|
||||
|
||||
ret = SCE_ERR(sceGxmShaderPatcherCreate, &patcherParams, &this->shaderPatcher);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GXMContext::create_display_buffers(SceGxmMultisampleMode msaaMode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
const uint32_t alignedWidth = ALIGN(VITA_GXM_SCREEN_WIDTH, SCE_GXM_TILE_SIZEX);
|
||||
const uint32_t alignedHeight = ALIGN(VITA_GXM_SCREEN_HEIGHT, SCE_GXM_TILE_SIZEY);
|
||||
uint32_t sampleCount = alignedWidth * alignedHeight;
|
||||
uint32_t depthStrideInSamples = alignedWidth;
|
||||
|
||||
if (msaaMode == SCE_GXM_MULTISAMPLE_4X) {
|
||||
sampleCount *= 4;
|
||||
depthStrideInSamples *= 2;
|
||||
}
|
||||
else if (msaaMode == SCE_GXM_MULTISAMPLE_2X) {
|
||||
sampleCount *= 2;
|
||||
}
|
||||
|
||||
// render target
|
||||
SceGxmRenderTargetParams renderTargetParams;
|
||||
memset(&renderTargetParams, 0, sizeof(SceGxmRenderTargetParams));
|
||||
renderTargetParams.flags = 0;
|
||||
renderTargetParams.width = VITA_GXM_SCREEN_WIDTH;
|
||||
renderTargetParams.height = VITA_GXM_SCREEN_HEIGHT;
|
||||
renderTargetParams.scenesPerFrame = 1;
|
||||
renderTargetParams.multisampleMode = msaaMode;
|
||||
renderTargetParams.multisampleLocations = 0;
|
||||
renderTargetParams.driverMemBlock = -1; // Invalid UID
|
||||
ret = SCE_ERR(sceGxmCreateRenderTarget, &renderTargetParams, &this->renderTarget);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
this->renderTargetInit = true;
|
||||
|
||||
for (int i = 0; i < GXM_DISPLAY_BUFFER_COUNT; i++) {
|
||||
this->displayBuffers[i] = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
|
||||
4 * VITA_GXM_SCREEN_STRIDE * VITA_GXM_SCREEN_HEIGHT,
|
||||
SCE_GXM_COLOR_SURFACE_ALIGNMENT,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
||||
&this->displayBuffersUid[i],
|
||||
"displayBuffers"
|
||||
);
|
||||
|
||||
ret = SCE_ERR(
|
||||
sceGxmColorSurfaceInit,
|
||||
&this->displayBuffersSurface[i],
|
||||
SCE_GXM_COLOR_FORMAT_A8B8G8R8,
|
||||
SCE_GXM_COLOR_SURFACE_LINEAR,
|
||||
(msaaMode == SCE_GXM_MULTISAMPLE_NONE) ? SCE_GXM_COLOR_SURFACE_SCALE_NONE
|
||||
: SCE_GXM_COLOR_SURFACE_SCALE_MSAA_DOWNSCALE,
|
||||
SCE_GXM_OUTPUT_REGISTER_SIZE_32BIT,
|
||||
VITA_GXM_SCREEN_WIDTH,
|
||||
VITA_GXM_SCREEN_HEIGHT,
|
||||
VITA_GXM_SCREEN_STRIDE,
|
||||
this->displayBuffers[i]
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = SCE_ERR(sceGxmSyncObjectCreate, &this->displayBuffersSync[i]);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// depth & stencil
|
||||
this->depthBufferData = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
||||
4 * sampleCount,
|
||||
SCE_GXM_DEPTHSTENCIL_SURFACE_ALIGNMENT,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
||||
&this->depthBufferUid,
|
||||
"depthBufferData"
|
||||
);
|
||||
|
||||
this->stencilBufferData = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
||||
1 * sampleCount,
|
||||
SCE_GXM_DEPTHSTENCIL_SURFACE_ALIGNMENT,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
||||
&this->stencilBufferUid,
|
||||
"stencilBufferData"
|
||||
);
|
||||
|
||||
ret = SCE_ERR(
|
||||
sceGxmDepthStencilSurfaceInit,
|
||||
&this->depthSurface,
|
||||
SCE_GXM_DEPTH_STENCIL_FORMAT_DF32_S8,
|
||||
SCE_GXM_DEPTH_STENCIL_SURFACE_TILED,
|
||||
depthStrideInSamples,
|
||||
this->depthBufferData,
|
||||
this->stencilBufferData
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GXMContext::destroy_display_buffers()
|
||||
{
|
||||
if (this->renderTargetInit) {
|
||||
sceGxmFinish(this->context);
|
||||
sceGxmDestroyRenderTarget(this->renderTarget);
|
||||
this->renderTargetInit = false;
|
||||
}
|
||||
for (int i = 0; i < GXM_DISPLAY_BUFFER_COUNT; i++) {
|
||||
if (this->displayBuffers[i]) {
|
||||
vita_mem_free(this->displayBuffersUid[i]);
|
||||
this->displayBuffers[i] = nullptr;
|
||||
this->displayBuffersUid[i] = -1;
|
||||
sceGxmSyncObjectDestroy(this->displayBuffersSync[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->depthBufferData) {
|
||||
vita_mem_free(this->depthBufferUid);
|
||||
this->depthBufferData = nullptr;
|
||||
this->depthBufferUid = -1;
|
||||
}
|
||||
|
||||
if (this->stencilBufferData) {
|
||||
vita_mem_free(this->stencilBufferUid);
|
||||
this->stencilBufferData = nullptr;
|
||||
this->stencilBufferUid = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void GXMContext::init_clear_mesh()
|
||||
{
|
||||
this->clearVertices = static_cast<GXMVertex2D*>(this->alloc(sizeof(GXMVertex2D) * 4, 4));
|
||||
this->clearVertices[0] = {.position = {-1.0, 1.0}, .texCoord = {0, 0}};
|
||||
this->clearVertices[1] = {.position = {1.0, 1.0}, .texCoord = {0, 0}};
|
||||
this->clearVertices[2] = {.position = {-1.0, -1.0}, .texCoord = {0, 0}};
|
||||
this->clearVertices[3] = {.position = {1.0, -1.0}, .texCoord = {0, 0}};
|
||||
|
||||
this->clearIndices = static_cast<uint16_t*>(this->alloc(sizeof(uint16_t) * 4, 4));
|
||||
this->clearIndices[0] = 0;
|
||||
this->clearIndices[1] = 1;
|
||||
this->clearIndices[2] = 2;
|
||||
this->clearIndices[3] = 3;
|
||||
}
|
||||
|
||||
int GXMContext::register_base_shaders()
|
||||
{
|
||||
int ret;
|
||||
// register plane, color, image shaders
|
||||
ret = SCE_ERR(
|
||||
sceGxmShaderPatcherRegisterProgram,
|
||||
this->shaderPatcher,
|
||||
planeVertexProgramGxp,
|
||||
&this->planeVertexProgramId
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = SCE_ERR(
|
||||
sceGxmShaderPatcherRegisterProgram,
|
||||
this->shaderPatcher,
|
||||
colorFragmentProgramGxp,
|
||||
&this->colorFragmentProgramId
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = SCE_ERR(
|
||||
sceGxmShaderPatcherRegisterProgram,
|
||||
this->shaderPatcher,
|
||||
imageFragmentProgramGxp,
|
||||
&this->imageFragmentProgramId
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
this->color_uColor = sceGxmProgramFindParameterByName(colorFragmentProgramGxp, "uColor"); // vec4
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GXMContext::patch_base_shaders(SceGxmMultisampleMode msaaMode)
|
||||
{
|
||||
int ret;
|
||||
{
|
||||
GET_SHADER_PARAM(positionAttribute, planeVertexProgramGxp, "aPosition", -1);
|
||||
GET_SHADER_PARAM(texCoordAttribute, planeVertexProgramGxp, "aTexCoord", -1);
|
||||
|
||||
SceGxmVertexAttribute vertexAttributes[2];
|
||||
SceGxmVertexStream vertexStreams[1];
|
||||
|
||||
// position
|
||||
vertexAttributes[0].streamIndex = 0;
|
||||
vertexAttributes[0].offset = 0;
|
||||
vertexAttributes[0].format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
|
||||
vertexAttributes[0].componentCount = 2;
|
||||
vertexAttributes[0].regIndex = sceGxmProgramParameterGetResourceIndex(positionAttribute);
|
||||
|
||||
// uv
|
||||
vertexAttributes[1].streamIndex = 0;
|
||||
vertexAttributes[1].offset = 8;
|
||||
vertexAttributes[1].format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
|
||||
vertexAttributes[1].componentCount = 2;
|
||||
vertexAttributes[1].regIndex = sceGxmProgramParameterGetResourceIndex(texCoordAttribute);
|
||||
|
||||
vertexStreams[0].stride = sizeof(GXMVertex2D);
|
||||
vertexStreams[0].indexSource = SCE_GXM_INDEX_SOURCE_INDEX_16BIT;
|
||||
|
||||
ret = SCE_ERR(
|
||||
sceGxmShaderPatcherCreateVertexProgram,
|
||||
this->shaderPatcher,
|
||||
this->planeVertexProgramId,
|
||||
vertexAttributes,
|
||||
2,
|
||||
vertexStreams,
|
||||
1,
|
||||
&this->planeVertexProgram
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = SCE_ERR(
|
||||
sceGxmShaderPatcherCreateFragmentProgram,
|
||||
this->shaderPatcher,
|
||||
this->colorFragmentProgramId,
|
||||
SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
|
||||
msaaMode,
|
||||
NULL,
|
||||
planeVertexProgramGxp,
|
||||
&this->colorFragmentProgram
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = SCE_ERR(
|
||||
sceGxmShaderPatcherCreateFragmentProgram,
|
||||
this->shaderPatcher,
|
||||
this->imageFragmentProgramId,
|
||||
SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
|
||||
msaaMode,
|
||||
&blendInfoTransparent,
|
||||
planeVertexProgramGxp,
|
||||
&this->imageFragmentProgram
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GXMContext::destroy_base_shaders()
|
||||
{
|
||||
sceGxmShaderPatcherReleaseVertexProgram(this->shaderPatcher, this->planeVertexProgram);
|
||||
sceGxmShaderPatcherReleaseFragmentProgram(this->shaderPatcher, this->colorFragmentProgram);
|
||||
sceGxmShaderPatcherReleaseFragmentProgram(this->shaderPatcher, this->imageFragmentProgram);
|
||||
}
|
||||
|
||||
int GXMContext::init(SceGxmMultisampleMode msaaMode)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = gxm_library_init();
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (this->cdramPool == nullptr) {
|
||||
this->init_cdram_allocator();
|
||||
}
|
||||
|
||||
if (this->context == nullptr) {
|
||||
ret = this->init_context();
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->planeVertexProgramId == 0) {
|
||||
ret = this->register_base_shaders();
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->clearVertices == nullptr) {
|
||||
this->init_clear_mesh();
|
||||
}
|
||||
|
||||
// recreate when msaa is different
|
||||
if (msaaMode != this->displayMsaa && this->renderTargetInit) {
|
||||
this->destroy_display_buffers();
|
||||
this->destroy_base_shaders();
|
||||
}
|
||||
|
||||
if (!this->renderTargetInit) {
|
||||
ret = this->create_display_buffers(msaaMode);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
ret = this->patch_base_shaders(msaaMode);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int inuse_mem = 0;
|
||||
void* GXMContext::alloc(size_t size, size_t align)
|
||||
{
|
||||
if (this->cdramPool == nullptr) {
|
||||
this->init_cdram_allocator();
|
||||
}
|
||||
DEBUG_ONLY_PRINTF("cdram_alloc(%d, %d) inuse=%d ", size, align, inuse_mem);
|
||||
void* ptr = tlsf_memalign(this->cdramPool, align, size);
|
||||
DEBUG_ONLY_PRINTF("ptr=%p\n", ptr);
|
||||
inuse_mem += tlsf_block_size(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void GXMContext::free(void* ptr)
|
||||
{
|
||||
inuse_mem -= tlsf_block_size(ptr);
|
||||
DEBUG_ONLY_PRINTF("cdram_free(%p)\n", ptr);
|
||||
tlsf_free(this->cdramPool, ptr);
|
||||
}
|
||||
|
||||
void GXMContext::clear(float r, float g, float b, bool new_scene)
|
||||
{
|
||||
new_scene = new_scene && !this->sceneStarted;
|
||||
if (new_scene) {
|
||||
sceGxmBeginScene(
|
||||
this->context,
|
||||
0,
|
||||
this->renderTarget,
|
||||
nullptr,
|
||||
nullptr,
|
||||
this->displayBuffersSync[this->backBufferIndex],
|
||||
&this->displayBuffersSurface[this->backBufferIndex],
|
||||
&this->depthSurface
|
||||
);
|
||||
this->sceneStarted = true;
|
||||
}
|
||||
|
||||
float color[] = {r, g, b, 1};
|
||||
|
||||
sceGxmSetVertexProgram(this->context, this->planeVertexProgram);
|
||||
sceGxmSetFragmentProgram(this->context, this->colorFragmentProgram);
|
||||
|
||||
void* vertUniforms;
|
||||
void* fragUniforms;
|
||||
sceGxmReserveVertexDefaultUniformBuffer(gxm->context, &vertUniforms);
|
||||
sceGxmReserveFragmentDefaultUniformBuffer(gxm->context, &fragUniforms);
|
||||
|
||||
sceGxmSetVertexStream(gxm->context, 0, this->clearVertices);
|
||||
sceGxmSetUniformDataF(fragUniforms, this->color_uColor, 0, 4, color);
|
||||
|
||||
sceGxmSetFrontDepthFunc(gxm->context, SCE_GXM_DEPTH_FUNC_ALWAYS);
|
||||
sceGxmDraw(gxm->context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, SCE_GXM_INDEX_FORMAT_U16, this->clearIndices, 4);
|
||||
if (new_scene) {
|
||||
sceGxmEndScene(this->context, nullptr, nullptr);
|
||||
this->sceneStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
void GXMContext::copy_frontbuffer()
|
||||
{
|
||||
SceGxmTexture texture;
|
||||
sceGxmTextureInitLinearStrided(
|
||||
&texture,
|
||||
this->displayBuffers[this->frontBufferIndex],
|
||||
SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_ABGR,
|
||||
VITA_GXM_SCREEN_WIDTH,
|
||||
VITA_GXM_SCREEN_HEIGHT,
|
||||
VITA_GXM_SCREEN_STRIDE * 4
|
||||
);
|
||||
sceGxmSetVertexProgram(this->context, this->planeVertexProgram);
|
||||
sceGxmSetFragmentProgram(this->context, this->imageFragmentProgram);
|
||||
|
||||
void* vertUniforms;
|
||||
void* fragUniforms;
|
||||
sceGxmReserveVertexDefaultUniformBuffer(this->context, &vertUniforms);
|
||||
sceGxmReserveFragmentDefaultUniformBuffer(this->context, &fragUniforms);
|
||||
|
||||
sceGxmSetVertexStream(this->context, 0, this->clearVertices);
|
||||
sceGxmSetFragmentTexture(this->context, 0, &texture);
|
||||
|
||||
sceGxmSetFrontDepthFunc(this->context, SCE_GXM_DEPTH_FUNC_ALWAYS);
|
||||
sceGxmDraw(this->context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, SCE_GXM_INDEX_FORMAT_U16, this->clearIndices, 4);
|
||||
}
|
||||
|
||||
void GXMContext::destroy()
|
||||
{
|
||||
sceGxmDisplayQueueFinish();
|
||||
if (gxm->context) {
|
||||
sceGxmFinish(gxm->context);
|
||||
}
|
||||
|
||||
this->destroy_display_buffers();
|
||||
this->destroy_base_shaders();
|
||||
|
||||
sceGxmShaderPatcherDestroy(this->shaderPatcher);
|
||||
sceGxmDestroyContext(this->context);
|
||||
vita_mem_fragment_usse_free(this->fragmentUsseRingBufferUid);
|
||||
vita_mem_free(this->fragmentRingBufferUid);
|
||||
vita_mem_free(this->vertexRingBufferUid);
|
||||
vita_mem_free(this->vdmRingBufferUid);
|
||||
SDL_free(this->contextHostMem);
|
||||
}
|
||||
|
||||
void GXMContext::swap_display()
|
||||
{
|
||||
if (this->sceneStarted) {
|
||||
sceGxmEndScene(gxm->context, nullptr, nullptr);
|
||||
this->sceneStarted = false;
|
||||
}
|
||||
|
||||
SceCommonDialogUpdateParam updateParam;
|
||||
SDL_zero(updateParam);
|
||||
updateParam.renderTarget.colorFormat = VITA_GXM_COLOR_FORMAT;
|
||||
updateParam.renderTarget.surfaceType = SCE_GXM_COLOR_SURFACE_LINEAR;
|
||||
updateParam.renderTarget.width = VITA_GXM_SCREEN_WIDTH;
|
||||
updateParam.renderTarget.height = VITA_GXM_SCREEN_HEIGHT;
|
||||
updateParam.renderTarget.strideInPixels = VITA_GXM_SCREEN_STRIDE;
|
||||
updateParam.renderTarget.colorSurfaceData = this->displayBuffers[this->backBufferIndex];
|
||||
updateParam.displaySyncObject = this->displayBuffersSync[this->backBufferIndex];
|
||||
sceCommonDialogUpdate(&updateParam);
|
||||
|
||||
sceGxmPadHeartbeat(
|
||||
&this->displayBuffersSurface[this->backBufferIndex],
|
||||
this->displayBuffersSync[this->backBufferIndex]
|
||||
);
|
||||
|
||||
// display
|
||||
GXMDisplayData displayData;
|
||||
displayData.address = this->displayBuffers[this->backBufferIndex];
|
||||
displayData.index = this->backBufferIndex;
|
||||
sceGxmDisplayQueueAddEntry(
|
||||
this->displayBuffersSync[this->frontBufferIndex],
|
||||
this->displayBuffersSync[this->backBufferIndex],
|
||||
&displayData
|
||||
);
|
||||
|
||||
this->frontBufferIndex = this->backBufferIndex;
|
||||
this->backBufferIndex = (this->backBufferIndex + 1) % GXM_DISPLAY_BUFFER_COUNT;
|
||||
}
|
||||
@ -1,9 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "tlsf.h"
|
||||
|
||||
#include <psp2/gxm.h>
|
||||
|
||||
#define VITA_GXM_SCREEN_WIDTH 960
|
||||
#define VITA_GXM_SCREEN_HEIGHT 544
|
||||
#define VITA_GXM_SCREEN_STRIDE 1024
|
||||
#define VITA_GXM_PENDING_SWAPS 2
|
||||
#define VITA_GXM_PIXEL_FORMAT SCE_DISPLAY_PIXELFORMAT_A8B8G8R8
|
||||
|
||||
#define GXM_DISPLAY_BUFFER_COUNT 3
|
||||
|
||||
typedef struct GXMVertex2D {
|
||||
@ -72,7 +76,7 @@ typedef struct GXMContext {
|
||||
// allocator
|
||||
SceUID cdramUID;
|
||||
void* cdramMem;
|
||||
tlsf_t cdramPool;
|
||||
void* cdramPool;
|
||||
|
||||
bool sceneStarted;
|
||||
|
||||
@ -96,3 +100,6 @@ typedef struct GXMContext {
|
||||
|
||||
// global so that common dialog can be rendererd without GXMRenderer
|
||||
extern GXMContext* gxm;
|
||||
|
||||
extern bool with_razor_capture;
|
||||
extern bool with_razor_hud;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "gxm_memory.h"
|
||||
|
||||
#include "tlsf.h"
|
||||
#include "utils.h"
|
||||
|
||||
@ -7,12 +8,6 @@
|
||||
#include <psp2/kernel/clib.h>
|
||||
#include <psp2/kernel/sysmem.h>
|
||||
|
||||
#define CDRAM_POOL_SIZE 64 * 1024 * 1024
|
||||
|
||||
static SceUID cdramAllocatorUID = -1;
|
||||
static tlsf_t cdramAllocator = nullptr;
|
||||
int inuse_mem = 0;
|
||||
|
||||
void* patcher_host_alloc(void* user_data, unsigned int size)
|
||||
{
|
||||
void* mem = SDL_malloc(size);
|
||||
@ -3,6 +3,7 @@
|
||||
#include "gxm_memory.h"
|
||||
#include "meshutils.h"
|
||||
#include "razor.h"
|
||||
#include "shaders/gxm_shaders.h"
|
||||
#include "tlsf.h"
|
||||
#include "utils.h"
|
||||
|
||||
@ -15,12 +16,6 @@
|
||||
#include <psp2/kernel/sysmem.h>
|
||||
#include <psp2/types.h>
|
||||
#include <string>
|
||||
#define INCBIN_PREFIX _inc_
|
||||
#include "incbin.h"
|
||||
|
||||
static bool with_razor_capture = false;
|
||||
static bool with_razor_hud = false;
|
||||
static bool gxm_initialized = false;
|
||||
|
||||
// from isleapp
|
||||
extern bool g_dpadUp;
|
||||
@ -28,748 +23,18 @@ extern bool g_dpadDown;
|
||||
extern bool g_dpadLeft;
|
||||
extern bool g_dpadRight;
|
||||
|
||||
#define VITA_GXM_SCREEN_WIDTH 960
|
||||
#define VITA_GXM_SCREEN_HEIGHT 544
|
||||
#define VITA_GXM_SCREEN_STRIDE 1024
|
||||
#define VITA_GXM_PENDING_SWAPS 2
|
||||
#define CDRAM_POOL_SIZE 64 * 1024 * 1024
|
||||
|
||||
#define VITA_GXM_COLOR_FORMAT SCE_GXM_COLOR_FORMAT_A8B8G8R8
|
||||
#define VITA_GXM_PIXEL_FORMAT SCE_DISPLAY_PIXELFORMAT_A8B8G8R8
|
||||
|
||||
#define SCE_GXM_PRECOMPUTED_ALIGNMENT 16
|
||||
|
||||
#define INCSHADER(filename, name) \
|
||||
INCBIN(name, filename); \
|
||||
const SceGxmProgram* name = (const SceGxmProgram*) _inc_##name##Data;
|
||||
|
||||
INCSHADER("shaders/main.vert.gxp", mainVertexProgramGxp);
|
||||
INCSHADER("shaders/main.color.frag.gxp", mainColorFragmentProgramGxp);
|
||||
INCSHADER("shaders/main.texture.frag.gxp", mainTextureFragmentProgramGxp);
|
||||
|
||||
INCSHADER("shaders/plane.vert.gxp", planeVertexProgramGxp);
|
||||
INCSHADER("shaders/image.frag.gxp", imageFragmentProgramGxp);
|
||||
INCSHADER("shaders/color.frag.gxp", colorFragmentProgramGxp);
|
||||
|
||||
static const SceGxmBlendInfo blendInfoOpaque = {
|
||||
.colorMask = SCE_GXM_COLOR_MASK_ALL,
|
||||
.colorFunc = SCE_GXM_BLEND_FUNC_NONE,
|
||||
.alphaFunc = SCE_GXM_BLEND_FUNC_NONE,
|
||||
.colorSrc = SCE_GXM_BLEND_FACTOR_ZERO,
|
||||
.colorDst = SCE_GXM_BLEND_FACTOR_ZERO,
|
||||
.alphaSrc = SCE_GXM_BLEND_FACTOR_ZERO,
|
||||
.alphaDst = SCE_GXM_BLEND_FACTOR_ZERO,
|
||||
};
|
||||
|
||||
static const SceGxmBlendInfo blendInfoTransparent = {
|
||||
.colorMask = SCE_GXM_COLOR_MASK_ALL,
|
||||
.colorFunc = SCE_GXM_BLEND_FUNC_ADD,
|
||||
.alphaFunc = SCE_GXM_BLEND_FUNC_ADD,
|
||||
.colorSrc = SCE_GXM_BLEND_FACTOR_SRC_ALPHA,
|
||||
.colorDst = SCE_GXM_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
||||
.alphaSrc = SCE_GXM_BLEND_FACTOR_ONE,
|
||||
.alphaDst = SCE_GXM_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
||||
};
|
||||
|
||||
typedef struct GXMVertex {
|
||||
float position[3];
|
||||
float normal[3];
|
||||
float texCoord[2];
|
||||
} GXMVertex;
|
||||
|
||||
typedef struct GXMDisplayData {
|
||||
void* address;
|
||||
int index;
|
||||
} GXMDisplayData;
|
||||
|
||||
static void display_callback(const void* callback_data)
|
||||
{
|
||||
const GXMDisplayData* display_data = (const GXMDisplayData*) callback_data;
|
||||
|
||||
GXMRenderer* renderer = static_cast<GXMRenderer*>(DDRenderer);
|
||||
renderer->DeferredDelete(display_data->index);
|
||||
|
||||
SceDisplayFrameBuf framebuf;
|
||||
SDL_memset(&framebuf, 0x00, sizeof(SceDisplayFrameBuf));
|
||||
framebuf.size = sizeof(SceDisplayFrameBuf);
|
||||
framebuf.base = display_data->address;
|
||||
framebuf.pitch = VITA_GXM_SCREEN_STRIDE;
|
||||
framebuf.pixelformat = VITA_GXM_PIXEL_FORMAT;
|
||||
framebuf.width = VITA_GXM_SCREEN_WIDTH;
|
||||
framebuf.height = VITA_GXM_SCREEN_HEIGHT;
|
||||
sceDisplaySetFrameBuf(&framebuf, SCE_DISPLAY_SETBUF_NEXTFRAME);
|
||||
sceDisplayWaitSetFrameBuf();
|
||||
}
|
||||
|
||||
#ifdef GXM_WITH_RAZOR
|
||||
#include <taihen.h>
|
||||
|
||||
static int load_skprx(const char* name)
|
||||
{
|
||||
int modid = taiLoadKernelModule(name, 0, nullptr);
|
||||
if (modid < 0) {
|
||||
sceClibPrintf("%s load: 0x%08x\n", name, modid);
|
||||
return modid;
|
||||
}
|
||||
int status;
|
||||
int ret = taiStartKernelModule(modid, 0, nullptr, 0, nullptr, &status);
|
||||
if (ret < 0) {
|
||||
sceClibPrintf("%s start: 0x%08x\n", name, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int load_suprx(const char* name)
|
||||
{
|
||||
sceClibPrintf("loading %s\n", name);
|
||||
int modid = _sceKernelLoadModule(name, 0, nullptr);
|
||||
if (modid < 0) {
|
||||
sceClibPrintf("%s load: 0x%08x\n", name, modid);
|
||||
return modid;
|
||||
}
|
||||
int status;
|
||||
int ret = sceKernelStartModule(modid, 0, nullptr, 0, nullptr, &status);
|
||||
if (ret < 0) {
|
||||
sceClibPrintf("%s start: 0x%08x\n", name, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void load_razor()
|
||||
{
|
||||
if (load_suprx("app0:librazorcapture_es4.suprx") >= 0) {
|
||||
with_razor_capture = true;
|
||||
}
|
||||
|
||||
if (with_razor_capture) {
|
||||
// sceRazorGpuCaptureEnableSalvage("ux0:data/gpu_crash.sgx");
|
||||
}
|
||||
|
||||
if (with_razor_hud) {
|
||||
sceRazorGpuTraceSetFilename("ux0:data/gpu_trace", 3);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int gxm_library_init()
|
||||
{
|
||||
if (gxm_initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef GXM_WITH_RAZOR
|
||||
load_razor();
|
||||
#endif
|
||||
|
||||
SceGxmInitializeParams initializeParams;
|
||||
SDL_memset(&initializeParams, 0, sizeof(SceGxmInitializeParams));
|
||||
initializeParams.flags = 0;
|
||||
initializeParams.displayQueueMaxPendingCount = VITA_GXM_PENDING_SWAPS;
|
||||
initializeParams.displayQueueCallback = display_callback;
|
||||
initializeParams.displayQueueCallbackDataSize = sizeof(GXMDisplayData);
|
||||
initializeParams.parameterBufferSize = SCE_GXM_DEFAULT_PARAMETER_BUFFER_SIZE;
|
||||
|
||||
int err = sceGxmInitialize(&initializeParams);
|
||||
if (err != 0) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "gxm init failed: %d", err);
|
||||
return err;
|
||||
}
|
||||
gxm_initialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
GXMContext* gxm;
|
||||
|
||||
void GXMContext::init_cdram_allocator()
|
||||
{
|
||||
// allocator
|
||||
this->cdramMem = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
|
||||
CDRAM_POOL_SIZE,
|
||||
16,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
||||
&this->cdramUID,
|
||||
"cdram_pool"
|
||||
);
|
||||
this->cdramPool = SDL_malloc(tlsf_size());
|
||||
tlsf_create(this->cdramPool);
|
||||
tlsf_add_pool(this->cdramPool, this->cdramMem, CDRAM_POOL_SIZE);
|
||||
}
|
||||
|
||||
int GXMContext::init_context()
|
||||
{
|
||||
int ret;
|
||||
|
||||
const unsigned int patcherBufferSize = 64 * 1024;
|
||||
const unsigned int patcherVertexUsseSize = 64 * 1024;
|
||||
const unsigned int patcherFragmentUsseSize = 64 * 1024;
|
||||
|
||||
// allocate buffers
|
||||
this->vdmRingBuffer = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
||||
SCE_GXM_DEFAULT_VDM_RING_BUFFER_SIZE,
|
||||
4,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ,
|
||||
&this->vdmRingBufferUid,
|
||||
"vdmRingBuffer"
|
||||
);
|
||||
|
||||
this->vertexRingBuffer = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
||||
SCE_GXM_DEFAULT_VERTEX_RING_BUFFER_SIZE,
|
||||
4,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ,
|
||||
&this->vertexRingBufferUid,
|
||||
"vertexRingBuffer"
|
||||
);
|
||||
|
||||
this->fragmentRingBuffer = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
||||
SCE_GXM_DEFAULT_FRAGMENT_RING_BUFFER_SIZE,
|
||||
4,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ,
|
||||
&this->fragmentRingBufferUid,
|
||||
"fragmentRingBuffer"
|
||||
);
|
||||
|
||||
this->fragmentUsseRingBuffer = vita_mem_fragment_usse_alloc(
|
||||
SCE_GXM_DEFAULT_FRAGMENT_USSE_RING_BUFFER_SIZE,
|
||||
&this->fragmentUsseRingBufferUid,
|
||||
&this->fragmentUsseRingBufferOffset
|
||||
);
|
||||
|
||||
// create context
|
||||
SceGxmContextParams contextParams;
|
||||
memset(&contextParams, 0, sizeof(SceGxmContextParams));
|
||||
contextParams.hostMem = SDL_malloc(SCE_GXM_MINIMUM_CONTEXT_HOST_MEM_SIZE);
|
||||
contextParams.hostMemSize = SCE_GXM_MINIMUM_CONTEXT_HOST_MEM_SIZE;
|
||||
contextParams.vdmRingBufferMem = this->vdmRingBuffer;
|
||||
contextParams.vdmRingBufferMemSize = SCE_GXM_DEFAULT_VDM_RING_BUFFER_SIZE;
|
||||
contextParams.vertexRingBufferMem = this->vertexRingBuffer;
|
||||
contextParams.vertexRingBufferMemSize = SCE_GXM_DEFAULT_VERTEX_RING_BUFFER_SIZE;
|
||||
contextParams.fragmentRingBufferMem = this->fragmentRingBuffer;
|
||||
contextParams.fragmentRingBufferMemSize = SCE_GXM_DEFAULT_FRAGMENT_RING_BUFFER_SIZE;
|
||||
contextParams.fragmentUsseRingBufferMem = this->fragmentUsseRingBuffer;
|
||||
contextParams.fragmentUsseRingBufferMemSize = SCE_GXM_DEFAULT_FRAGMENT_USSE_RING_BUFFER_SIZE;
|
||||
contextParams.fragmentUsseRingBufferOffset = this->fragmentUsseRingBufferOffset;
|
||||
|
||||
ret = SCE_ERR(sceGxmCreateContext, &contextParams, &this->context);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
this->contextHostMem = contextParams.hostMem;
|
||||
|
||||
// shader patcher
|
||||
this->patcherBuffer = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
||||
patcherBufferSize,
|
||||
4,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
||||
&this->patcherBufferUid,
|
||||
"patcherBuffer"
|
||||
);
|
||||
|
||||
this->patcherVertexUsse =
|
||||
vita_mem_vertex_usse_alloc(patcherVertexUsseSize, &this->patcherVertexUsseUid, &this->patcherVertexUsseOffset);
|
||||
|
||||
this->patcherFragmentUsse = vita_mem_fragment_usse_alloc(
|
||||
patcherFragmentUsseSize,
|
||||
&this->patcherFragmentUsseUid,
|
||||
&this->patcherFragmentUsseOffset
|
||||
);
|
||||
|
||||
SceGxmShaderPatcherParams patcherParams;
|
||||
memset(&patcherParams, 0, sizeof(SceGxmShaderPatcherParams));
|
||||
patcherParams.userData = NULL;
|
||||
patcherParams.hostAllocCallback = &patcher_host_alloc;
|
||||
patcherParams.hostFreeCallback = &patcher_host_free;
|
||||
patcherParams.bufferAllocCallback = NULL;
|
||||
patcherParams.bufferFreeCallback = NULL;
|
||||
patcherParams.bufferMem = this->patcherBuffer;
|
||||
patcherParams.bufferMemSize = patcherBufferSize;
|
||||
patcherParams.vertexUsseAllocCallback = NULL;
|
||||
patcherParams.vertexUsseFreeCallback = NULL;
|
||||
patcherParams.vertexUsseMem = this->patcherVertexUsse;
|
||||
patcherParams.vertexUsseMemSize = patcherVertexUsseSize;
|
||||
patcherParams.vertexUsseOffset = this->patcherVertexUsseOffset;
|
||||
patcherParams.fragmentUsseAllocCallback = NULL;
|
||||
patcherParams.fragmentUsseFreeCallback = NULL;
|
||||
patcherParams.fragmentUsseMem = this->patcherFragmentUsse;
|
||||
patcherParams.fragmentUsseMemSize = patcherFragmentUsseSize;
|
||||
patcherParams.fragmentUsseOffset = this->patcherFragmentUsseOffset;
|
||||
|
||||
ret = SCE_ERR(sceGxmShaderPatcherCreate, &patcherParams, &this->shaderPatcher);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GXMContext::create_display_buffers(SceGxmMultisampleMode msaaMode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
const uint32_t alignedWidth = ALIGN(VITA_GXM_SCREEN_WIDTH, SCE_GXM_TILE_SIZEX);
|
||||
const uint32_t alignedHeight = ALIGN(VITA_GXM_SCREEN_HEIGHT, SCE_GXM_TILE_SIZEY);
|
||||
uint32_t sampleCount = alignedWidth * alignedHeight;
|
||||
uint32_t depthStrideInSamples = alignedWidth;
|
||||
|
||||
if (msaaMode == SCE_GXM_MULTISAMPLE_4X) {
|
||||
sampleCount *= 4;
|
||||
depthStrideInSamples *= 2;
|
||||
}
|
||||
else if (msaaMode == SCE_GXM_MULTISAMPLE_2X) {
|
||||
sampleCount *= 2;
|
||||
}
|
||||
|
||||
// render target
|
||||
SceGxmRenderTargetParams renderTargetParams;
|
||||
memset(&renderTargetParams, 0, sizeof(SceGxmRenderTargetParams));
|
||||
renderTargetParams.flags = 0;
|
||||
renderTargetParams.width = VITA_GXM_SCREEN_WIDTH;
|
||||
renderTargetParams.height = VITA_GXM_SCREEN_HEIGHT;
|
||||
renderTargetParams.scenesPerFrame = 1;
|
||||
renderTargetParams.multisampleMode = msaaMode;
|
||||
renderTargetParams.multisampleLocations = 0;
|
||||
renderTargetParams.driverMemBlock = -1; // Invalid UID
|
||||
ret = SCE_ERR(sceGxmCreateRenderTarget, &renderTargetParams, &this->renderTarget);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
this->renderTargetInit = true;
|
||||
|
||||
for (int i = 0; i < GXM_DISPLAY_BUFFER_COUNT; i++) {
|
||||
this->displayBuffers[i] = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
|
||||
4 * VITA_GXM_SCREEN_STRIDE * VITA_GXM_SCREEN_HEIGHT,
|
||||
SCE_GXM_COLOR_SURFACE_ALIGNMENT,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
||||
&this->displayBuffersUid[i],
|
||||
"displayBuffers"
|
||||
);
|
||||
|
||||
ret = SCE_ERR(
|
||||
sceGxmColorSurfaceInit,
|
||||
&this->displayBuffersSurface[i],
|
||||
SCE_GXM_COLOR_FORMAT_A8B8G8R8,
|
||||
SCE_GXM_COLOR_SURFACE_LINEAR,
|
||||
(msaaMode == SCE_GXM_MULTISAMPLE_NONE) ? SCE_GXM_COLOR_SURFACE_SCALE_NONE
|
||||
: SCE_GXM_COLOR_SURFACE_SCALE_MSAA_DOWNSCALE,
|
||||
SCE_GXM_OUTPUT_REGISTER_SIZE_32BIT,
|
||||
VITA_GXM_SCREEN_WIDTH,
|
||||
VITA_GXM_SCREEN_HEIGHT,
|
||||
VITA_GXM_SCREEN_STRIDE,
|
||||
this->displayBuffers[i]
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = SCE_ERR(sceGxmSyncObjectCreate, &this->displayBuffersSync[i]);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// depth & stencil
|
||||
this->depthBufferData = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
||||
4 * sampleCount,
|
||||
SCE_GXM_DEPTHSTENCIL_SURFACE_ALIGNMENT,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
||||
&this->depthBufferUid,
|
||||
"depthBufferData"
|
||||
);
|
||||
|
||||
this->stencilBufferData = vita_mem_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
||||
1 * sampleCount,
|
||||
SCE_GXM_DEPTHSTENCIL_SURFACE_ALIGNMENT,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
||||
&this->stencilBufferUid,
|
||||
"stencilBufferData"
|
||||
);
|
||||
|
||||
ret = SCE_ERR(
|
||||
sceGxmDepthStencilSurfaceInit,
|
||||
&this->depthSurface,
|
||||
SCE_GXM_DEPTH_STENCIL_FORMAT_DF32_S8,
|
||||
SCE_GXM_DEPTH_STENCIL_SURFACE_TILED,
|
||||
depthStrideInSamples,
|
||||
this->depthBufferData,
|
||||
this->stencilBufferData
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GXMContext::destroy_display_buffers()
|
||||
{
|
||||
if (this->renderTargetInit) {
|
||||
sceGxmFinish(this->context);
|
||||
sceGxmDestroyRenderTarget(this->renderTarget);
|
||||
this->renderTargetInit = false;
|
||||
}
|
||||
for (int i = 0; i < GXM_DISPLAY_BUFFER_COUNT; i++) {
|
||||
if (this->displayBuffers[i]) {
|
||||
vita_mem_free(this->displayBuffersUid[i]);
|
||||
this->displayBuffers[i] = nullptr;
|
||||
this->displayBuffersUid[i] = -1;
|
||||
sceGxmSyncObjectDestroy(this->displayBuffersSync[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->depthBufferData) {
|
||||
vita_mem_free(this->depthBufferUid);
|
||||
this->depthBufferData = nullptr;
|
||||
this->depthBufferUid = -1;
|
||||
}
|
||||
|
||||
if (this->stencilBufferData) {
|
||||
vita_mem_free(this->stencilBufferUid);
|
||||
this->stencilBufferData = nullptr;
|
||||
this->stencilBufferUid = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void GXMContext::init_clear_mesh()
|
||||
{
|
||||
this->clearVertices = static_cast<GXMVertex2D*>(this->alloc(sizeof(GXMVertex2D) * 4, 4));
|
||||
this->clearVertices[0] = {.position = {-1.0, 1.0}, .texCoord = {0, 0}};
|
||||
this->clearVertices[1] = {.position = {1.0, 1.0}, .texCoord = {0, 0}};
|
||||
this->clearVertices[2] = {.position = {-1.0, -1.0}, .texCoord = {0, 0}};
|
||||
this->clearVertices[3] = {.position = {1.0, -1.0}, .texCoord = {0, 0}};
|
||||
|
||||
this->clearIndices = static_cast<uint16_t*>(this->alloc(sizeof(uint16_t) * 4, 4));
|
||||
this->clearIndices[0] = 0;
|
||||
this->clearIndices[1] = 1;
|
||||
this->clearIndices[2] = 2;
|
||||
this->clearIndices[3] = 3;
|
||||
}
|
||||
|
||||
int GXMContext::register_base_shaders()
|
||||
{
|
||||
int ret;
|
||||
// register plane, color, image shaders
|
||||
ret = SCE_ERR(
|
||||
sceGxmShaderPatcherRegisterProgram,
|
||||
this->shaderPatcher,
|
||||
planeVertexProgramGxp,
|
||||
&this->planeVertexProgramId
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = SCE_ERR(
|
||||
sceGxmShaderPatcherRegisterProgram,
|
||||
this->shaderPatcher,
|
||||
colorFragmentProgramGxp,
|
||||
&this->colorFragmentProgramId
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = SCE_ERR(
|
||||
sceGxmShaderPatcherRegisterProgram,
|
||||
this->shaderPatcher,
|
||||
imageFragmentProgramGxp,
|
||||
&this->imageFragmentProgramId
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
this->color_uColor = sceGxmProgramFindParameterByName(colorFragmentProgramGxp, "uColor"); // vec4
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GXMContext::patch_base_shaders(SceGxmMultisampleMode msaaMode)
|
||||
{
|
||||
int ret;
|
||||
{
|
||||
GET_SHADER_PARAM(positionAttribute, planeVertexProgramGxp, "aPosition", -1);
|
||||
GET_SHADER_PARAM(texCoordAttribute, planeVertexProgramGxp, "aTexCoord", -1);
|
||||
|
||||
SceGxmVertexAttribute vertexAttributes[2];
|
||||
SceGxmVertexStream vertexStreams[1];
|
||||
|
||||
// position
|
||||
vertexAttributes[0].streamIndex = 0;
|
||||
vertexAttributes[0].offset = 0;
|
||||
vertexAttributes[0].format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
|
||||
vertexAttributes[0].componentCount = 2;
|
||||
vertexAttributes[0].regIndex = sceGxmProgramParameterGetResourceIndex(positionAttribute);
|
||||
|
||||
// uv
|
||||
vertexAttributes[1].streamIndex = 0;
|
||||
vertexAttributes[1].offset = 8;
|
||||
vertexAttributes[1].format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
|
||||
vertexAttributes[1].componentCount = 2;
|
||||
vertexAttributes[1].regIndex = sceGxmProgramParameterGetResourceIndex(texCoordAttribute);
|
||||
|
||||
vertexStreams[0].stride = sizeof(GXMVertex2D);
|
||||
vertexStreams[0].indexSource = SCE_GXM_INDEX_SOURCE_INDEX_16BIT;
|
||||
|
||||
ret = SCE_ERR(
|
||||
sceGxmShaderPatcherCreateVertexProgram,
|
||||
this->shaderPatcher,
|
||||
this->planeVertexProgramId,
|
||||
vertexAttributes,
|
||||
2,
|
||||
vertexStreams,
|
||||
1,
|
||||
&this->planeVertexProgram
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = SCE_ERR(
|
||||
sceGxmShaderPatcherCreateFragmentProgram,
|
||||
this->shaderPatcher,
|
||||
this->colorFragmentProgramId,
|
||||
SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
|
||||
msaaMode,
|
||||
NULL,
|
||||
planeVertexProgramGxp,
|
||||
&this->colorFragmentProgram
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = SCE_ERR(
|
||||
sceGxmShaderPatcherCreateFragmentProgram,
|
||||
this->shaderPatcher,
|
||||
this->imageFragmentProgramId,
|
||||
SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
|
||||
msaaMode,
|
||||
&blendInfoTransparent,
|
||||
planeVertexProgramGxp,
|
||||
&this->imageFragmentProgram
|
||||
);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GXMContext::destroy_base_shaders()
|
||||
{
|
||||
sceGxmShaderPatcherReleaseVertexProgram(this->shaderPatcher, this->planeVertexProgram);
|
||||
sceGxmShaderPatcherReleaseFragmentProgram(this->shaderPatcher, this->colorFragmentProgram);
|
||||
sceGxmShaderPatcherReleaseFragmentProgram(this->shaderPatcher, this->imageFragmentProgram);
|
||||
}
|
||||
|
||||
int GXMContext::init(SceGxmMultisampleMode msaaMode)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = gxm_library_init();
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (this->cdramPool == nullptr) {
|
||||
this->init_cdram_allocator();
|
||||
}
|
||||
|
||||
if (this->context == nullptr) {
|
||||
ret = this->init_context();
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->planeVertexProgramId == 0) {
|
||||
ret = this->register_base_shaders();
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->clearVertices == nullptr) {
|
||||
this->init_clear_mesh();
|
||||
}
|
||||
|
||||
// recreate when msaa is different
|
||||
if (msaaMode != this->displayMsaa && this->renderTargetInit) {
|
||||
this->destroy_display_buffers();
|
||||
this->destroy_base_shaders();
|
||||
}
|
||||
|
||||
if (!this->renderTargetInit) {
|
||||
ret = this->create_display_buffers(msaaMode);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
ret = this->patch_base_shaders(msaaMode);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int inuse_mem = 0;
|
||||
void* GXMContext::alloc(size_t size, size_t align)
|
||||
{
|
||||
if (this->cdramPool == nullptr) {
|
||||
this->init_cdram_allocator();
|
||||
}
|
||||
DEBUG_ONLY_PRINTF("cdram_alloc(%d, %d) inuse=%d ", size, align, inuse_mem);
|
||||
void* ptr = tlsf_memalign(this->cdramPool, align, size);
|
||||
DEBUG_ONLY_PRINTF("ptr=%p\n", ptr);
|
||||
inuse_mem += tlsf_block_size(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void GXMContext::free(void* ptr)
|
||||
{
|
||||
inuse_mem -= tlsf_block_size(ptr);
|
||||
DEBUG_ONLY_PRINTF("cdram_free(%p)\n", ptr);
|
||||
tlsf_free(this->cdramPool, ptr);
|
||||
}
|
||||
|
||||
void GXMContext::clear(float r, float g, float b, bool new_scene)
|
||||
{
|
||||
new_scene = new_scene && !this->sceneStarted;
|
||||
if (new_scene) {
|
||||
sceGxmBeginScene(
|
||||
this->context,
|
||||
0,
|
||||
this->renderTarget,
|
||||
nullptr,
|
||||
nullptr,
|
||||
this->displayBuffersSync[this->backBufferIndex],
|
||||
&this->displayBuffersSurface[this->backBufferIndex],
|
||||
&this->depthSurface
|
||||
);
|
||||
this->sceneStarted = true;
|
||||
}
|
||||
|
||||
float color[] = {r, g, b, 1};
|
||||
|
||||
sceGxmSetVertexProgram(this->context, this->planeVertexProgram);
|
||||
sceGxmSetFragmentProgram(this->context, this->colorFragmentProgram);
|
||||
|
||||
void* vertUniforms;
|
||||
void* fragUniforms;
|
||||
sceGxmReserveVertexDefaultUniformBuffer(gxm->context, &vertUniforms);
|
||||
sceGxmReserveFragmentDefaultUniformBuffer(gxm->context, &fragUniforms);
|
||||
|
||||
sceGxmSetVertexStream(gxm->context, 0, this->clearVertices);
|
||||
sceGxmSetUniformDataF(fragUniforms, this->color_uColor, 0, 4, color);
|
||||
|
||||
sceGxmSetFrontDepthFunc(gxm->context, SCE_GXM_DEPTH_FUNC_ALWAYS);
|
||||
sceGxmDraw(gxm->context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, SCE_GXM_INDEX_FORMAT_U16, this->clearIndices, 4);
|
||||
if (new_scene) {
|
||||
sceGxmEndScene(this->context, nullptr, nullptr);
|
||||
this->sceneStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
void GXMContext::copy_frontbuffer()
|
||||
{
|
||||
SceGxmTexture texture;
|
||||
sceGxmTextureInitLinearStrided(
|
||||
&texture,
|
||||
this->displayBuffers[this->frontBufferIndex],
|
||||
SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_ABGR,
|
||||
VITA_GXM_SCREEN_WIDTH,
|
||||
VITA_GXM_SCREEN_HEIGHT,
|
||||
VITA_GXM_SCREEN_STRIDE * 4
|
||||
);
|
||||
sceGxmSetVertexProgram(this->context, this->planeVertexProgram);
|
||||
sceGxmSetFragmentProgram(this->context, this->imageFragmentProgram);
|
||||
|
||||
void* vertUniforms;
|
||||
void* fragUniforms;
|
||||
sceGxmReserveVertexDefaultUniformBuffer(this->context, &vertUniforms);
|
||||
sceGxmReserveFragmentDefaultUniformBuffer(this->context, &fragUniforms);
|
||||
|
||||
sceGxmSetVertexStream(this->context, 0, this->clearVertices);
|
||||
sceGxmSetFragmentTexture(this->context, 0, &texture);
|
||||
|
||||
sceGxmSetFrontDepthFunc(this->context, SCE_GXM_DEPTH_FUNC_ALWAYS);
|
||||
sceGxmDraw(this->context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, SCE_GXM_INDEX_FORMAT_U16, this->clearIndices, 4);
|
||||
}
|
||||
|
||||
void GXMContext::destroy()
|
||||
{
|
||||
sceGxmDisplayQueueFinish();
|
||||
if (gxm->context) {
|
||||
sceGxmFinish(gxm->context);
|
||||
}
|
||||
|
||||
this->destroy_display_buffers();
|
||||
this->destroy_base_shaders();
|
||||
|
||||
sceGxmShaderPatcherDestroy(this->shaderPatcher);
|
||||
sceGxmDestroyContext(this->context);
|
||||
vita_mem_fragment_usse_free(this->fragmentUsseRingBufferUid);
|
||||
vita_mem_free(this->fragmentRingBufferUid);
|
||||
vita_mem_free(this->vertexRingBufferUid);
|
||||
vita_mem_free(this->vdmRingBufferUid);
|
||||
SDL_free(this->contextHostMem);
|
||||
}
|
||||
|
||||
void GXMContext::swap_display()
|
||||
{
|
||||
if (this->sceneStarted) {
|
||||
sceGxmEndScene(gxm->context, nullptr, nullptr);
|
||||
this->sceneStarted = false;
|
||||
}
|
||||
|
||||
SceCommonDialogUpdateParam updateParam;
|
||||
SDL_zero(updateParam);
|
||||
updateParam.renderTarget.colorFormat = VITA_GXM_COLOR_FORMAT;
|
||||
updateParam.renderTarget.surfaceType = SCE_GXM_COLOR_SURFACE_LINEAR;
|
||||
updateParam.renderTarget.width = VITA_GXM_SCREEN_WIDTH;
|
||||
updateParam.renderTarget.height = VITA_GXM_SCREEN_HEIGHT;
|
||||
updateParam.renderTarget.strideInPixels = VITA_GXM_SCREEN_STRIDE;
|
||||
updateParam.renderTarget.colorSurfaceData = this->displayBuffers[this->backBufferIndex];
|
||||
updateParam.displaySyncObject = this->displayBuffersSync[this->backBufferIndex];
|
||||
sceCommonDialogUpdate(&updateParam);
|
||||
|
||||
sceGxmPadHeartbeat(
|
||||
&this->displayBuffersSurface[this->backBufferIndex],
|
||||
this->displayBuffersSync[this->backBufferIndex]
|
||||
);
|
||||
|
||||
// display
|
||||
GXMDisplayData displayData;
|
||||
displayData.address = this->displayBuffers[this->backBufferIndex];
|
||||
displayData.index = this->backBufferIndex;
|
||||
sceGxmDisplayQueueAddEntry(
|
||||
this->displayBuffersSync[this->frontBufferIndex],
|
||||
this->displayBuffersSync[this->backBufferIndex],
|
||||
&displayData
|
||||
);
|
||||
|
||||
this->frontBufferIndex = this->backBufferIndex;
|
||||
this->backBufferIndex = (this->backBufferIndex + 1) % GXM_DISPLAY_BUFFER_COUNT;
|
||||
}
|
||||
|
||||
Direct3DRMRenderer* GXMRenderer::Create(DWORD width, DWORD height, DWORD msaaSamples)
|
||||
{
|
||||
int ret = gxm_library_init();
|
||||
if (ret < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return new GXMRenderer(width, height, msaaSamples);
|
||||
}
|
||||
|
||||
GXMRenderer::GXMRenderer(DWORD width, DWORD height, DWORD msaaSamples)
|
||||
{
|
||||
m_width = VITA_GXM_SCREEN_WIDTH;
|
||||
m_height = VITA_GXM_SCREEN_HEIGHT;
|
||||
m_virtualWidth = width;
|
||||
m_virtualHeight = height;
|
||||
|
||||
SceGxmMultisampleMode msaaMode = SCE_GXM_MULTISAMPLE_NONE;
|
||||
if (msaaSamples == 2) {
|
||||
@ -779,18 +44,27 @@ GXMRenderer::GXMRenderer(DWORD width, DWORD height, DWORD msaaSamples)
|
||||
msaaMode = SCE_GXM_MULTISAMPLE_4X;
|
||||
}
|
||||
|
||||
int ret;
|
||||
if (!gxm) {
|
||||
gxm = (GXMContext*) SDL_malloc(sizeof(GXMContext));
|
||||
memset(gxm, 0, sizeof(GXMContext));
|
||||
}
|
||||
ret = SCE_ERR(gxm->init, msaaMode);
|
||||
if (ret < 0) {
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new GXMRenderer(width, height, msaaMode);
|
||||
}
|
||||
|
||||
GXMRenderer::GXMRenderer(DWORD width, DWORD height, SceGxmMultisampleMode msaaMode)
|
||||
{
|
||||
m_width = VITA_GXM_SCREEN_WIDTH;
|
||||
m_height = VITA_GXM_SCREEN_HEIGHT;
|
||||
m_virtualWidth = width;
|
||||
m_virtualHeight = height;
|
||||
|
||||
// register shader programs
|
||||
ret = SCE_ERR(
|
||||
int ret = SCE_ERR(
|
||||
sceGxmShaderPatcherRegisterProgram,
|
||||
gxm->shaderPatcher,
|
||||
mainVertexProgramGxp,
|
||||
@ -1516,6 +790,8 @@ void GXMRenderer::StartScene()
|
||||
return;
|
||||
}
|
||||
|
||||
this->DeferredDelete(gxm->frontBufferIndex);
|
||||
|
||||
#ifdef GXM_WITH_RAZOR
|
||||
bool dpad_up_clicked = !this->last_dpad_up && g_dpadUp;
|
||||
bool dpad_down_clicked = !this->last_dpad_down && g_dpadDown;
|
||||
@ -1,415 +0,0 @@
|
||||
/**
|
||||
* @file incbin.h
|
||||
* @author Dale Weiler
|
||||
* @brief Utility for including binary files
|
||||
*
|
||||
* Facilities for including binary files into the current translation unit and
|
||||
* making use from them externally in other translation units.
|
||||
*/
|
||||
#ifndef INCBIN_HDR
|
||||
#define INCBIN_HDR
|
||||
#include <limits.h>
|
||||
#if defined(__AVX512BW__) || defined(__AVX512CD__) || defined(__AVX512DQ__) || defined(__AVX512ER__) || \
|
||||
defined(__AVX512PF__) || defined(__AVX512VL__) || defined(__AVX512F__)
|
||||
#define INCBIN_ALIGNMENT_INDEX 6
|
||||
#elif defined(__AVX__) || defined(__AVX2__)
|
||||
#define INCBIN_ALIGNMENT_INDEX 5
|
||||
#elif defined(__SSE__) || defined(__SSE2__) || defined(__SSE3__) || defined(__SSSE3__) || defined(__SSE4_1__) || \
|
||||
defined(__SSE4_2__) || defined(__neon__) || defined(__ARM_NEON) || defined(__ALTIVEC__)
|
||||
#define INCBIN_ALIGNMENT_INDEX 4
|
||||
#elif ULONG_MAX != 0xffffffffu
|
||||
#define INCBIN_ALIGNMENT_INDEX 3
|
||||
#else
|
||||
#define INCBIN_ALIGNMENT_INDEX 2
|
||||
#endif
|
||||
|
||||
/* Lookup table of (1 << n) where `n' is `INCBIN_ALIGNMENT_INDEX' */
|
||||
#define INCBIN_ALIGN_SHIFT_0 1
|
||||
#define INCBIN_ALIGN_SHIFT_1 2
|
||||
#define INCBIN_ALIGN_SHIFT_2 4
|
||||
#define INCBIN_ALIGN_SHIFT_3 8
|
||||
#define INCBIN_ALIGN_SHIFT_4 16
|
||||
#define INCBIN_ALIGN_SHIFT_5 32
|
||||
#define INCBIN_ALIGN_SHIFT_6 64
|
||||
|
||||
/* Actual alignment value */
|
||||
#define INCBIN_ALIGNMENT INCBIN_CONCATENATE(INCBIN_CONCATENATE(INCBIN_ALIGN_SHIFT, _), INCBIN_ALIGNMENT_INDEX)
|
||||
|
||||
/* Stringize */
|
||||
#define INCBIN_STR(X) #X
|
||||
#define INCBIN_STRINGIZE(X) INCBIN_STR(X)
|
||||
/* Concatenate */
|
||||
#define INCBIN_CAT(X, Y) X##Y
|
||||
#define INCBIN_CONCATENATE(X, Y) INCBIN_CAT(X, Y)
|
||||
/* Deferred macro expansion */
|
||||
#define INCBIN_EVAL(X) X
|
||||
#define INCBIN_INVOKE(N, ...) INCBIN_EVAL(N(__VA_ARGS__))
|
||||
/* Variable argument count for overloading by arity */
|
||||
#define INCBIN_VA_ARG_COUNTER(_1, _2, _3, N, ...) N
|
||||
#define INCBIN_VA_ARGC(...) INCBIN_VA_ARG_COUNTER(__VA_ARGS__, 3, 2, 1, 0)
|
||||
|
||||
/* Green Hills uses a different directive for including binary data */
|
||||
#if defined(__ghs__)
|
||||
#if (__ghs_asm == 2)
|
||||
#define INCBIN_MACRO ".file"
|
||||
/* Or consider the ".myrawdata" entry in the ld file */
|
||||
#else
|
||||
#define INCBIN_MACRO "\tINCBIN"
|
||||
#endif
|
||||
#else
|
||||
#define INCBIN_MACRO ".incbin"
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#define INCBIN_ALIGN __attribute__((aligned(INCBIN_ALIGNMENT)))
|
||||
#else
|
||||
#define INCBIN_ALIGN __declspec(align(INCBIN_ALIGNMENT))
|
||||
#endif
|
||||
|
||||
#if defined(__arm__) || /* GNU C and RealView */ \
|
||||
defined(__arm) || /* Diab */ \
|
||||
defined(_ARM) /* ImageCraft */
|
||||
#define INCBIN_ARM
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* Utilize .balign where supported */
|
||||
#define INCBIN_ALIGN_HOST ".balign " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
|
||||
#define INCBIN_ALIGN_BYTE ".balign 1\n"
|
||||
#elif defined(INCBIN_ARM)
|
||||
/*
|
||||
* On arm assemblers, the alignment value is calculated as (1 << n) where `n' is
|
||||
* the shift count. This is the value passed to `.align'
|
||||
*/
|
||||
#define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT_INDEX) "\n"
|
||||
#define INCBIN_ALIGN_BYTE ".align 0\n"
|
||||
#else
|
||||
/* We assume other inline assembler's treat `.align' as `.balign' */
|
||||
#define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
|
||||
#define INCBIN_ALIGN_BYTE ".align 1\n"
|
||||
#endif
|
||||
|
||||
/* INCBIN_CONST is used by incbin.c generated files */
|
||||
#if defined(__cplusplus)
|
||||
#define INCBIN_EXTERNAL extern "C"
|
||||
#define INCBIN_CONST extern const
|
||||
#else
|
||||
#define INCBIN_EXTERNAL extern
|
||||
#define INCBIN_CONST const
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Optionally override the linker section into which size and data is
|
||||
* emitted.
|
||||
*
|
||||
* @warning If you use this facility, you might have to deal with
|
||||
* platform-specific linker output section naming on your own.
|
||||
*/
|
||||
#if !defined(INCBIN_OUTPUT_SECTION)
|
||||
#if defined(__APPLE__)
|
||||
#define INCBIN_OUTPUT_SECTION ".const_data"
|
||||
#else
|
||||
#define INCBIN_OUTPUT_SECTION ".rodata"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Optionally override the linker section into which data is emitted.
|
||||
*
|
||||
* @warning If you use this facility, you might have to deal with
|
||||
* platform-specific linker output section naming on your own.
|
||||
*/
|
||||
#if !defined(INCBIN_OUTPUT_DATA_SECTION)
|
||||
#define INCBIN_OUTPUT_DATA_SECTION INCBIN_OUTPUT_SECTION
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Optionally override the linker section into which size is emitted.
|
||||
*
|
||||
* @warning If you use this facility, you might have to deal with
|
||||
* platform-specific linker output section naming on your own.
|
||||
*
|
||||
* @note This is useful for Harvard architectures where program memory cannot
|
||||
* be directly read from the program without special instructions. With this you
|
||||
* can chose to put the size variable in RAM rather than ROM.
|
||||
*/
|
||||
#if !defined(INCBIN_OUTPUT_SIZE_SECTION)
|
||||
#define INCBIN_OUTPUT_SIZE_SECTION INCBIN_OUTPUT_SECTION
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#if defined(TARGET_OS_IPHONE) && !defined(INCBIN_SILENCE_BITCODE_WARNING)
|
||||
#warning \
|
||||
"incbin is incompatible with bitcode. Using the library will break upload to App Store if you have bitcode enabled. Add `#define INCBIN_SILENCE_BITCODE_WARNING` before including this header to silence this warning."
|
||||
#endif
|
||||
/* The directives are different for Apple branded compilers */
|
||||
#define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n"
|
||||
#define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
|
||||
#define INCBIN_INT ".long "
|
||||
#define INCBIN_MANGLE "_"
|
||||
#define INCBIN_BYTE ".byte "
|
||||
#define INCBIN_TYPE(...)
|
||||
#else
|
||||
#define INCBIN_SECTION ".section " INCBIN_OUTPUT_SECTION "\n"
|
||||
#define INCBIN_GLOBAL(NAME) ".global " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
|
||||
#if defined(__ghs__)
|
||||
#define INCBIN_INT ".word "
|
||||
#else
|
||||
#define INCBIN_INT ".int "
|
||||
#endif
|
||||
#if defined(__USER_LABEL_PREFIX__)
|
||||
#define INCBIN_MANGLE INCBIN_STRINGIZE(__USER_LABEL_PREFIX__)
|
||||
#else
|
||||
#define INCBIN_MANGLE ""
|
||||
#endif
|
||||
#if defined(INCBIN_ARM)
|
||||
/* On arm assemblers, `@' is used as a line comment token */
|
||||
#define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", %object\n"
|
||||
#elif defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__)
|
||||
/* Mingw and Cygwin don't support this directive either */
|
||||
#define INCBIN_TYPE(NAME)
|
||||
#else
|
||||
/* It's safe to use `@' on other architectures */
|
||||
#define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", @object\n"
|
||||
#endif
|
||||
#define INCBIN_BYTE ".byte "
|
||||
#endif
|
||||
|
||||
/* List of style types used for symbol names */
|
||||
#define INCBIN_STYLE_CAMEL 0
|
||||
#define INCBIN_STYLE_SNAKE 1
|
||||
|
||||
/**
|
||||
* @brief Specify the prefix to use for symbol names.
|
||||
*
|
||||
* @note By default this is "g".
|
||||
*
|
||||
* @code
|
||||
* #define INCBIN_PREFIX incbin
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols instead:
|
||||
* // const unsigned char incbinFoo<data>[];
|
||||
* // const unsigned char *const incbinFoo<end>;
|
||||
* // const unsigned int incbinFoo<size>;
|
||||
* @endcode
|
||||
*/
|
||||
#if !defined(INCBIN_PREFIX)
|
||||
#define INCBIN_PREFIX g
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Specify the style used for symbol names.
|
||||
*
|
||||
* Possible options are
|
||||
* - INCBIN_STYLE_CAMEL "CamelCase"
|
||||
* - INCBIN_STYLE_SNAKE "snake_case"
|
||||
*
|
||||
* @note By default this is INCBIN_STYLE_CAMEL
|
||||
*
|
||||
* @code
|
||||
* #define INCBIN_STYLE INCBIN_STYLE_SNAKE
|
||||
* #include "incbin.h"
|
||||
* INCBIN(foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>foo_data[];
|
||||
* // const unsigned char *const <prefix>foo_end;
|
||||
* // const unsigned int <prefix>foo_size;
|
||||
* @endcode
|
||||
*/
|
||||
#if !defined(INCBIN_STYLE)
|
||||
#define INCBIN_STYLE INCBIN_STYLE_CAMEL
|
||||
#endif
|
||||
|
||||
/* Style lookup tables */
|
||||
#define INCBIN_STYLE_0_DATA Data
|
||||
#define INCBIN_STYLE_0_END End
|
||||
#define INCBIN_STYLE_0_SIZE Size
|
||||
#define INCBIN_STYLE_1_DATA _data
|
||||
#define INCBIN_STYLE_1_END _end
|
||||
#define INCBIN_STYLE_1_SIZE _size
|
||||
|
||||
/* Style lookup: returning identifier */
|
||||
#define INCBIN_STYLE_IDENT(TYPE) \
|
||||
INCBIN_CONCATENATE(INCBIN_STYLE_, INCBIN_CONCATENATE(INCBIN_EVAL(INCBIN_STYLE), INCBIN_CONCATENATE(_, TYPE)))
|
||||
|
||||
/* Style lookup: returning string literal */
|
||||
#define INCBIN_STYLE_STRING(TYPE) INCBIN_STRINGIZE(INCBIN_STYLE_IDENT(TYPE))
|
||||
|
||||
/* Generate the global labels by indirectly invoking the macro with our style
|
||||
* type and concatenating the name against them. */
|
||||
#define INCBIN_GLOBAL_LABELS(NAME, TYPE) \
|
||||
INCBIN_INVOKE(INCBIN_GLOBAL, INCBIN_CONCATENATE(NAME, INCBIN_INVOKE(INCBIN_STYLE_IDENT, TYPE))) \
|
||||
INCBIN_INVOKE(INCBIN_TYPE, INCBIN_CONCATENATE(NAME, INCBIN_INVOKE(INCBIN_STYLE_IDENT, TYPE)))
|
||||
|
||||
/**
|
||||
* @brief Externally reference binary data included in another translation unit.
|
||||
*
|
||||
* Produces three external symbols that reference the binary data included in
|
||||
* another translation unit.
|
||||
*
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param TYPE Optional array type. Omitting this picks a default of `unsigned char`.
|
||||
* @param NAME The name given for the binary data
|
||||
*
|
||||
* @code
|
||||
* INCBIN_EXTERN(Foo);
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // extern const unsigned char <prefix>Foo<data>[];
|
||||
* // extern const unsigned char *const <prefix>Foo<end>;
|
||||
* // extern const unsigned int <prefix>Foo<size>;
|
||||
* @endcode
|
||||
*
|
||||
* You may specify a custom optional data type as well as the first argument.
|
||||
* @code
|
||||
* INCBIN_EXTERN(custom_type, Foo);
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // extern const custom_type <prefix>Foo<data>[];
|
||||
* // extern const custom_type *const <prefix>Foo<end>;
|
||||
* // extern const unsigned int <prefix>Foo<size>;
|
||||
* @endcode
|
||||
*/
|
||||
#define INCBIN_EXTERN(...) INCBIN_CONCATENATE(INCBIN_EXTERN_, INCBIN_VA_ARGC(__VA_ARGS__))(__VA_ARGS__)
|
||||
#define INCBIN_EXTERN_1(NAME, ...) INCBIN_EXTERN_2(unsigned char, NAME)
|
||||
#define INCBIN_EXTERN_2(TYPE, NAME) \
|
||||
INCBIN_EXTERNAL const INCBIN_ALIGN TYPE \
|
||||
INCBIN_CONCATENATE(INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), INCBIN_STYLE_IDENT(DATA))[]; \
|
||||
INCBIN_EXTERNAL const INCBIN_ALIGN \
|
||||
TYPE* const INCBIN_CONCATENATE(INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), INCBIN_STYLE_IDENT(END)); \
|
||||
INCBIN_EXTERNAL const unsigned int INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(SIZE) \
|
||||
)
|
||||
|
||||
/**
|
||||
* @brief Externally reference textual data included in another translation unit.
|
||||
*
|
||||
* Produces three external symbols that reference the textual data included in
|
||||
* another translation unit.
|
||||
*
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param NAME The name given for the textual data
|
||||
*
|
||||
* @code
|
||||
* INCBIN_EXTERN(Foo);
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // extern const char <prefix>Foo<data>[];
|
||||
* // extern const char *const <prefix>Foo<end>;
|
||||
* // extern const unsigned int <prefix>Foo<size>;
|
||||
* @endcode
|
||||
*/
|
||||
#define INCTXT_EXTERN(NAME) INCBIN_EXTERN_2(char, NAME)
|
||||
|
||||
/**
|
||||
* @brief Include a binary file into the current translation unit.
|
||||
*
|
||||
* Includes a binary file into the current translation unit, producing three symbols
|
||||
* for objects that encode the data and size respectively.
|
||||
*
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param TYPE Optional array type. Omitting this picks a default of `unsigned char`.
|
||||
* @param NAME The name to associate with this binary data (as an identifier.)
|
||||
* @param FILENAME The file to include (as a string literal.)
|
||||
*
|
||||
* @code
|
||||
* INCBIN(Icon, "icon.png");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>Icon<data>[];
|
||||
* // const unsigned char *const <prefix>Icon<end>;
|
||||
* // const unsigned int <prefix>Icon<size>;
|
||||
* @endcode
|
||||
*
|
||||
* You may specify a custom optional data type as well as the first argument.
|
||||
* These macros are specialized by arity.
|
||||
* @code
|
||||
* INCBIN(custom_type, Icon, "icon.png");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const custom_type <prefix>Icon<data>[];
|
||||
* // const custom_type *const <prefix>Icon<end>;
|
||||
* // const unsigned int <prefix>Icon<size>;
|
||||
* @endcode
|
||||
*
|
||||
* @warning This must be used in global scope
|
||||
* @warning The identifiers may be different if INCBIN_STYLE is not default
|
||||
*
|
||||
* To externally reference the data included by this in another translation unit
|
||||
* please @see INCBIN_EXTERN.
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
#define INCBIN(NAME, FILENAME) INCBIN_EXTERN(NAME)
|
||||
#else
|
||||
#define INCBIN(...) INCBIN_CONCATENATE(INCBIN_, INCBIN_VA_ARGC(__VA_ARGS__))(__VA_ARGS__)
|
||||
#if defined(__GNUC__)
|
||||
#define INCBIN_1(...) _Pragma("GCC error \"Single argument INCBIN not allowed\"")
|
||||
#elif defined(__clang__)
|
||||
#define INCBIN_1(...) _Pragma("clang error \"Single argument INCBIN not allowed\"")
|
||||
#else
|
||||
#define INCBIN_1(...) /* Cannot do anything here */
|
||||
#endif
|
||||
#define INCBIN_2(NAME, FILENAME) INCBIN_3(unsigned char, NAME, FILENAME)
|
||||
#define INCBIN_3(TYPE, NAME, FILENAME) INCBIN_COMMON(TYPE, NAME, FILENAME, /* No terminator for binary data */)
|
||||
#define INCBIN_COMMON(TYPE, NAME, FILENAME, TERMINATOR) \
|
||||
__asm__(INCBIN_SECTION INCBIN_GLOBAL_LABELS(NAME, DATA) \
|
||||
INCBIN_ALIGN_HOST INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA \
|
||||
) ":\n" INCBIN_MACRO " \"" FILENAME "\"\n" TERMINATOR INCBIN_GLOBAL_LABELS(NAME, END) \
|
||||
INCBIN_ALIGN_BYTE INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END \
|
||||
) ":\n" INCBIN_BYTE "1\n" INCBIN_GLOBAL_LABELS(NAME, SIZE) \
|
||||
INCBIN_ALIGN_HOST INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(SIZE \
|
||||
) ":\n" INCBIN_INT INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END \
|
||||
) " - " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA \
|
||||
) "\n" INCBIN_ALIGN_HOST ".text\n"); \
|
||||
INCBIN_EXTERN(TYPE, NAME)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Include a textual file into the current translation unit.
|
||||
*
|
||||
* This behaves the same as INCBIN except it produces char compatible arrays
|
||||
* and implicitly adds a null-terminator byte, thus the size of data included
|
||||
* by this is one byte larger than that of INCBIN.
|
||||
*
|
||||
* Includes a textual file into the current translation unit, producing three
|
||||
* symbols for objects that encode the data and size respectively.
|
||||
*
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param NAME The name to associate with this binary data (as an identifier.)
|
||||
* @param FILENAME The file to include (as a string literal.)
|
||||
*
|
||||
* @code
|
||||
* INCTXT(Readme, "readme.txt");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const char <prefix>Readme<data>[];
|
||||
* // const char *const <prefix>Readme<end>;
|
||||
* // const unsigned int <prefix>Readme<size>;
|
||||
* @endcode
|
||||
*
|
||||
* @warning This must be used in global scope
|
||||
* @warning The identifiers may be different if INCBIN_STYLE is not default
|
||||
*
|
||||
* To externally reference the data included by this in another translation unit
|
||||
* please @see INCBIN_EXTERN.
|
||||
*/
|
||||
#if defined(_MSC_VER)
|
||||
#define INCTXT(NAME, FILENAME) INCBIN_EXTERN(NAME)
|
||||
#else
|
||||
#define INCTXT(NAME, FILENAME) INCBIN_COMMON(char, NAME, FILENAME, INCBIN_BYTE "0\n")
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -1,3 +1,2 @@
|
||||
*.exe
|
||||
cache/
|
||||
*.perf.txt
|
||||
cache/
|
||||
105
miniwin/src/d3drm/backends/gxm/shaders/CMakeLists.txt
Normal file
105
miniwin/src/d3drm/backends/gxm/shaders/CMakeLists.txt
Normal file
@ -0,0 +1,105 @@
|
||||
cmake_minimum_required(VERSION 3.25...4.0 FATAL_ERROR)
|
||||
|
||||
find_program(PSP2CGC NAMES ${CMAKE_CURRENT_SOURCE_DIR}/psp2cgc.exe psp2cgc.exe psp2cgc REQUIRED)
|
||||
find_program(PSP2SHADERPERF NAMES ${CMAKE_CURRENT_SOURCE_DIR}/psp2shaderperf.exe psp2shaderperf.exe psp2shaderperf REQUIRED)
|
||||
|
||||
list(APPEND CGC_COMMON_FLAGS "-Wperf" "-cache" "-cachedir" "${CMAKE_CURRENT_BINARY_DIR}/cache" "-W4" "-Wsuppress=5206,5203")
|
||||
|
||||
make_directory("${CMAKE_CURRENT_BINARY_DIR}/cache")
|
||||
|
||||
# compile .cg to .gxp if psp2cgc is found
|
||||
macro(COMPILE_SHADER INPUT_CG OUTPUT_GXP PROFILE)
|
||||
set(EXTRA_FLAGS "${ARGN}")
|
||||
|
||||
set(INPUT_CG_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_CG}")
|
||||
set(OUTPUT_GXP_PATH "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_GXP}")
|
||||
set(TRACKED_GXP_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${OUTPUT_GXP}")
|
||||
|
||||
if(PSP2CGC)
|
||||
if(EXISTS ${TRACKED_GXP_PATH})
|
||||
file(COPY ${TRACKED_GXP_PATH} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
add_custom_command(
|
||||
OUTPUT "${OUTPUT_GXP_PATH}"
|
||||
COMMAND "${PSP2CGC}" ${CGC_COMMON_FLAGS} -profile "${PROFILE}" ${EXTRA_FLAGS} "${INPUT_CG_PATH}" -o "${OUTPUT_GXP_PATH}"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${OUTPUT_GXP_PATH}" "${TRACKED_GXP_PATH}"
|
||||
DEPENDS "${INPUT_CG_PATH}"
|
||||
COMMENT "Compiling ${INPUT_CG} -> ${OUTPUT_GXP} ${PROFILE}"
|
||||
)
|
||||
else()
|
||||
if(NOT EXISTS "${TRACKED_GXP_PATH}")
|
||||
message(FATAL_ERROR "missing shader ${TRACKED_GXP_PATH} ${INPUT_CG}, but psp2cgc.exe not found")
|
||||
endif()
|
||||
add_custom_command(
|
||||
OUTPUT "${OUTPUT_GXP_PATH}"
|
||||
COMMAND /usr/bin/env bash
|
||||
ARGS "-c"
|
||||
"if [ \"${INPUT_CG_PATH}\" -nt \"${TRACKED_GXP_PATH}\" ]; then
|
||||
echo '${INPUT_CG} changed but dont have psp2cgc, cant compile' >&2;
|
||||
exit 1;
|
||||
fi"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${TRACKED_GXP_PATH}" "${OUTPUT_GXP_PATH}"
|
||||
DEPENDS "${INPUT_CG_PATH}" "${TRACKED_GXP_PATH}"
|
||||
VERBATIM
|
||||
COMMENT "Copy ${TRACKED_GXP_PATH} -> ${OUTPUT_GXP} ${PROFILE}"
|
||||
)
|
||||
endif()
|
||||
set(TARGET_NAME "compile_${OUTPUT_GXP}")
|
||||
string(REPLACE "." "_" TARGET_NAME "${TARGET_NAME}")
|
||||
add_custom_target("${TARGET_NAME}" DEPENDS "${OUTPUT_GXP_PATH}")
|
||||
add_dependencies(gxm_shaders_compile "${TARGET_NAME}")
|
||||
endmacro()
|
||||
|
||||
|
||||
# analyze gxp to create a .perf.txt
|
||||
macro(GENERATE_PERF GXP_FILE)
|
||||
set(GXP_PATH "${CMAKE_CURRENT_BINARY_DIR}/${GXP_FILE}")
|
||||
string(REPLACE ".gxp" ".perf.txt" PERF_FILE_NAME "${GXP_FILE}")
|
||||
set(PERF_FILE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${PERF_FILE_NAME}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${PERF_FILE_PATH}"
|
||||
COMMAND "${PSP2SHADERPERF}" -stats -symbols -disasm "${GXP_PATH}" > "${PERF_FILE_PATH}"
|
||||
DEPENDS "${GXP_PATH}"
|
||||
COMMENT "Generating performance analysis for ${GXP_FILE}"
|
||||
)
|
||||
set(TARGET_NAME "perf_${PERF_FILE_NAME}")
|
||||
string(REPLACE "." "_" TARGET_NAME "${TARGET_NAME}")
|
||||
add_custom_target("${TARGET_NAME}" DEPENDS "${PERF_FILE_PATH}")
|
||||
add_dependencies(gxm_perfs "${TARGET_NAME}" )
|
||||
endmacro()
|
||||
|
||||
|
||||
|
||||
add_library(gxm_shaders STATIC gxm_shaders.s)
|
||||
|
||||
target_include_directories(gxm_shaders PRIVATE
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
add_custom_target(gxm_shaders_compile)
|
||||
add_dependencies(gxm_shaders gxm_shaders_compile)
|
||||
|
||||
COMPILE_SHADER(plane.vert.cg plane.vert.gxp sce_vp_psp2)
|
||||
COMPILE_SHADER(color.frag.cg color.frag.gxp sce_fp_psp2)
|
||||
COMPILE_SHADER(image.frag.cg image.frag.gxp sce_fp_psp2)
|
||||
COMPILE_SHADER(main.vert.cg main.vert.gxp sce_vp_psp2)
|
||||
COMPILE_SHADER(main.frag.cg main.color.frag.gxp sce_fp_psp2)
|
||||
COMPILE_SHADER(main.frag.cg main.texture.frag.gxp sce_fp_psp2 -DTEXTURED=1)
|
||||
|
||||
|
||||
# .perf.txt
|
||||
if(PSP2SHADERPERF)
|
||||
add_custom_target(gxm_perfs)
|
||||
add_dependencies(gxm_shaders gxm_perfs)
|
||||
GENERATE_PERF(plane.vert.gxp)
|
||||
GENERATE_PERF(color.frag.gxp)
|
||||
GENERATE_PERF(image.frag.gxp)
|
||||
GENERATE_PERF(main.vert.gxp)
|
||||
GENERATE_PERF(main.color.frag.gxp)
|
||||
GENERATE_PERF(main.texture.frag.gxp)
|
||||
else()
|
||||
message(STATUS "psp2shaderperf not found")
|
||||
endif()
|
||||
|
||||
|
||||
@ -1,27 +0,0 @@
|
||||
shaders += plane.vert.gxp color.frag.gxp image.frag.gxp
|
||||
shaders += main.vert.gxp main.color.frag.gxp main.texture.frag.gxp
|
||||
|
||||
PERFS = $(shaders:.gxp=.perf.txt)
|
||||
|
||||
all: $(shaders)
|
||||
perfs: $(PERFS)
|
||||
|
||||
CGCFLAGS = -Wperf -cache -cachedir cache -W4 -Wsuppress=5206,5203
|
||||
|
||||
cache:
|
||||
mkdir -p cache
|
||||
|
||||
%.vert.gxp: %.vert.cg | cache
|
||||
./psp2cgc.exe $(CGCFLAGS) -profile sce_vp_psp2 $< -o $@
|
||||
|
||||
%.frag.gxp: %.frag.cg | cache
|
||||
./psp2cgc.exe $(CGCFLAGS) -profile sce_fp_psp2 $< -o $@
|
||||
|
||||
main.color.frag.gxp: main.frag.cg | cache
|
||||
./psp2cgc.exe $(CGCFLAGS) -profile sce_fp_psp2 $< -o $@
|
||||
|
||||
main.texture.frag.gxp: main.frag.cg | cache
|
||||
./psp2cgc.exe $(CGCFLAGS) -DTEXTURED=1 -profile sce_fp_psp2 $< -o $@
|
||||
|
||||
%.perf.txt: %.gxp
|
||||
./psp2shaderperf.exe -stats -symbols -disasm $< > $@
|
||||
Binary file not shown.
26
miniwin/src/d3drm/backends/gxm/shaders/color.frag.perf.txt
Normal file
26
miniwin/src/d3drm/backends/gxm/shaders/color.frag.perf.txt
Normal file
@ -0,0 +1,26 @@
|
||||
Total estimated cost: 1 cycles, parallel mode
|
||||
Register count: 2 PAs, 0 temps, 4 SAs *
|
||||
Texture reads: 0 non-dependent, dependent: 0 unconditional, 0 conditional
|
||||
|
||||
High level analysis:
|
||||
No warnings.
|
||||
|
||||
* Please refer to the Razor documentation for details regarding the meaning of these numbers. Decreasing the number of registers used will not necessarily increase performance
|
||||
|
||||
Instruction statistics:
|
||||
Number of alu ops: 1
|
||||
Number of mem ops: 0
|
||||
Number of tex ops: 0
|
||||
Number of floating point ops: 0
|
||||
Number of integer ops: 0
|
||||
Number of pack ops: 1
|
||||
Number of mov ops: 0
|
||||
Number of nop ops: 0
|
||||
|
||||
Constants:
|
||||
[DEFAULT + 0 ] sa0 = (float4) uColor
|
||||
|
||||
|
||||
Primary program:
|
||||
0: pack.f16.f32 pa0.xyzw, sa0.xyzw
|
||||
|
||||
32
miniwin/src/d3drm/backends/gxm/shaders/gxm_shaders.h
Normal file
32
miniwin/src/d3drm/backends/gxm/shaders/gxm_shaders.h
Normal file
@ -0,0 +1,32 @@
|
||||
#include <psp2/gxm.h>
|
||||
|
||||
#define GXP(sym) \
|
||||
extern uint8_t _inc_##sym[]; \
|
||||
static const SceGxmProgram* sym = (const SceGxmProgram*) _inc_##sym;
|
||||
|
||||
GXP(mainVertexProgramGxp);
|
||||
GXP(mainColorFragmentProgramGxp);
|
||||
GXP(mainTextureFragmentProgramGxp);
|
||||
GXP(planeVertexProgramGxp);
|
||||
GXP(imageFragmentProgramGxp);
|
||||
GXP(colorFragmentProgramGxp);
|
||||
|
||||
static const SceGxmBlendInfo blendInfoOpaque = {
|
||||
.colorMask = SCE_GXM_COLOR_MASK_ALL,
|
||||
.colorFunc = SCE_GXM_BLEND_FUNC_NONE,
|
||||
.alphaFunc = SCE_GXM_BLEND_FUNC_NONE,
|
||||
.colorSrc = SCE_GXM_BLEND_FACTOR_ZERO,
|
||||
.colorDst = SCE_GXM_BLEND_FACTOR_ZERO,
|
||||
.alphaSrc = SCE_GXM_BLEND_FACTOR_ZERO,
|
||||
.alphaDst = SCE_GXM_BLEND_FACTOR_ZERO,
|
||||
};
|
||||
|
||||
static const SceGxmBlendInfo blendInfoTransparent = {
|
||||
.colorMask = SCE_GXM_COLOR_MASK_ALL,
|
||||
.colorFunc = SCE_GXM_BLEND_FUNC_ADD,
|
||||
.alphaFunc = SCE_GXM_BLEND_FUNC_ADD,
|
||||
.colorSrc = SCE_GXM_BLEND_FACTOR_SRC_ALPHA,
|
||||
.colorDst = SCE_GXM_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
||||
.alphaSrc = SCE_GXM_BLEND_FACTOR_ONE,
|
||||
.alphaDst = SCE_GXM_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
||||
};
|
||||
15
miniwin/src/d3drm/backends/gxm/shaders/gxm_shaders.s
Normal file
15
miniwin/src/d3drm/backends/gxm/shaders/gxm_shaders.s
Normal file
@ -0,0 +1,15 @@
|
||||
.section .rodata, "a", %progbits
|
||||
|
||||
.macro embed file, symbol
|
||||
.global _inc_\symbol
|
||||
.align 16
|
||||
_inc_\symbol:
|
||||
.incbin "\file"
|
||||
.endm
|
||||
|
||||
embed "main.vert.gxp", mainVertexProgramGxp
|
||||
embed "main.color.frag.gxp", mainColorFragmentProgramGxp
|
||||
embed "main.texture.frag.gxp", mainTextureFragmentProgramGxp
|
||||
embed "plane.vert.gxp", planeVertexProgramGxp
|
||||
embed "image.frag.gxp", imageFragmentProgramGxp
|
||||
embed "color.frag.gxp", colorFragmentProgramGxp
|
||||
Binary file not shown.
29
miniwin/src/d3drm/backends/gxm/shaders/image.frag.perf.txt
Normal file
29
miniwin/src/d3drm/backends/gxm/shaders/image.frag.perf.txt
Normal file
@ -0,0 +1,29 @@
|
||||
Total estimated cost: 0 cycles, parallel mode
|
||||
Register count: 2 PAs, 0 temps, 0 SAs *
|
||||
Texture reads: 1 non-dependent, dependent: 0 unconditional, 0 conditional
|
||||
|
||||
High level analysis:
|
||||
No warnings.
|
||||
|
||||
* Please refer to the Razor documentation for details regarding the meaning of these numbers. Decreasing the number of registers used will not necessarily increase performance
|
||||
|
||||
Instruction statistics:
|
||||
Number of alu ops: 0
|
||||
Number of mem ops: 0
|
||||
Number of tex ops: 0
|
||||
Number of floating point ops: 0
|
||||
Number of integer ops: 0
|
||||
Number of pack ops: 0
|
||||
Number of mov ops: 0
|
||||
Number of nop ops: 0
|
||||
|
||||
|
||||
Samplers:
|
||||
TEXUNIT0 = uTexture
|
||||
|
||||
Iterators:
|
||||
|
||||
|
||||
Primary program:
|
||||
pa0 = tex2D<half4>(uTexture, TEXCOORD0.xy)
|
||||
|
||||
Binary file not shown.
134
miniwin/src/d3drm/backends/gxm/shaders/main.color.frag.perf.txt
Normal file
134
miniwin/src/d3drm/backends/gxm/shaders/main.color.frag.perf.txt
Normal file
@ -0,0 +1,134 @@
|
||||
Total estimated cost: 63.5 cycles, parallel mode
|
||||
Register count: 8 PAs, 16 temps, 38 SAs *
|
||||
Texture reads: 0 non-dependent, dependent: 0 unconditional, 0 conditional
|
||||
|
||||
High level analysis:
|
||||
No warnings.
|
||||
|
||||
* Please refer to the Razor documentation for details regarding the meaning of these numbers. Decreasing the number of registers used will not necessarily increase performance
|
||||
|
||||
Instruction statistics:
|
||||
Number of alu ops: 63
|
||||
Number of mem ops: 0
|
||||
Number of tex ops: 0
|
||||
Number of floating point ops: 43
|
||||
Number of integer ops: 1
|
||||
Number of pack ops: 1
|
||||
Number of mov ops: 15
|
||||
Number of nop ops: 2
|
||||
|
||||
Constants:
|
||||
[DEFAULT + 0 ] sa0 = (float1) uShininess
|
||||
[DEFAULT + 2 ] sa2 = (float4) uColor
|
||||
[BUFFER0 + 0 ] = (float4) uLights[0].color
|
||||
[BUFFER0 + 4 ] = (float4) uLights[0].vec
|
||||
[BUFFER0 + 8 ] = (float1) uLights[0].isDirectional
|
||||
[BUFFER0 + 10 ] = (float4) uLights[1].color
|
||||
[BUFFER0 + 14 ] = (float4) uLights[1].vec
|
||||
[BUFFER0 + 18 ] = (float1) uLights[1].isDirectional
|
||||
[BUFFER0 + 20 ] = (float3) uAmbientLight
|
||||
[LITERAL + 1 ] sa7 = 0xffffffff (-1.#QNAN0f) (-1.#QNANh, -1.#QNANh)
|
||||
[LITERAL + 2 ] sa8 = 0x3f800000 (1.000000f) (0.00000h, 1.87500h)
|
||||
[BUFFER0 ] sa6 = buffer base address
|
||||
|
||||
Iterators:
|
||||
pa0 = (float4) TEXCOORD1
|
||||
pa4 = (float4) TEXCOORD2
|
||||
|
||||
|
||||
Secondary program:
|
||||
0 : lda32 sa1, [sa6, 0x9]
|
||||
1 : lda32.fetch3 sa9, [sa6, 0x4]
|
||||
2 : mul.f32 sa32.xy, -sa8.yz, sa0.yy
|
||||
3 : mul.f32 sa34.x, -sa10.y, sa0.y
|
||||
4 : lda32.fetch4 sa12, [sa6, 0x0]
|
||||
5 : mov.f32 sa36.xy, {0, 0}
|
||||
6 : mov.f32 sa30.x, {0}
|
||||
7 : lda32 sa15, [sa6, 0x13]
|
||||
8 : lda32.fetch3 sa16, [sa6, 0xe]
|
||||
9 : mul.f32 sa26.xy, -sa16.xy, sa14.yy
|
||||
10: mul.f32 sa28.x, -sa18.x, sa14.y
|
||||
11: lda32.fetch4 sa19, [sa6, 0xa]
|
||||
12: lda32.fetch3 sa22, [sa6, 0x14]
|
||||
13: nop
|
||||
|
||||
Primary program:
|
||||
0 : cmp.gt.f32 p0, sa0.x, {0}
|
||||
1 : mov.f32 i0.xyz, pa0.xyz
|
||||
2 : add.f32 i1.xyz, sa8.yzw, -i0.xyz
|
||||
2 : +dot.f32 pa3.x, i0.xyz, i0.xyz
|
||||
3 : mov.f32 i2.xyz, sa32.xyz
|
||||
4 : mad.f32 i2.xyz, -sa0.yyy, i1.xyz, i2.xyz
|
||||
5 : add.f32 i1.xyz, i2.xyz, i1.xyz
|
||||
5 : +rsq.f32 pa3.x, pa3.x
|
||||
6 : dot.f32 i2.x, i1.xyz, i1.xyz
|
||||
7 : rsq.f32 pa3.x, i2.x
|
||||
7 : +mul.f32 i0.xyz, -i0.xyz, pa2.yyy
|
||||
8 : mad.f32 i2.xyz, pa2.yyy, i1.xyz, i0.xyz
|
||||
8 : +mov.f32 r6.xy, i0.xy
|
||||
9 : mov.f32 r8.x, i0.z
|
||||
10: dot.f32 i0.x, i2.xyz, i2.xyz
|
||||
10: +mov.f32 r2.xy, i2.xy
|
||||
11: mov.f32 r4.x, i2.z
|
||||
12: rsq.f32 pa3.x, i0.x
|
||||
12: +mul.f32 i0.xyz, i1.xyz, pa2.yyy
|
||||
13: dot.f32 i0.x, pa4.xyz, i0.xyz
|
||||
14: max.f32 r14.x, i0.x, {0}
|
||||
15: p0 br #19
|
||||
16: mov.f32 r10.xy, sa36.xy
|
||||
17: or.u32 r12.x, sa30.x, 0x0
|
||||
18: br #30
|
||||
#19: cmov.lezero.f32 r0.x, r14.x, {0}, sa8.x
|
||||
20: cmov.ltzero.f32 i0.x, r14.x, sa8.x, {0}
|
||||
21: mad.f32 i0.x, r0.x, {1}, -i0.x
|
||||
22: mul.f32 i1.xyz, r2.xyz, pa2.yyy
|
||||
23: dot.f32 i1.x, pa4.xyz, i1.xyz
|
||||
24: max.f32 i1.x, i1.x, {0}
|
||||
25: mul.f32 i0.xyz, sa12.xyz, i0.xxx
|
||||
25: +log.f32 pa3.x, i1.x
|
||||
26: mul.f32 i1.x, sa0.x, pa2.y
|
||||
27: exp.f32 i1.x, i1.x
|
||||
28: mad.f32 r10.xy, i0.xy, i1.xx, {0, 0}
|
||||
29: mul.f32 r12.x, i0.z, i1.x
|
||||
#30: nop
|
||||
31: mov.f32 i0.xyz, sa16.xyz
|
||||
32: mad.f32 i0.xyz, -pa0.xyz, {1, 1, 1}, i0.xyz
|
||||
33: mov.f32 i1.xyz, sa26.xyz
|
||||
34: mad.f32 i1.xyz, -sa14.yyy, i0.xyz, i1.xyz
|
||||
35: add.f32 i0.xyz, i1.xyz, i0.xyz
|
||||
35: +mov.f32 i2.xyz, r6.xyz
|
||||
36: dot.f32 i1.x, i0.xyz, i0.xyz
|
||||
37: rsq.f32 i1.x, i1.x
|
||||
38: mad.f32 i2.xyz, i1.xxx, i0.xyz, i2.xyz
|
||||
39: mul.f32 i0.xyz, i0.xyz, i1.xxx
|
||||
39: +mov.f32 r0.xy, i2.xy
|
||||
40: dot.f32 i0.x, pa4.xyz, i0.xyz
|
||||
41: max.f32 pa2.x, i0.x, {0}
|
||||
42: dot.f32 i0.x, i2.xyz, i2.xyz
|
||||
43: mov.f32 r2.x, i2.z
|
||||
44: rsq.f32 pa0.-y, i0.x
|
||||
45: !p0 br #57
|
||||
46: cmov.lezero.f32 pa0.x, pa2.x, {0}, sa8.x
|
||||
47: cmov.ltzero.f32 i0.x, pa2.x, sa8.x, {0}
|
||||
48: mad.f32 i0.x, pa0.x, {1}, -i0.x
|
||||
49: mul.f32 i1.xyz, r0.xyz, pa0.yyy
|
||||
50: dot.f32 i1.x, pa4.xyz, i1.xyz
|
||||
51: max.f32 i1.x, i1.x, {0}
|
||||
52: mul.f32 i0.xyz, sa18.yzw, i0.xxx
|
||||
52: +log.f32 pa0.x, i1.x
|
||||
53: mul.f32 i1.x, sa0.x, pa0.x
|
||||
54: exp.f32 i1.x, i1.x
|
||||
55: mad.f32 r10.xy, i0.xy, i1.xx, r10.xy
|
||||
56: mad.f32 r12.x, i0.z, i1.x, r12.x
|
||||
#57: nop
|
||||
58: mad.f32 i0.xy, r14.xx, sa12.xy, sa22.xy
|
||||
59: mad.f32 i0.--z, r14.--x, sa14.--x, sa24.--x
|
||||
60: mov.f32 i1.xyz, sa18.yzw
|
||||
61: mad.f32 i0.xyz, pa2.xxx, i1.xyz, i0.xyz
|
||||
62: mov.f32 i1.xyz, r10.xyz
|
||||
63: mov.f32 i2.xyzw, sa2.xyzw
|
||||
64: mad.f32 i0.xyz, i2.xyz, i0.xyz, i1.xyz
|
||||
65: min.f32 i0.xyz, i0.xyz, {1, 1, 1}
|
||||
66: max.f32 i2.xyz, i0.xyz, {0, 0, 0}
|
||||
67: pack.f16.f32 pa0.xyzw, i2.xyzw
|
||||
|
||||
@ -6,9 +6,9 @@ struct SceneLight {
|
||||
|
||||
void main(
|
||||
float4 vPosition : POSITION,
|
||||
float2 vTexCoord : TEXCOORD0,
|
||||
float3 vViewPos : TEXCOORD1,
|
||||
float3 vNormal : TEXCOORD2,
|
||||
float2 vTexCoord : TEXCOORD3,
|
||||
|
||||
uniform __nostrip SceneLight uLights[2] : BUFFER[0],
|
||||
uniform __nostrip float3 uAmbientLight : BUFFER[0],
|
||||
|
||||
Binary file not shown.
@ -0,0 +1,139 @@
|
||||
Total estimated cost: 64.5 cycles, parallel mode
|
||||
Register count: 12 PAs, 16 temps, 38 SAs *
|
||||
Texture reads: 1 non-dependent, dependent: 0 unconditional, 0 conditional
|
||||
|
||||
High level analysis:
|
||||
No warnings.
|
||||
|
||||
* Please refer to the Razor documentation for details regarding the meaning of these numbers. Decreasing the number of registers used will not necessarily increase performance
|
||||
|
||||
Instruction statistics:
|
||||
Number of alu ops: 64
|
||||
Number of mem ops: 0
|
||||
Number of tex ops: 0
|
||||
Number of floating point ops: 44
|
||||
Number of integer ops: 1
|
||||
Number of pack ops: 1
|
||||
Number of mov ops: 15
|
||||
Number of nop ops: 2
|
||||
|
||||
Constants:
|
||||
[DEFAULT + 0 ] sa0 = (float1) uShininess
|
||||
[DEFAULT + 2 ] sa2 = (float4) uColor
|
||||
[BUFFER0 + 0 ] = (float4) uLights[0].color
|
||||
[BUFFER0 + 4 ] = (float4) uLights[0].vec
|
||||
[BUFFER0 + 8 ] = (float1) uLights[0].isDirectional
|
||||
[BUFFER0 + 10 ] = (float4) uLights[1].color
|
||||
[BUFFER0 + 14 ] = (float4) uLights[1].vec
|
||||
[BUFFER0 + 18 ] = (float1) uLights[1].isDirectional
|
||||
[BUFFER0 + 20 ] = (float3) uAmbientLight
|
||||
[LITERAL + 1 ] sa7 = 0xffffffff (-1.#QNAN0f) (-1.#QNANh, -1.#QNANh)
|
||||
[LITERAL + 2 ] sa8 = 0x3f800000 (1.000000f) (0.00000h, 1.87500h)
|
||||
[BUFFER0 ] sa6 = buffer base address
|
||||
|
||||
Samplers:
|
||||
TEXUNIT0 = uTexture
|
||||
|
||||
Iterators:
|
||||
pa0 = (float4) TEXCOORD1
|
||||
pa8 = (float4) TEXCOORD2
|
||||
|
||||
|
||||
Secondary program:
|
||||
0 : lda32 sa1, [sa6, 0x9]
|
||||
1 : lda32.fetch3 sa9, [sa6, 0x4]
|
||||
2 : mul.f32 sa32.xy, -sa8.yz, sa0.yy
|
||||
3 : mul.f32 sa34.x, -sa10.y, sa0.y
|
||||
4 : lda32.fetch4 sa12, [sa6, 0x0]
|
||||
5 : mov.f32 sa36.xy, {0, 0}
|
||||
6 : mov.f32 sa30.x, {0}
|
||||
7 : lda32 sa15, [sa6, 0x13]
|
||||
8 : lda32.fetch3 sa16, [sa6, 0xe]
|
||||
9 : mul.f32 sa26.xy, -sa16.xy, sa14.yy
|
||||
10: mul.f32 sa28.x, -sa18.x, sa14.y
|
||||
11: lda32.fetch4 sa19, [sa6, 0xa]
|
||||
12: lda32.fetch3 sa22, [sa6, 0x14]
|
||||
13: nop
|
||||
|
||||
Primary program:
|
||||
pa4 = tex2D<float4>(uTexture, TEXCOORD0.xy)
|
||||
0 : cmp.gt.f32 p0, sa0.x, {0}
|
||||
1 : mov.f32 i0.xyz, pa0.xyz
|
||||
2 : add.f32 i1.xyz, sa8.yzw, -i0.xyz
|
||||
2 : +dot.f32 pa3.x, i0.xyz, i0.xyz
|
||||
3 : mov.f32 i2.xyz, sa32.xyz
|
||||
4 : mad.f32 i2.xyz, -sa0.yyy, i1.xyz, i2.xyz
|
||||
5 : add.f32 i1.xyz, i2.xyz, i1.xyz
|
||||
5 : +rsq.f32 pa3.x, pa3.x
|
||||
6 : dot.f32 i2.x, i1.xyz, i1.xyz
|
||||
7 : rsq.f32 pa3.x, i2.x
|
||||
7 : +mul.f32 i0.xyz, -i0.xyz, pa2.yyy
|
||||
8 : mad.f32 i2.xyz, pa2.yyy, i1.xyz, i0.xyz
|
||||
8 : +mov.f32 r6.xy, i0.xy
|
||||
9 : mov.f32 r8.x, i0.z
|
||||
10: dot.f32 i0.x, i2.xyz, i2.xyz
|
||||
10: +mov.f32 r2.xy, i2.xy
|
||||
11: mov.f32 r4.x, i2.z
|
||||
12: rsq.f32 pa3.x, i0.x
|
||||
12: +mul.f32 i0.xyz, i1.xyz, pa2.yyy
|
||||
13: dot.f32 i0.x, pa8.xyz, i0.xyz
|
||||
14: max.f32 r14.x, i0.x, {0}
|
||||
15: p0 br #19
|
||||
16: mov.f32 r10.xy, sa36.xy
|
||||
17: or.u32 r12.x, sa30.x, 0x0
|
||||
18: br #30
|
||||
#19: cmov.lezero.f32 r0.x, r14.x, {0}, sa8.x
|
||||
20: cmov.ltzero.f32 i0.x, r14.x, sa8.x, {0}
|
||||
21: mad.f32 i0.x, r0.x, {1}, -i0.x
|
||||
22: mul.f32 i1.xyz, r2.xyz, pa2.yyy
|
||||
23: dot.f32 i1.x, pa8.xyz, i1.xyz
|
||||
24: max.f32 i1.x, i1.x, {0}
|
||||
25: mul.f32 i0.xyz, sa12.xyz, i0.xxx
|
||||
25: +log.f32 pa3.x, i1.x
|
||||
26: mul.f32 i1.x, sa0.x, pa2.y
|
||||
27: exp.f32 i1.x, i1.x
|
||||
28: mad.f32 r10.xy, i0.xy, i1.xx, {0, 0}
|
||||
29: mul.f32 r12.x, i0.z, i1.x
|
||||
#30: nop
|
||||
31: mov.f32 i0.xyz, sa16.xyz
|
||||
32: mad.f32 i0.xyz, -pa0.xyz, {1, 1, 1}, i0.xyz
|
||||
33: mov.f32 i1.xyz, sa26.xyz
|
||||
34: mad.f32 i1.xyz, -sa14.yyy, i0.xyz, i1.xyz
|
||||
35: add.f32 i0.xyz, i1.xyz, i0.xyz
|
||||
35: +mov.f32 i2.xyz, r6.xyz
|
||||
36: dot.f32 i1.x, i0.xyz, i0.xyz
|
||||
37: rsq.f32 i1.x, i1.x
|
||||
38: mad.f32 i2.xyz, i1.xxx, i0.xyz, i2.xyz
|
||||
39: mul.f32 i0.xyz, i0.xyz, i1.xxx
|
||||
39: +mov.f32 r0.xy, i2.xy
|
||||
40: dot.f32 i0.x, pa8.xyz, i0.xyz
|
||||
41: max.f32 pa2.x, i0.x, {0}
|
||||
42: dot.f32 i0.x, i2.xyz, i2.xyz
|
||||
43: mov.f32 r2.x, i2.z
|
||||
44: rsq.f32 pa0.-y, i0.x
|
||||
45: !p0 br #57
|
||||
46: cmov.lezero.f32 pa0.x, pa2.x, {0}, sa8.x
|
||||
47: cmov.ltzero.f32 i0.x, pa2.x, sa8.x, {0}
|
||||
48: mad.f32 i0.x, pa0.x, {1}, -i0.x
|
||||
49: mul.f32 i1.xyz, r0.xyz, pa0.yyy
|
||||
50: dot.f32 i1.x, pa8.xyz, i1.xyz
|
||||
51: max.f32 i1.x, i1.x, {0}
|
||||
52: mul.f32 i0.xyz, sa18.yzw, i0.xxx
|
||||
52: +log.f32 pa0.x, i1.x
|
||||
53: mul.f32 i1.x, sa0.x, pa0.x
|
||||
54: exp.f32 i1.x, i1.x
|
||||
55: mad.f32 r10.xy, i0.xy, i1.xx, r10.xy
|
||||
56: mad.f32 r12.x, i0.z, i1.x, r12.x
|
||||
#57: nop
|
||||
58: mad.f32 i0.xy, r14.xx, sa12.xy, sa22.xy
|
||||
59: mad.f32 i0.--z, r14.--x, sa14.--x, sa24.--x
|
||||
60: mov.f32 i1.xyz, sa18.yzw
|
||||
61: mad.f32 i0.xyz, pa2.xxx, i1.xyz, i0.xyz
|
||||
62: mov.f32 i1.xyz, r10.xyz
|
||||
63: mov.f32 i2.xyzw, sa2.xyzw
|
||||
64: mad.f32 i0.xyz, i2.xyz, i0.xyz, i1.xyz
|
||||
65: min.f32 i0.xyz, i0.xyz, {1, 1, 1}
|
||||
66: max.f32 i0.xyz, i0.xyz, {0, 0, 0}
|
||||
67: mad.f32 i2.xyz, pa4.xyz, i0.xyz, {0, 0, 0}
|
||||
68: pack.f16.f32 pa0.xyzw, i2.xyzw
|
||||
|
||||
@ -8,9 +8,9 @@ void main(
|
||||
column_major uniform float4x4 uProjectionMatrix,
|
||||
|
||||
out float4 vPosition : POSITION,
|
||||
out float2 vTexCoord : TEXCOORD0,
|
||||
out float3 vViewPos : TEXCOORD1,
|
||||
out float3 vNormal : TEXCOORD2,
|
||||
out float2 vTexCoord : TEXCOORD3
|
||||
out float3 vNormal : TEXCOORD2
|
||||
) {
|
||||
float4 viewPos = mul(uModelViewMatrix, float4(aPosition, 1.0));
|
||||
vPosition = mul(uProjectionMatrix, viewPos);
|
||||
|
||||
Binary file not shown.
87
miniwin/src/d3drm/backends/gxm/shaders/main.vert.perf.txt
Normal file
87
miniwin/src/d3drm/backends/gxm/shaders/main.vert.perf.txt
Normal file
@ -0,0 +1,87 @@
|
||||
Total estimated cost: 18 cycles, parallel mode
|
||||
Register count: 12 PAs, 0 temps, 56 SAs *
|
||||
Texture reads: 0 non-dependent, dependent: 0 unconditional, 0 conditional
|
||||
|
||||
High level analysis:
|
||||
- One or multiple vertex outputs are misaligned due to a three coefficient TEXCOORD vertex output. This will lead to additional data movement instructions being generated. If only one three coefficient TEXCOORD output is present, assigning it the coordinate with the greatest index will allow the previous coordinates to be aligned.
|
||||
|
||||
* Please refer to the Razor documentation for details regarding the meaning of these numbers. Decreasing the number of registers used will not necessarily increase performance
|
||||
|
||||
Instruction statistics:
|
||||
Number of alu ops: 19
|
||||
Number of mem ops: 0
|
||||
Number of tex ops: 0
|
||||
Number of floating point ops: 14
|
||||
Number of integer ops: 0
|
||||
Number of pack ops: 0
|
||||
Number of mov ops: 4
|
||||
Number of nop ops: 0
|
||||
|
||||
Constants:
|
||||
[DEFAULT + 0 ] sa0 = (float4) uModelViewMatrix[4]
|
||||
[DEFAULT + 32 ] sa32 = (float3) uNormalMatrix[3]
|
||||
[DEFAULT + 16 ] sa16 = (float4) uProjectionMatrix[4]
|
||||
|
||||
Vertex attributes:
|
||||
pa0 = (float4) aPosition
|
||||
pa4 = (float4) aNormal
|
||||
pa8 = (float4) aTexCoord
|
||||
|
||||
|
||||
Secondary program:
|
||||
0 : nop
|
||||
1 : mov.f32 i0.xyzw, sa8.xyzw
|
||||
2 : mul.f32 i1.xyzw, sa16.xyzw, i0.xxxx
|
||||
3 : mad.f32 i1.xyzw, sa20.xyzw, i0.yyyy, i1.xyzw
|
||||
4 : mad.f32 i1.xyzw, sa24.xyzw, i0.zzzz, i1.xyzw
|
||||
5 : mad.f32 sa52.xy, sa28.xy, i0.ww, i1.xy
|
||||
6 : mad.f32 sa54.xy, sa30.xy, i0.ww, i1.zw
|
||||
7 : mov.f32 i1.xyzw, sa12.xyzw
|
||||
8 : mul.f32 i2.xyzw, sa28.xyzw, i1.wwww
|
||||
9 : mad.f32 i2.xyzw, sa24.xyzw, i1.zzzz, i2.xyzw
|
||||
10: mad.f32 i2.xyzw, sa20.xyzw, i1.yyyy, i2.xyzw
|
||||
11: mad.f32 sa48.xy, sa16.xy, i1.xx, i2.xy
|
||||
12: mad.f32 sa50.xy, sa18.xy, i1.xx, i2.zw
|
||||
13: mov.f32 i2.xyzw, sa4.xyzw
|
||||
14: mul.f32 i1.xyzw, sa16.xyzw, i2.xxxx
|
||||
15: mad.f32 i1.xyzw, sa20.xyzw, i2.yyyy, i1.xyzw
|
||||
16: mad.f32 i1.xyzw, sa24.xyzw, i2.zzzz, i1.xyzw
|
||||
17: mad.f32 sa44.xy, sa28.xy, i2.ww, i1.xy
|
||||
18: mad.f32 sa46.xy, sa30.xy, i2.ww, i1.zw
|
||||
19: mov.f32 i1.xyzw, sa0.xyzw
|
||||
20: mul.f32 i0.xyzw, sa16.xyzw, i1.xxxx
|
||||
20: +mov.f32 sa4.xy, i1.yy
|
||||
21: mad.f32 i0.xyzw, sa20.xyzw, i1.yyyy, i0.xyzw
|
||||
22: mad.f32 i0.xyzw, sa24.xyzw, i1.zzzz, i0.xyzw
|
||||
23: mad.f32 sa18.xy, sa28.xy, i1.ww, i0.xy
|
||||
24: mad.f32 sa16.xy, sa30.xy, i1.ww, i0.zw
|
||||
25: mov.f32 sa4.-y, i2.-y
|
||||
26: mov.f32 sa6.xy, (sa8.y, sa12.y)
|
||||
27: mov.f32 sa0.x, i1.x
|
||||
28: mov.f32 sa0.-y, i2.-x
|
||||
29: mov.f32 sa8.-y, sa12.-x
|
||||
30: mov.f32 sa10.-y, sa14.-x
|
||||
31: mov.f32 sa2.xy, sa8.xy
|
||||
32: mov.f32 sa8.x, i1.z
|
||||
33: mov.f32 sa8.-y, i2.-z
|
||||
|
||||
Primary program:
|
||||
0 : mov.f32 o4.xy, pa8.xy
|
||||
1 : mov.f32 i0.xyz, pa4.xyz
|
||||
2 : mad.f32 i1.xyz, sa32.xyz, i0.xxx, {0, 0, 0}
|
||||
3 : mad.f32 i1.xyz, sa36.xyz, i0.yyy, i1.xyz
|
||||
4 : mad.f32 i0.xyz, sa40.xyz, i0.zzz, i1.xyz
|
||||
5 : mov.f32 i1.xyz, pa0.xyz
|
||||
6 : mov.f32 i2.xyzw, sa48.xyzw
|
||||
7 : mad.f32 i2.xyzw, sa52.xyzw, i1.zzzz, i2.xyzw
|
||||
8 : mad.f32 i2.xyzw, sa44.xyzw, i1.yyyy, i2.xyzw
|
||||
9 : mad.f32 o0.xy, sa18.xy, i1.xx, i2.xy
|
||||
10: mad.f32 o2.xy, sa16.xy, i1.xx, i2.zw
|
||||
11: dot.f32 o6.x, sa0.xyzw, i1.xyz1
|
||||
12: dot.f32 o6.-y, sa4.xyzw, i1.xyz1
|
||||
13: dot.f32 o6.--z, sa8.xyzw, i1.xyz1
|
||||
14: dot.f32 i1.x, i0.xyz, i0.xyz
|
||||
15: rsq.f32 i1.x, i1.x
|
||||
16: mad.f32 o10.xy, i0.yz, i1.xx, {0, 0}
|
||||
17: mad.f32 o8.-y, i0.-x, i1.-x, {0, 0}
|
||||
|
||||
Binary file not shown.
30
miniwin/src/d3drm/backends/gxm/shaders/plane.vert.perf.txt
Normal file
30
miniwin/src/d3drm/backends/gxm/shaders/plane.vert.perf.txt
Normal file
@ -0,0 +1,30 @@
|
||||
Total estimated cost: 3 cycles, parallel mode
|
||||
Register count: 8 PAs, 0 temps, 0 SAs *
|
||||
Texture reads: 0 non-dependent, dependent: 0 unconditional, 0 conditional
|
||||
|
||||
High level analysis:
|
||||
No warnings.
|
||||
|
||||
* Please refer to the Razor documentation for details regarding the meaning of these numbers. Decreasing the number of registers used will not necessarily increase performance
|
||||
|
||||
Instruction statistics:
|
||||
Number of alu ops: 4
|
||||
Number of mem ops: 0
|
||||
Number of tex ops: 0
|
||||
Number of floating point ops: 2
|
||||
Number of integer ops: 0
|
||||
Number of pack ops: 0
|
||||
Number of mov ops: 1
|
||||
Number of nop ops: 0
|
||||
|
||||
|
||||
Vertex attributes:
|
||||
pa0 = (float4) aPosition
|
||||
pa4 = (float4) aTexCoord
|
||||
|
||||
|
||||
Primary program:
|
||||
0: mov.f32 o4.xy, pa4.xy
|
||||
1: mul.f32 o0.xy, pa0.xy, {1, 1}
|
||||
2: mul.f32 o2.xy, {1, 1}, {1, 1}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "../d3drm/backends/gxm/gxm_context.h"
|
||||
#include "d3drmrenderer.h"
|
||||
#include "d3drmtexture_impl.h"
|
||||
#include "ddpalette_impl.h"
|
||||
#include "ddraw_impl.h"
|
||||
#include "gxm_context.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <psp2/gxm.h>
|
||||
@ -52,7 +52,7 @@ struct GXMSceneLightUniform {
|
||||
class GXMRenderer : public Direct3DRMRenderer {
|
||||
public:
|
||||
static Direct3DRMRenderer* Create(DWORD width, DWORD height, DWORD msaaSamples);
|
||||
GXMRenderer(DWORD width, DWORD height, DWORD msaaSamples);
|
||||
GXMRenderer(DWORD width, DWORD height, SceGxmMultisampleMode msaaMode);
|
||||
~GXMRenderer() override;
|
||||
|
||||
void PushLights(const SceneLight* lightsArray, size_t count) override;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user