Merge branch 'master' of github.com:hrydgard/ppsspp

This commit is contained in:
Henrik Rydgard 2012-11-04 19:57:37 +01:00
commit 0c66247c0d
29 changed files with 221 additions and 165 deletions

View File

@ -22,6 +22,7 @@ set(SRCS
Timer.cpp
x64Analyzer.cpp
x64Emitter.cpp
x86Disasm.cpp
)
# TODO

View File

@ -18,6 +18,8 @@
#ifndef _FIXED_SIZE_QUEUE_H_
#define _FIXED_SIZE_QUEUE_H_
#include <cstring>
// STL-look-a-like interface, but name is mixed case to distinguish it clearly from the
// real STL classes.

View File

@ -15,11 +15,10 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef _LOG_H_
#define _LOG_H_
#pragma once
#define NOTICE_LEVEL 1 // VERY important information that is NOT errors. Like startup and OSReports.
#define ERROR_LEVEL 2 // Critical errors
#define NOTICE_LEVEL 1 // VERY important information that is NOT errors. Like startup and debugprintfs from the game itself.
#define ERROR_LEVEL 2 // Important errors.
#define WARNING_LEVEL 3 // Something is suspicious.
#define INFO_LEVEL 4 // General information.
#define DEBUG_LEVEL 5 // Detailed debugging - might make things slow.
@ -28,28 +27,28 @@ namespace LogTypes
{
enum LOG_TYPE {
MASTER_LOG,
BOOT,
COMMON,
CPU,
LOADER,
IO,
PAD,
FILESYS,
DISCIO,
G3D,
DMA,
INTC,
MEMMAP,
SOUND,
HLE,
TIMER,
VIDEO,
DYNA_REC,
NETPLAY,
MASTER_LOG,
BOOT,
COMMON,
CPU,
LOADER,
IO,
PAD,
FILESYS,
DISCIO,
G3D,
DMA,
INTC,
MEMMAP,
SOUND,
HLE,
TIMER,
VIDEO,
DYNA_REC,
NETPLAY,
NUMBER_OF_LOGS, // Must be last
JIT = DYNA_REC,
NUMBER_OF_LOGS, // Must be last
JIT = DYNA_REC,
};
// FIXME: should this be removed?
@ -73,30 +72,25 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type,
#endif
;
#if defined(LOGGING) || defined(_DEBUG) || defined(DEBUGFAST) // || defined(ANDROID) //|| defined(__APPLE__)//
#if defined(LOGGING) || defined(_DEBUG) || defined(DEBUGFAST)
#define MAX_LOGLEVEL DEBUG_LEVEL
#else
#ifndef MAX_LOGLEVEL
#define MAX_LOGLEVEL INFO_LEVEL
//#define MAX_LOGLEVEL NOTICE_LEVEL
#endif // loglevel
#endif // logging
#ifdef GEKKO
#define GENERIC_LOG(t, v, ...)
#else
// Let the compiler optimize this out
#define GENERIC_LOG(t, v, ...) { \
if (v <= MAX_LOGLEVEL) \
GenericLog(v, t, __FILE__, __LINE__, __VA_ARGS__); \
}
#endif
#define ERROR_LOG(t,...) { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) }
#define WARN_LOG(t,...) { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) }
#define ERROR_LOG(t,...) { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) }
#define WARN_LOG(t,...) { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) }
#define NOTICE_LOG(t,...) { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) }
#define INFO_LOG(t,...) { GENERIC_LOG(LogTypes::t, LogTypes::LINFO, __VA_ARGS__) }
#define DEBUG_LOG(t,...) { GENERIC_LOG(LogTypes::t, LogTypes::LDEBUG, __VA_ARGS__) }
#define INFO_LOG(t,...) { GENERIC_LOG(LogTypes::t, LogTypes::LINFO, __VA_ARGS__) }
#define DEBUG_LOG(t,...) { GENERIC_LOG(LogTypes::t, LogTypes::LDEBUG, __VA_ARGS__) }
#if MAX_LOGLEVEL >= DEBUG_LEVEL
#define _dbg_assert_(_t_, _a_) \
@ -123,8 +117,7 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type,
#define _assert_(_a_) _dbg_assert_(MASTER_LOG, _a_)
#ifndef GEKKO
#ifdef _WIN32
#ifdef _MSC_VER
#define _assert_msg_(_t_, _a_, _fmt_, ...) \
if (!(_a_)) {\
if (!PanicYesNo(_fmt_, __VA_ARGS__)) {Crash();} \
@ -135,8 +128,3 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type,
if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) {Crash();} \
}
#endif // WIN32
#else // GEKKO
#define _assert_msg_(_t_, _a_, _fmt_, ...)
#endif
#endif // _LOG_H_

View File

@ -5,8 +5,10 @@
#error DO NOT COMPILE THIS INTO ANDROID BUILDS
#endif
#include <stdlib.h>
#include <string.h>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cstdarg>
#include "x86Disasm.h"
/*==============================unasmt.h==============================*/

View File

@ -76,8 +76,11 @@ set(SRCS
set(SRCS ${SRCS})
add_library(core STATIC ${SRCS})
target_link_libraries(core general common)
target_link_libraries(core general base)
if(UNIX)
add_definitions(-fPIC)
add_definitions(-std=gnu++0x)
endif(UNIX)

View File

@ -85,5 +85,7 @@ void CConfig::Save()
iniFile.Save(iniFilename_.c_str());
NOTICE_LOG(LOADER, "Config saved: %s", iniFilename_.c_str());
} else {
NOTICE_LOG(LOADER, "Error saving config: %s", iniFilename_.c_str());
}
}

View File

@ -17,6 +17,7 @@
#include <vector>
#include <cstdio>
#include "MsgHandler.h"
#include "StdMutex.h"

View File

@ -16,9 +16,10 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "../Core.h"
#include "BreakPoints.h"
#include "Breakpoints.h"
#include "SymbolMap.h"
#include "FixedSizeUnorderedSet.h"
#include <cstdio>
#define MAX_BREAKPOINTS 16

View File

@ -16,6 +16,7 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include <cstdio>
struct MemMap;

View File

@ -21,6 +21,8 @@ extern "C"
};
#include "BlockDevices.h"
#include <cstdio>
#include <cstring>
FileBlockDevice::FileBlockDevice(std::string _filename)
: filename(_filename)

View File

@ -18,6 +18,8 @@
#include "Globals.h"
#include "Log.h"
#include "ISOFileSystem.h"
#include <cstring>
#include <cstdio>
const int sectorSize = 2048;

View File

@ -139,7 +139,7 @@ void sceGeContinue()
u32 sceGeSetCallback(u32 structAddr)
{
ERROR_LOG(HLE,"sceGeSetCallback(struct=%08x)", structAddr);
ERROR_LOG(HLE,"HALFIMPL sceGeSetCallback(struct=%08x)", structAddr);
PspGeCallbackData ge_callback_data;
Memory::ReadStruct(structAddr, &ge_callback_data);
@ -148,7 +148,8 @@ u32 sceGeSetCallback(u32 structAddr)
sceKernelRegisterSubIntrHandler(PSP_GE_INTR, PSP_GE_SUBINTR_FINISH, ge_callback_data.finish_func, ge_callback_data.finish_arg);
if (ge_callback_data.signal_func)
sceKernelRegisterSubIntrHandler(PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL, ge_callback_data.signal_func, ge_callback_data.signal_arg);
// TODO: This should return a callback ID
return 0;
}

View File

@ -18,6 +18,7 @@
#pragma once
#include "../../Globals.h"
#include <cstring>
enum
{

View File

@ -17,6 +17,7 @@
#pragma once
#include <cstring>
#include <string>
#include "../Globals.h"

View File

@ -656,7 +656,7 @@ namespace MIPSInt
}
colors[i] = col;
}
u32 ov[2] = {colors[0] | (colors[1] << 16), colors[2] | (colors[3] << 16)};
u32 ov[2] = {(u32)colors[0] | (colors[1] << 16), (u32)colors[2] | (colors[3] << 16)};
WriteVector((const float *)ov, V_Pair, vd);
PC += 4;
EatPrefixes();
@ -967,7 +967,7 @@ namespace MIPSInt
void Int_Vcst(u32 op)
{
static const float constants[32] =
static const float constants[32] =
{
0,
std::numeric_limits<float>::infinity(), // or max() ?? pspautotests seem to indicate inf
@ -986,9 +986,9 @@ namespace MIPSInt
(float)M_LN10,
2*(float)M_PI,
(float)M_PI/6,
log10(2.0f),
log(10.0f)/log(2.0f), //"Log2(10)",
sqrt(3.0f)/2.0f, //"Sqrt(3)/2"
log10f(2.0f),
logf(10.0f)/logf(2.0f), //"Log2(10)",
sqrtf(3.0f)/2.0f, //"Sqrt(3)/2"
};
int conNum = (op >> 16) & 0x1f;
@ -997,12 +997,12 @@ namespace MIPSInt
VectorSize sz = GetVecSize(op);
float c = constants[conNum];
float temp[4] = {c,c,c,c};
WriteVector(temp, sz, vd);
WriteVector(temp, sz, vd);
PC += 4;
EatPrefixes();
}
enum VCondition
enum VCondition
{
VC_FL,
VC_EQ,

View File

@ -18,7 +18,7 @@
#include "ABI.h"
#include "x64Emitter.h"
#include "../../Memmap.h"
#include "../../MemMap.h"
#include "../MIPS.h"
#include "../../CoreTiming.h"

View File

@ -20,7 +20,7 @@
#include "MemArena.h"
#include "ChunkFile.h"
#include "Memmap.h"
#include "MemMap.h"
#include "Core.h"
#include "MIPS/MIPS.h"
#include "MIPS/JitCommon/JitCommon.h"

View File

@ -19,7 +19,8 @@
#include "Atomic.h"
#include "MemMap.h"
// #include "../Core.h"
#include "Config.h"
#include "MIPS/MIPS.h"
// TODO: Fix this
@ -82,6 +83,10 @@ inline void ReadFromHardware(T &var, const u32 address)
else
{
WARN_LOG(MEMMAP, "ReadFromHardware: Invalid address %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
if (!g_Config.bIgnoreBadMemAccess) {
// TODO: Not sure what the best way to crash is...
exit(0);
}
var = 0;
}
}
@ -106,6 +111,10 @@ inline void WriteToHardware(u32 address, const T data)
else
{
WARN_LOG(MEMMAP, "WriteToHardware: Invalid address %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
if (!g_Config.bIgnoreBadMemAccess) {
// TODO: Not sure what the best way to crash is...
exit(0);
}
}
}

View File

@ -5,6 +5,7 @@
#include <vector>
#include <list>
#include <cstring>
// Generic allocator thingy

View File

@ -14,6 +14,8 @@ set(SRCS
set(SRCS ${SRCS})
add_library(gpu STATIC ${SRCS})
target_link_libraries(gpu general gfx_es2)
target_link_libraries(gpu general lin)
if(UNIX)
add_definitions(-fPIC)

View File

@ -25,6 +25,7 @@
#include "FragmentShaderGenerator.h"
#include "../ge_constants.h"
#include "../GPUState.h"
#include <cstdio>
// TODO: remove
static char buffer[16384];

View File

@ -18,6 +18,7 @@
#pragma once
#include "../Globals.h"
#include <cstring>
// TODO: this doesn't belong here
struct Color4

View File

@ -17,6 +17,15 @@ BASIC BUILD INSTRUCTIONS
(for more detailed instructions, see http://www.ppsspp.org/development.html )
First of all, after having checked out the source, don't forget to
run:
git submodule init
git submodule update
in order to get the "native" library.
Now, the actual building:
PPSSPP currently has three build systems, for building
for the following platforms:
* Win32: MSVC

View File

@ -23,9 +23,13 @@ add_definitions(-DSDL)
add_definitions(-Wno-multichar)
add_definitions(-fno-strict-aliasing)
add_definitions(-DUSE_PROFILER)
add_definitions(-D_DEBUG)
#Damn MacOSX
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x")
if (UNIX)
if (NOT APPLE) # can't build the SDL .m file with -std=gnu++0x
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x")
endif()
endif()
include_directories(..)
include_directories(../Common)
@ -68,18 +72,18 @@ set(FILES
../android/jni/MenuScreens.cpp
../android/jni/GamepadEmu.cpp
../android/jni/UIShader.cpp
../android/jni/ui_atlas.cpp
../android/jni/ui_atlas.cpp
../native/base/PCMain.cpp
)
if (APPLE)
SET(FILES ${FILES} SDLMain.m)
SET(FILES ${FILES} SDLMain.m)
endif (APPLE)
add_executable(ppsspp ${FILES})
target_link_libraries(ppsspp ${LIBS})
set(FILES ../headless/Headless.cpp)
add_executable(ppsspp-headless ${FILES})

View File

@ -1,6 +1,7 @@
1. Install GCC, CMake, and development libraries for zlib and sdl.
2. cd SDL
3. ./b.sh
3. ./buildassets.sh
4. ./b.sh
That should do it.

0
SDL/b.sh Normal file → Executable file
View File

0
SDL/buildassets.sh Normal file → Executable file
View File

View File

@ -6,7 +6,7 @@
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
@ -49,32 +49,32 @@
Texture *uiTexture;
ScreenManager screenManager;
ScreenManager *screenManager;
std::string config_filename;
class AndroidLogger : public LogListener
{
public:
void Log(LogTypes::LOG_LEVELS level, const char *msg)
{
switch (level)
{
case LogTypes::LDEBUG:
case LogTypes::LINFO:
ILOG("%s", msg);
break;
case LogTypes::LERROR:
ELOG("%s", msg);
break;
case LogTypes::LWARNING:
WLOG("%s", msg);
break;
case LogTypes::LNOTICE:
default:
ILOG("%s", msg);
break;
}
}
void Log(LogTypes::LOG_LEVELS level, const char *msg)
{
switch (level)
{
case LogTypes::LDEBUG:
case LogTypes::LINFO:
ILOG("%s", msg);
break;
case LogTypes::LERROR:
ELOG("%s", msg);
break;
case LogTypes::LWARNING:
WLOG("%s", msg);
break;
case LogTypes::LNOTICE:
default:
ILOG("%s", msg);
break;
}
}
};
@ -86,30 +86,30 @@ public:
// hasRendered = false;
}
virtual void UpdateUI() {}
virtual void UpdateUI() {}
virtual void UpdateMemView() {}
virtual void UpdateDisassembly() {}
virtual void UpdateMemView() {}
virtual void UpdateDisassembly() {}
virtual void SetDebugMode(bool mode) { }
virtual void SetDebugMode(bool mode) { }
virtual void InitGL() {}
virtual void BeginFrame() {}
virtual void EndFrame() {}
virtual void ShutdownGL() {}
virtual void InitGL() {}
virtual void BeginFrame() {}
virtual void EndFrame() {}
virtual void ShutdownGL() {}
virtual void InitSound(PMixer *mixer);
virtual void UpdateSound() {};
virtual void ShutdownSound();
virtual void InitSound(PMixer *mixer);
virtual void UpdateSound() {};
virtual void ShutdownSound();
// this is sent from EMU thread! Make sure that Host handles it properly!
virtual void BootDone() {}
virtual void PrepareShutdown() {}
// this is sent from EMU thread! Make sure that Host handles it properly!
virtual void BootDone() {}
virtual void PrepareShutdown() {}
virtual bool IsDebuggingEnabled() {return false;}
virtual bool AttemptLoadSymbolMap() {return false;}
virtual void ResetSymbolMap() {}
virtual void AddSymbol(std::string name, u32 addr, u32 size, int type=0) {}
virtual bool IsDebuggingEnabled() {return false;}
virtual bool AttemptLoadSymbolMap() {return false;}
virtual void ResetSymbolMap() {}
virtual void AddSymbol(std::string name, u32 addr, u32 size, int type=0) {}
};
// globals
@ -130,10 +130,10 @@ void NativeHost::ShutdownSound()
void NativeMix(short *audio, int num_samples)
{
if (g_mixer)
{
g_mixer->Mix(audio, num_samples/2);
}
if (g_mixer)
{
g_mixer->Mix(audio, num_samples/2);
}
else
{
//memset(audio, 0, numSamples * 2);
@ -149,32 +149,41 @@ void NativeGetAppInfo(std::string *app_dir_name, std::string *app_nice_name, boo
void NativeInit(int argc, const char *argv[], const char *savegame_directory, const char *external_directory, const char *installID)
{
std::string user_data_path = savegame_directory;
std::string user_data_path = savegame_directory;
// We want this to be FIRST.
VFSRegister("", new DirectoryAssetReader("assets/"));
VFSRegister("", new DirectoryAssetReader(user_data_path.c_str()));
// We want this to be FIRST.
VFSRegister("", new DirectoryAssetReader("assets/"));
VFSRegister("", new DirectoryAssetReader(user_data_path.c_str()));
host = new NativeHost();
host = new NativeHost();
logger = new AndroidLogger();
logger = new AndroidLogger();
LogManager::Init();
LogManager *logman = LogManager::GetInstance();
ILOG("Logman: %p", logman);
LogManager::Init();
LogManager *logman = LogManager::GetInstance();
ILOG("Logman: %p", logman);
if (argc > 1)
{
boot_filename = argv[1];
if (!File::Exists(boot_filename))
{
fprintf(stdout, "File not found: %s\n", boot_filename.c_str());
exit(1);
// Parse command line
LogTypes::LOG_LEVELS logLevel = LogTypes::LINFO;
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
switch (argv[i][1]) {
case 'd':
// Enable debug logging
logLevel = LogTypes::LDEBUG;
break;
}
} else {
boot_filename = argv[i];
if (!File::Exists(boot_filename))
{
fprintf(stdout, "File not found: %s\n", boot_filename.c_str());
exit(1);
}
}
}
config_filename = user_data_path + "/config.ini";
config_filename = user_data_path + "ppsspp.ini";
g_Config.Load(config_filename.c_str());
@ -186,17 +195,18 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co
#endif
}
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++)
{
LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i;
logman->SetEnable(type, true);
logman->SetLogLevel(type, LogTypes::LDEBUG);
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++)
{
LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i;
logman->SetEnable(type, true);
logman->SetLogLevel(type, logLevel);
#ifdef ANDROID
logman->AddListener(type, logger);
logman->AddListener(type, logger);
#endif
}
logman->SetLogLevel(LogTypes::G3D, LogTypes::LERROR);
INFO_LOG(BOOT, "Logger inited.");
}
// Special hack for G3D as it's very spammy. Need to make a flag for this.
logman->SetLogLevel(LogTypes::G3D, LogTypes::LERROR);
INFO_LOG(BOOT, "Logger inited.");
}
void NativeInitGraphics()
@ -205,8 +215,14 @@ void NativeInitGraphics()
gl_lost_manager_init();
ui_draw2d.SetAtlas(&ui_atlas);
screenManager.switchScreen(new LogoScreen(boot_filename));
// screenManager.switchScreen(new FileSelectScreen());
screenManager = new ScreenManager();
if (boot_filename.empty()) {
screenManager->switchScreen(new LogoScreen(boot_filename));
} else {
// Go directly into the game.
screenManager->switchScreen(new EmuScreen(boot_filename));
}
// screenManager->switchScreen(new FileSelectScreen());
UIShader_Init();
@ -223,57 +239,57 @@ void NativeInitGraphics()
uiTexture = new Texture();
if (!uiTexture->Load("ui_atlas.zim"))
{
ELOG("Failed to load texture");
}
uiTexture->Bind(0);
{
ELOG("Failed to load texture");
}
uiTexture->Bind(0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
void NativeRender()
{
glViewport(0, 0, pixel_xres, pixel_yres);
Matrix4x4 ortho;
Matrix4x4 ortho;
ortho.setOrtho(0.0f, dp_xres, dp_yres, 0.0f, -1.0f, 1.0f);
glsl_bind(UIShader_Get());
glUniformMatrix4fv(UIShader_Get()->u_worldviewproj, 1, GL_FALSE, ortho.getReadPtr());
screenManager.render();
screenManager->render();
}
void NativeUpdate(InputState &input)
{
UIUpdateMouse(0, input.pointer_x[0], input.pointer_y[0], input.pointer_down[0]);
screenManager.update(input);
screenManager->update(input);
}
void NativeDeviceLost()
{
screenManager.deviceLost();
screenManager->deviceLost();
gl_lost();
// Should dirty EVERYTHING
}
bool NativeIsAtTopLevel()
{
// TODO
return false;
// TODO
return false;
}
void NativeTouch(int finger, float x, float y, double time, TouchEvent event)
{
switch (event) {
case TOUCH_DOWN:
break;
case TOUCH_MOVE:
break;
case TOUCH_UP:
break;
}
switch (event) {
case TOUCH_DOWN:
break;
case TOUCH_MOVE:
break;
case TOUCH_UP:
break;
}
}
void NativeShutdownGraphics()
@ -281,21 +297,25 @@ void NativeShutdownGraphics()
delete uiTexture;
uiTexture = NULL;
screenManager.shutdown();
screenManager->shutdown();
delete screenManager;
screenManager = 0;
UIShader_Shutdown();
gl_lost_manager_shutdown();
gl_lost_manager_shutdown();
}
void NativeShutdown()
{
delete host;
host = 0;
LogManager::Shutdown();
delete host;
host = 0;
g_Config.Save();
LogManager::Shutdown();
// This means that the activity has been completely destroyed. PPSSPP does not
// boot up correctly with "dirty" global variables currently, so we hack around that
// by simply exiting.
#ifdef ANDROID
exit(0);
#endif
}

2
native

@ -1 +1 @@
Subproject commit d533766061b866d391ef80607a58d1e4b81643bf
Subproject commit b80e99726b5bda47f4a3165111295dc35337870d