Merge branch 'master' into translation-update-script

This commit is contained in:
Guo Yunhe 2020-01-23 12:04:19 +02:00 committed by GitHub
commit 24aa0f7fe6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
159 changed files with 5052 additions and 8372 deletions

View File

@ -1,4 +1,10 @@
# Future
- BUGFIX: Fix mouse capture hotkey not working
- CHEEVOS/BUGFIX: Achievement triggers could cause Retroarch to Crash
- CHEEVOS: Support for extended Sega CD memory
- EMSCRIPTEN: Recreate input event listeners properly
- LIBRETRO: Add disk control interface API extension
- LOCALIZATION: Update Polish translation
# 1.8.4
- ANDROID/BUGFIX: Prevent crash when Android device is minimized and you go back to the app

View File

@ -170,6 +170,7 @@ OBJ += frontend/frontend_driver.o \
intl/msg_hash_us.o \
$(LIBRETRO_COMM_DIR)/queues/task_queue.o \
tasks/task_content.o \
tasks/task_patch.o \
tasks/task_save.o \
tasks/task_file_transfer.o \
tasks/task_image.o \
@ -230,6 +231,7 @@ OBJ += \
managers/state_manager.o \
gfx/drivers_font_renderer/bitmapfont.o \
tasks/task_autodetect.o \
tasks/task_autodetect_blissbox.o \
input/input_autodetect_builtin.o \
input/input_keymaps.o \
input/input_remapping.o \
@ -241,6 +243,7 @@ OBJ += \
$(LIBRETRO_COMM_DIR)/file/config_file.o \
$(LIBRETRO_COMM_DIR)/file/config_file_userdata.o \
runtime_file.o \
disk_index_file.o \
tasks/task_screenshot.o \
tasks/task_powerstate.o \
$(LIBRETRO_COMM_DIR)/gfx/scaler/scaler.o \
@ -259,7 +262,8 @@ OBJ += \
performance_counters.o \
verbosity.o \
$(LIBRETRO_COMM_DIR)/playlists/label_sanitization.o \
manual_content_scan.o
manual_content_scan.o \
disk_control_interface.o
ifeq ($(HAVE_AUDIOMIXER), 1)
DEFINES += -DHAVE_AUDIOMIXER

View File

@ -371,7 +371,7 @@ static ssize_t coreaudio_write(void *data, const void *buf_, size_t size)
break;
}
#if TARGET_OS_IPHONE
#if TARGET_OS_IOS
if (write_avail == 0 && !scond_wait_timeout(
dev->cond, dev->lock, 3000000))
g_interrupted = true;

View File

@ -210,7 +210,7 @@ static void rcheevos_get_user_agent(char* buffer)
int major, minor;
char tmp[64];
ptr = rcheevos_user_agent_prefix + sprintf(rcheevos_user_agent_prefix, "RetroArch/" PACKAGE_VERSION);
ptr = rcheevos_user_agent_prefix + sprintf(rcheevos_user_agent_prefix, "RetroArch/%s", PACKAGE_VERSION);
if (frontend && frontend->get_os)
{
@ -2486,8 +2486,6 @@ found:
*************************************************************************/
CORO_SUB(RCHEEVOS_HTTP_GET)
rcheevos_get_user_agent(buffer);
for (coro->k = 0; coro->k < 5; coro->k++)
{
if (coro->k != 0)
@ -2513,6 +2511,7 @@ found:
continue;
}
rcheevos_get_user_agent(buffer);
net_http_connection_set_user_agent(coro->conn, buffer);
coro->http = net_http_new(coro->conn);

View File

@ -124,6 +124,7 @@ const uint8_t* rcheevos_patch_address(unsigned address, int console)
{
rarch_system_info_t* system = runloop_get_system_info();
const void* pointer = NULL;
unsigned original_address = address;
if (console == RC_CONSOLE_NINTENDO)
{
@ -208,6 +209,21 @@ const uint8_t* rcheevos_patch_address(unsigned address, int console)
address += 0x006000 - 0x020000;
}
}
else if (console == RC_CONSOLE_SEGA_CD)
{
if (address < 0x010000)
{
/* Work RAM. */
address += 0xFF0000;
CHEEVOS_LOG(RCHEEVOS_TAG "Sega CD memory address %X adjusted to %X\n", original_address, address);
}
else
{
/* CD-ROM peripheral RAM - exposed at virtual address to avoid banking */
address += 0x80020000 - 0x010000;
CHEEVOS_LOG(RCHEEVOS_TAG "Sega CD memory address %X adjusted to %X\n", original_address, address);
}
}
desc = system->mmaps.descriptors;
end = desc + system->mmaps.num_descriptors;
@ -216,19 +232,18 @@ const uint8_t* rcheevos_patch_address(unsigned address, int console)
{
if (((desc->core.start ^ address) & desc->core.select) == 0)
{
unsigned addr = address;
pointer = desc->core.ptr;
pointer = desc->core.ptr;
address -= desc->core.start;
address = (unsigned)rcheevos_var_reduce(
(addr - desc->core.start) & desc->disconnect_mask,
desc->core.disconnect);
if (desc->disconnect_mask)
address = (unsigned)rcheevos_var_reduce(address & desc->disconnect_mask, desc->core.disconnect);
if (address >= desc->core.len)
address -= rcheevos_var_highest_bit(address);
address += desc->core.offset;
CHEEVOS_LOG(RCHEEVOS_TAG "address %X set to descriptor %d at offset %X\n", addr, (int)((desc - system->mmaps.descriptors) + 1), address);
CHEEVOS_LOG(RCHEEVOS_TAG "address %X set to descriptor %d at offset %X\n", original_address, (int)((desc - system->mmaps.descriptors) + 1), address);
break;
}
}
@ -236,7 +251,6 @@ const uint8_t* rcheevos_patch_address(unsigned address, int console)
else
{
unsigned i;
unsigned addr = address;
for (i = 0; i < 4; i++)
{
@ -260,10 +274,9 @@ const uint8_t* rcheevos_patch_address(unsigned address, int console)
core_get_memory(&meminfo);
if (addr < meminfo.size)
if (address < meminfo.size)
{
pointer = meminfo.data;
address = addr;
break;
}
@ -272,15 +285,15 @@ const uint8_t* rcheevos_patch_address(unsigned address, int console)
* it's size is not always set correctly in the core.
*/
if (i == 0 && console == RC_CONSOLE_NINTENDO)
addr -= 0x6000;
address -= 0x6000;
else
addr -= meminfo.size;
address -= meminfo.size;
}
}
if (pointer == NULL)
{
CHEEVOS_LOG(RCHEEVOS_TAG "address %X not supported\n", address);
CHEEVOS_LOG(RCHEEVOS_TAG "address %X not supported\n", original_address);
return NULL;
}

View File

@ -1341,7 +1341,6 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
SETTING_BOOL("frame_time_counter_reset_after_load_state", &settings->bools.frame_time_counter_reset_after_load_state, true, false, false);
SETTING_BOOL("frame_time_counter_reset_after_save_state", &settings->bools.frame_time_counter_reset_after_save_state, true, false, false);
SETTING_BOOL("crt_switch_resolution_use_custom_refresh_rate", &settings->bools.crt_switch_custom_refresh_enable, true, false, false);
SETTING_BOOL("automatically_add_content_to_playlist", &settings->bools.automatically_add_content_to_playlist, true, DEFAULT_AUTOMATICALLY_ADD_CONTENT_TO_PLAYLIST, false);
SETTING_BOOL("ui_companion_start_on_boot", &settings->bools.ui_companion_start_on_boot, true, ui_companion_start_on_boot, false);
SETTING_BOOL("ui_companion_enable", &settings->bools.ui_companion_enable, true, ui_companion_enable, false);
SETTING_BOOL("ui_companion_toggle", &settings->bools.ui_companion_toggle, false, ui_companion_toggle, false);

View File

@ -361,7 +361,6 @@ typedef struct settings
bool bluetooth_enable;
bool localap_enable;
bool automatically_add_content_to_playlist;
bool video_window_show_decorations;
bool video_window_save_positions;

5
core.h
View File

@ -25,6 +25,7 @@
#include "core_type.h"
#include "input/input_defines.h"
#include "disk_control_interface.h"
RETRO_BEGIN_DECLS
@ -67,8 +68,8 @@ typedef struct rarch_system_info
bool supports_vfs;
struct retro_disk_control_callback disk_control_cb;
struct retro_location_callback location_cb;
disk_control_interface_t disk_control;
struct retro_location_callback location_cb;
struct
{

View File

@ -16,7 +16,7 @@ struct packet_buffer
packet_buffer_t *packet_buffer_create()
{
packet_buffer_t *b = malloc(sizeof(packet_buffer_t));
packet_buffer_t *b = (packet_buffer_t*)malloc(sizeof(packet_buffer_t));
if (!b)
return NULL;

View File

@ -25,14 +25,14 @@ struct video_buffer
video_buffer_t *video_buffer_create(size_t capacity, int frame_size, int width, int height)
{
video_buffer_t *b = malloc(sizeof(video_buffer_t));
video_buffer_t *b = (video_buffer_t*)malloc(sizeof(video_buffer_t));
if (!b)
return NULL;
memset(b, 0, sizeof(video_buffer_t));
b->capacity = capacity;
b->status = malloc(sizeof(enum kbStatus) * capacity);
b->status = (enum kbStatus*)malloc(sizeof(enum kbStatus) * capacity);
if (!b->status)
goto fail;
for (int i = 0; i < capacity; i++)
@ -44,21 +44,21 @@ video_buffer_t *video_buffer_create(size_t capacity, int frame_size, int width,
if (!b->lock || !b->open_cond || !b->finished_cond)
goto fail;
b->buffer = malloc(sizeof(video_decoder_context_t) * capacity);
b->buffer = (video_decoder_context_t*)malloc(sizeof(video_decoder_context_t) * capacity);
if (!b->buffer)
goto fail;
for (int i = 0; i < capacity; i++)
{
b->buffer[i].index = i;
b->buffer[i].pts = 0;
b->buffer[i].sws = sws_alloc_context();
b->buffer[i].source = av_frame_alloc();
b->buffer[i].index = i;
b->buffer[i].pts = 0;
b->buffer[i].sws = sws_alloc_context();
b->buffer[i].source = av_frame_alloc();
#if LIBAVUTIL_VERSION_MAJOR > 55
b->buffer[i].hw_source = av_frame_alloc();
#endif
b->buffer[i].target = av_frame_alloc();
b->buffer[i].frame_buf = av_malloc(frame_size);
b->buffer[i].target = av_frame_alloc();
b->buffer[i].frame_buf = (uint8_t*)av_malloc(frame_size);
avpicture_fill((AVPicture*)b->buffer[i].target, (const uint8_t*)b->buffer[i].frame_buf,
PIX_FMT_RGB32, width, height);

View File

@ -1,92 +0,0 @@
---
AccessModifierOffset: -4
AlignAfterOpenBracket: true
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: false
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Stroustrup
BreakBeforeInheritanceComma: true
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakStringLiterals: true
ColumnLimit: 100
CommentPragmas: ''
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
FixNamespaceComments: true
ForEachMacros: []
IndentCaseLabels: false
IncludeCategories:
- Regex: '^("|<)stdafx\.h(pp)?("|>)'
Priority: -1
- Regex: '^<(W|w)indows.h>'
Priority: 1
- Regex: '^<'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '(_test|_win|_linux|_mac|_ios|_osx|_null)?$'
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
PenaltyBreakAssignment: 0
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 9999999
PointerAlignment: Left
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: true
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Never
---
Language: Cpp
---
Language: ObjC
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: false
---
Language: Java
BasedOnStyle: Google
BreakAfterJavaFieldAnnotations: true
...

View File

@ -1,5 +0,0 @@
/build*/
/.vscode/
/thirdparty/
.vs/
.DS_Store

View File

@ -1,47 +0,0 @@
language: cpp
env:
global:
- CLANG_FORMAT_SUFFIX="-dummy" # don't use formatting on Travis, this is
# needed not to use default 3.5 version
# which is too old.
matrix:
include:
- os: linux
env: MATRIX_EVAL="CC=gcc-5 && CXX=g++-5"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-5
- os: linux
env: MATRIX_EVAL="CC=clang-4.0 && CXX=clang++-4.0"
addons:
apt:
sources:
- llvm-toolchain-trusty-4.0
packages:
- clang-4.0
- os: linux
env: MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0"
addons:
apt:
sources:
- llvm-toolchain-trusty-5.0
packages:
- clang-5.0
- os: osx
osx_image: xcode9
# prevent Travis from overwriting our CXX variables
before_install:
- eval "${MATRIX_EVAL}"
- echo $CXX
script:
- mkdir build
- cd build
- cmake -DCLANG_FORMAT_SUFFIX=$CLANG_FORMAT_SUFFIX --config Release ..
- cmake --build . -- -j2

View File

@ -1,56 +0,0 @@
cmake_minimum_required (VERSION 3.2.0)
project (DiscordRPC)
include(GNUInstallDirs)
option(BUILD_EXAMPLES "Build example apps" ON)
# format
file(GLOB_RECURSE ALL_SOURCE_FILES
examples/*.cpp examples/*.h examples/*.c
include/*.h
src/*.cpp src/*.h src/*.c
)
# Set CLANG_FORMAT_SUFFIX if you are using custom clang-format, e.g. clang-format-5.0
find_program(CLANG_FORMAT_CMD clang-format${CLANG_FORMAT_SUFFIX})
if (CLANG_FORMAT_CMD)
add_custom_target(
clangformat
COMMAND ${CLANG_FORMAT_CMD}
-i -style=file -fallback-style=none
${ALL_SOURCE_FILES}
DEPENDS
${ALL_SOURCE_FILES}
)
endif(CLANG_FORMAT_CMD)
# thirdparty stuff
execute_process(
COMMAND mkdir ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty
ERROR_QUIET
)
find_file(RAPIDJSONTEST NAMES rapidjson rapidjson-1.1.0 PATHS ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty CMAKE_FIND_ROOT_PATH_BOTH)
if (NOT RAPIDJSONTEST)
message("no rapidjson, download")
set(RJ_TAR_FILE ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/v1.1.0.tar.gz)
file(DOWNLOAD https://github.com/miloyip/rapidjson/archive/v1.1.0.tar.gz ${RJ_TAR_FILE})
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xzf ${RJ_TAR_FILE}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty
)
file(REMOVE ${RJ_TAR_FILE})
endif(NOT RAPIDJSONTEST)
find_file(RAPIDJSON NAMES rapidjson rapidjson-1.1.0 PATHS ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty CMAKE_FIND_ROOT_PATH_BOTH)
add_library(rapidjson STATIC IMPORTED ${RAPIDJSON})
# add subdirs
add_subdirectory(src)
if (BUILD_EXAMPLES)
add_subdirectory(examples/send-presence)
endif(BUILD_EXAMPLES)

View File

@ -1,91 +0,0 @@
# Discord RPC
This is a library for interfacing your game with a locally running Discord desktop client. It's known to work on Windows, macOS, and Linux. You can use the lib directly if you like, or use it as a guide to writing your own if it doesn't suit your game as is. PRs/feedback welcome if you have an improvement everyone might want, or can describe how this doesn't meet your needs.
Included here are some quick demos that implement the very minimal subset to show current status, and
have callbacks for where a more complete game would do more things (joining, spectating, etc).
## Documentation
The most up to date documentation for Rich Presence can always be found on our [developer site](https://discordapp.com/developers/docs/rich-presence/how-to)! If you're interested in rolling your own native implementation of Rich Presence via IPC sockets instead of using our SDK—hey, you've got free time, right?—check out the ["Hard Mode" documentation](https://github.com/discordapp/discord-rpc/blob/master/documentation/hard-mode.md).
## Basic Usage
Zeroith, you should be set up to build things because you are a game developer, right?
First, head on over to the [Discord developers site](https://discordapp.com/developers/applications/me) and make yourself an app. Keep track of `Client ID` -- you'll need it here to pass to the init function.
### From package
Download a release package for your platform(s) -- they have subdirs with various prebuilt options, select the one you need add `/include` to your compile includes, `/lib` to your linker paths, and link with `discord-rpc`. For the dynamically linked builds, you'll need to ship the associated file along with your game.
### From repo
First-eth, you'll want `CMake`. There's a few different ways to install it on your system, and you should refer to [their website](https://cmake.org/install/). Many package managers provide ways of installing CMake as well.
To make sure it's installed correctly, type `cmake --version` into your flavor of terminal/cmd. If you get a response with a version number, you're good to go!
There's a [CMake](https://cmake.org/download/) file that should be able to generate the lib for you; Sometimes I use it like this:
```sh
cd <path to discord-rpc>
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=<path to install discord-rpc to>
cmake --build . --config Release --target install
```
There is a wrapper build script `build.py` that runs `cmake` with a few different options.
Usually, I run `build.py` to get things started, then use the generated project files as I work on things. It does depend on `click` library, so do a quick `pip install click` to make sure you have it if you want to run `build.py`.
There are some CMake options you might care about:
| flag | default | does |
|------|---------|------|
| `ENABLE_IO_THREAD` | `ON` | When enabled, we start up a thread to do io processing, if disabled you should call `Discord_UpdateConnection` yourself.
| `USE_STATIC_CRT` | `OFF` | (Windows) Enable to statically link the CRT, avoiding requiring users install the redistributable package. (The prebuilt binaries enable this option)
| [`BUILD_SHARED_LIBS`](https://cmake.org/cmake/help/v3.7/variable/BUILD_SHARED_LIBS.html) | `OFF` | Build library as a DLL
| `WARNINGS_AS_ERRORS` | `OFF` | When enabled, compiles with `-Werror` (on *nix platforms). |
## Continuous Builds
Why do we have three of these? Three times the fun!
| CI | badge |
|----|-------|
| TravisCI | [![Build status](https://travis-ci.org/discordapp/discord-rpc.svg?branch=master)](https://travis-ci.org/discordapp/discord-rpc)
| AppVeyor | [![Build status](https://ci.appveyor.com/api/projects/status/qvkoc0w1c4f4b8tj?svg=true)](https://ci.appveyor.com/project/crmarsh/discord-rpc)
| Buildkite (internal) | [![Build status](https://badge.buildkite.com/e103d79d247f6776605a15246352a04b8fd83d69211b836111.svg)](https://buildkite.com/discord/discord-rpc)
## Sample: send-presence
This is a text adventure "game" that inits/deinits the connection to Discord, and sends a presence update on each command.
## Sample: button-clicker
This is a sample [Unity](https://unity3d.com/) project that wraps a DLL version of the library, and sends presence updates when you click on a button. Run `python build.py unity` in the root directory to build the correct library files and place them in their respective folders.
## Sample: unrealstatus
This is a sample [Unreal](https://www.unrealengine.com) project that wraps the DLL version of the library with an Unreal plugin, exposes a blueprint class for interacting with it, and uses that to make a very simple UI. Run `python build.py unreal` in the root directory to build the correct library files and place them in their respective folders.
## Wrappers and Implementations
Below is a table of unofficial, community-developed wrappers for and implementations of Rich Presence in various languages. If you would like to have yours added, please make a pull request adding your repository to the table. The repository should include:
- The code
- A brief ReadMe of how to use it
- A working example
###### Rich Presence Wrappers and Implementations
| Name | Language |
|------|----------|
| [DerelictDiscordRPC](https://github.com/voidblaster/DerelictDiscordRPC) | [D](https://dlang.org/) |
| [discord-rpc.jar](https://github.com/Vatuu/discord-rpc "Discord-RPC.jar") | Java |
| [java-discord-rpc](https://github.com/MinnDevelopment/java-discord-rpc) | Java |
| [Discord-IPC](https://github.com/jagrosh/DiscordIPC) | Java |
| [Discord Rich Presence](https://npmjs.org/discord-rich-presence) | JavaScript |
| [drpc4k](https://github.com/Bluexin/drpc4k) | [Kotlin](https://kotlinlang.org/) |
| [lua-discordRPC](https://github.com/pfirsich/lua-discordRPC) | LuaJIT (FFI) |
| [pypresence](https://github.com/qwertyquerty/pypresence) | [Python](https://python.org/) |
| [SwordRPC](https://github.com/Azoy/SwordRPC) | [Swift](https://swift.org) |

View File

@ -1,17 +0,0 @@
version: '{build}'
install:
- python -m pip install click
build_script:
- mkdir examples\unrealstatus\Plugins\discordrpc\Binaries\ThirdParty\discordrpcLibrary\Win64
- python build.py
artifacts:
- path: builds\install\win32-dynamic
name: win32-dynamic
- path: builds\install\win32-static
name: win32-static
- path: builds\install\win64-dynamic
name: win64-dynamic
- path: builds\install\win64-static
name: win64-static

View File

@ -1,304 +0,0 @@
#!/usr/bin/env python
import os
import subprocess
import sys
import shutil
import zipfile
from contextlib import contextmanager
import click
def get_platform():
""" a name for the platform """
if sys.platform.startswith('win'):
return 'win'
elif sys.platform == 'darwin':
return 'osx'
elif sys.platform.startswith('linux'):
return 'linux'
raise Exception('Unsupported platform ' + sys.platform)
SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__))
# we use Buildkite which sets this env variable by default
IS_BUILD_MACHINE = os.environ.get('CI', '') == 'true'
PLATFORM = get_platform()
INSTALL_ROOT = os.path.join(SCRIPT_PATH, 'builds', 'install')
def get_signtool():
""" get path to code signing tool """
if PLATFORM == 'win':
sdk_dir = 'c:\\Program Files (x86)\\Windows Kits\\10' # os.environ['WindowsSdkDir']
return os.path.join(sdk_dir, 'bin', 'x86', 'signtool.exe')
elif PLATFORM == 'osx':
return '/usr/bin/codesign'
@contextmanager
def cd(new_dir):
""" Temporarily change current directory """
if new_dir:
old_dir = os.getcwd()
os.chdir(new_dir)
yield
if new_dir:
os.chdir(old_dir)
def mkdir_p(path):
""" mkdir -p """
if not os.path.isdir(path):
click.secho('Making ' + path, fg='yellow')
os.makedirs(path)
@click.group(invoke_without_command=True)
@click.pass_context
@click.option('--clean', is_flag=True)
def cli(ctx, clean):
""" click wrapper for command line stuff """
if ctx.invoked_subcommand is None:
ctx.invoke(libs, clean=clean)
if IS_BUILD_MACHINE:
ctx.invoke(sign)
ctx.invoke(archive)
@cli.command()
@click.pass_context
def unity(ctx):
""" build just dynamic libs for use in unity project """
ctx.invoke(libs, clean=False, static=False, shared=True, skip_formatter=True, just_release=True)
BUILDS = []
click.echo('--- Copying libs and header into unity example')
UNITY_PROJECT_PATH = os.path.join(SCRIPT_PATH, 'examples', 'button-clicker', 'Assets', 'Plugins')
if sys.platform.startswith('win'):
LIBRARY_NAME = 'discord-rpc.dll'
BUILD_64_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'win64-dynamic', 'src', 'Release')
UNITY_64_DLL_PATH = os.path.join(UNITY_PROJECT_PATH, 'x86_64')
BUILDS.append({BUILD_64_BASE_PATH: UNITY_64_DLL_PATH})
BUILD_32_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'win32-dynamic', 'src', 'Release')
UNITY_32_DLL_PATH = os.path.join(UNITY_PROJECT_PATH, 'x86')
BUILDS.append({BUILD_32_BASE_PATH: UNITY_32_DLL_PATH})
elif sys.platform == 'darwin':
LIBRARY_NAME = 'discord-rpc.bundle'
BUILD_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'osx-dynamic', 'src')
UNITY_DLL_PATH = UNITY_PROJECT_PATH
os.rename(
os.path.join(BUILD_BASE_PATH, 'libdiscord-rpc.dylib'), os.path.join(BUILD_BASE_PATH, 'discord-rpc.bundle'))
BUILDS.append({BUILD_BASE_PATH: UNITY_DLL_PATH})
elif sys.platform.startswith('linux'):
LIBRARY_NAME = 'discord-rpc.so'
BUILD_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'linux-dynamic', 'src')
UNITY_DLL_PATH = os.path.join(UNITY_PROJECT_PATH, 'x86')
os.rename(os.path.join(BUILD_BASE_PATH, 'libdiscord-rpc.so'), os.path.join(BUILD_BASE_PATH, 'discord-rpc.so'))
BUILDS.append({BUILD_BASE_PATH: UNITY_DLL_PATH})
else:
raise Exception('Unsupported platform ' + sys.platform)
for build in BUILDS:
for i in build:
mkdir_p(build[i])
shutil.copy(os.path.join(i, LIBRARY_NAME), build[i])
@cli.command()
@click.pass_context
def unreal(ctx):
""" build libs and copy them into the unreal project """
ctx.invoke(libs, clean=False, static=False, shared=True, skip_formatter=True, just_release=True)
BUILDS = []
click.echo('--- Copying libs and header into unreal example')
UNREAL_PROJECT_PATH = os.path.join(SCRIPT_PATH, 'examples', 'unrealstatus', 'Plugins', 'discordrpc')
UNREAL_INCLUDE_PATH = os.path.join(UNREAL_PROJECT_PATH, 'Source', 'ThirdParty', 'DiscordRpcLibrary', 'Include')
mkdir_p(UNREAL_INCLUDE_PATH)
shutil.copy(os.path.join(SCRIPT_PATH, 'include', 'discord_rpc.h'), UNREAL_INCLUDE_PATH)
if sys.platform.startswith('win'):
LIBRARY_NAME = 'discord-rpc.lib'
BUILD_64_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'win64-dynamic', 'src', 'Release')
UNREAL_64_DLL_PATH = os.path.join(UNREAL_PROJECT_PATH, 'Source', 'ThirdParty', 'DiscordRpcLibrary', 'Win64')
BUILDS.append({BUILD_64_BASE_PATH: UNREAL_64_DLL_PATH})
BUILD_32_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'win32-dynamic', 'src', 'Release')
UNREAL_32_DLL_PATH = os.path.join(UNREAL_PROJECT_PATH, 'Source', 'ThirdParty', 'DiscordRpcLibrary', 'Win32')
BUILDS.append({BUILD_32_BASE_PATH: UNREAL_32_DLL_PATH})
elif sys.platform == 'darwin':
LIBRARY_NAME = 'libdiscord-rpc.dylib'
BUILD_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'osx-dynamic', 'src')
UNREAL_DLL_PATH = os.path.join(UNREAL_PROJECT_PATH, 'Source', 'ThirdParty', 'DiscordRpcLibrary', 'Mac')
BUILDS.append({BUILD_BASE_PATH: UNREAL_DLL_PATH})
elif sys.platform.startswith('linux'):
LIBRARY_NAME = 'libdiscord-rpc.so'
BUILD_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'linux-dynamic', 'src')
UNREAL_DLL_PATH = os.path.join(UNREAL_PROJECT_PATH, 'Source', 'ThirdParty', 'DiscordRpcLibrary', 'Linux')
BUILDS.append({BUILD_BASE_PATH: UNREAL_DLL_PATH})
else:
raise Exception('Unsupported platform ' + sys.platform)
for build in BUILDS:
for i in build:
mkdir_p(build[i])
shutil.copy(os.path.join(i, LIBRARY_NAME), build[i])
def build_lib(build_name, generator, options, just_release):
""" Create a dir under builds, run build and install in it """
build_path = os.path.join(SCRIPT_PATH, 'builds', build_name)
install_path = os.path.join(INSTALL_ROOT, build_name)
mkdir_p(build_path)
mkdir_p(install_path)
with cd(build_path):
initial_cmake = ['cmake', SCRIPT_PATH, '-DCMAKE_INSTALL_PREFIX=%s' % os.path.join('..', 'install', build_name)]
if generator:
initial_cmake.extend(['-G', generator])
for key in options:
val = options[key]
if type(val) is bool:
val = 'ON' if val else 'OFF'
initial_cmake.append('-D%s=%s' % (key, val))
click.echo('--- Building ' + build_name)
subprocess.check_call(initial_cmake)
if not just_release:
subprocess.check_call(['cmake', '--build', '.', '--config', 'Debug'])
subprocess.check_call(['cmake', '--build', '.', '--config', 'Release', '--target', 'install'])
@cli.command()
def archive():
""" create zip of install dir """
click.echo('--- Archiving')
archive_file_path = os.path.join(SCRIPT_PATH, 'builds', 'discord-rpc-%s.zip' % get_platform())
archive_file = zipfile.ZipFile(archive_file_path, 'w', zipfile.ZIP_DEFLATED)
archive_src_base_path = INSTALL_ROOT
archive_dst_base_path = 'discord-rpc'
with cd(archive_src_base_path):
for path, _, filenames in os.walk('.'):
for fname in filenames:
fpath = os.path.join(path, fname)
dst_path = os.path.normpath(os.path.join(archive_dst_base_path, fpath))
click.echo('Adding ' + dst_path)
archive_file.write(fpath, dst_path)
@cli.command()
def sign():
""" Do code signing within install directory using our cert """
tool = get_signtool()
signable_extensions = set()
if PLATFORM == 'win':
signable_extensions.add('.dll')
sign_command_base = [
tool,
'sign',
'/n',
'Discord Inc.',
'/a',
'/tr',
'http://timestamp.digicert.com/rfc3161',
'/as',
'/td',
'sha256',
'/fd',
'sha256',
]
elif PLATFORM == 'osx':
signable_extensions.add('.dylib')
sign_command_base = [
tool,
'--keychain',
os.path.expanduser('~/Library/Keychains/login.keychain'),
'-vvvv',
'--deep',
'--force',
'--sign',
'Developer ID Application: Hammer & Chisel Inc. (53Q6R32WPB)',
]
else:
click.secho('Not signing things on this platform yet', fg='red')
return
click.echo('--- Signing')
for path, _, filenames in os.walk(INSTALL_ROOT):
for fname in filenames:
ext = os.path.splitext(fname)[1]
if ext not in signable_extensions:
continue
fpath = os.path.join(path, fname)
click.echo('Sign ' + fpath)
sign_command = sign_command_base + [fpath]
subprocess.check_call(sign_command)
@cli.command()
@click.option('--clean', is_flag=True)
@click.option('--static', is_flag=True)
@click.option('--shared', is_flag=True)
@click.option('--skip_formatter', is_flag=True)
@click.option('--just_release', is_flag=True)
def libs(clean, static, shared, skip_formatter, just_release):
""" Do all the builds for this platform """
if clean:
shutil.rmtree('builds', ignore_errors=True)
mkdir_p('builds')
if not (static or shared):
static = True
shared = True
static_options = {}
dynamic_options = {
'BUILD_SHARED_LIBS': True,
'USE_STATIC_CRT': True,
}
if skip_formatter or IS_BUILD_MACHINE:
static_options['CLANG_FORMAT_SUFFIX'] = 'none'
dynamic_options['CLANG_FORMAT_SUFFIX'] = 'none'
if IS_BUILD_MACHINE:
just_release = True
static_options['WARNINGS_AS_ERRORS'] = True
dynamic_options['WARNINGS_AS_ERRORS'] = True
if PLATFORM == 'win':
generator32 = 'Visual Studio 14 2015'
generator64 = 'Visual Studio 14 2015 Win64'
if static:
build_lib('win32-static', generator32, static_options, just_release)
build_lib('win64-static', generator64, static_options, just_release)
if shared:
build_lib('win32-dynamic', generator32, dynamic_options, just_release)
build_lib('win64-dynamic', generator64, dynamic_options, just_release)
elif PLATFORM == 'osx':
if static:
build_lib('osx-static', None, static_options, just_release)
if shared:
build_lib('osx-dynamic', None, dynamic_options, just_release)
elif PLATFORM == 'linux':
if static:
build_lib('linux-static', None, static_options, just_release)
if shared:
build_lib('linux-dynamic', None, dynamic_options, just_release)
if __name__ == '__main__':
os.chdir(SCRIPT_PATH)
sys.exit(cli())

View File

@ -1,164 +0,0 @@
# Hard Mode: Roll Your Own Client
Discord's Rich Presence feature is designed as an obfuscated addition to our existing [RPC infrastructure](https://discordapp.com/developers/docs/topics/rpc). The standalone library and header files make it easy for any dev to drop it into their game.
Our library communicates with Discord over the local Discord RPC socket. We've already done the work in connecting properly, handling disconnects and reconnects, and other RPC intracacies, but those who have done this implementation for our private alpha Voice and Chat SDK can simply make use of the new RPC commands and events to implement Rich Presence.
## Hark! A warning!
By committing to an RPC-only integration, you decide to forego the work our library and header file have done for you in the way of error handling, state storage, disconnecting and reconnecting, and other quality of life abstractions. While simply implementing the new RPC command and events will enable Rich Presence for your game, we highly suggest that you do your best to mimic the functionality of the SDK the most that you can. It ensure not only code quality on your part, but also an excellent experience on the part of your players.
## Application Protocol Registration
One thing that cannot be explicitly done over RPC is registering an application protocol for your game. If you choose to do an RPC-only implementation, you will have to register your application protocol yourself in the format of `discord-[your_app_id]://`. You can use `Discord_Register()` as a good(?) example of how to properly register an application protocol for use with Discord. For OSX and Linux it is probably simpler to handle the protocol registration as part of your install/packaging.
## New RPC Command
The new RPC command for Rich Presence is `SET_ACTIVITY`. The fields are similar to what is outlined in the SDK; we've combined similar fields into objects for the sake of less data on the wire.
The one major difference is the `party.size` field. It is an array with a size of two. The first element is the current party size, `partySize` from the main documentation. The second element is the maximum party size, `partyMax` from the main documentation.
Below is a full example of a `SET_ACTIVITY` command. Field restrictions like size are the same as outlined in the main documentation.
```
{
"cmd": "SET_ACTIVITY",
"args": {
"pid": 9999, // Your application's process id - required field
"activity": {
"state": "In a Group",
"details": "Competitive | In a Match",
"timestamps": {
"start": time(nullptr),
"end": time(nullptr) + ((60 * 5) + 23)
},
"assets": {
"large_image": "numbani_map",
"large_text": "Numbani",
"small_image": "pharah_profile",
"small_text": "Pharah"
},
"party": {
"id": GameEngine.GetPartyId(),
"size": [3, 6]
},
"secrets": {
"join": "025ed05c71f639de8bfaa0d679d7c94b2fdce12f",
"spectate": "e7eb30d2ee025ed05c71ea495f770b76454ee4e0",
"match": "4b2fdce12f639de8bfa7e3591b71a0d679d7c93f"
},
"instance": true
}
},
"nonce": "647d814a-4cf8-4fbb-948f-898abd24f55b"
}
```
## New RPC Events
The three new RPC events for Rich Presence power the ability to join and spectate your friends' games.
First is the `ACTIVITY_JOIN` event:
```json
{
"cmd": "DISPATCH",
"data": {
"secret": "025ed05c71f639de8bfaa0d679d7c94b2fdce12f"
},
"evt": "ACTIVITY_JOIN"
}
```
Second is the `ACTIVITY_SPECTATE` event:
```json
{
"cmd": "DISPATCH",
"data": {
"secret": "e7eb30d2ee025ed05c71ea495f770b76454ee4e0"
},
"evt": "ACTIVITY_SPECTATE"
}
```
And third is the `ACTIVITY_JOIN_REQUEST` event:
```json
{
"cmd": "DISPATCH",
"data": {
"user": {
"id": "53908232506183680",
"username": "Mason",
"discriminator": "1337",
"avatar": "a_bab14f271d565501444b2ca3be944b25"
}
},
"evt": "ACTIVITY_JOIN_REQUEST"
}
```
In order to receive these events, you need to [subscribe](https://discordapp.com/developers/docs/topics/rpc#subscribe) to them like so:
```json
{
"nonce": "be9a6de3-31d0-4767-a8e9-4818c5690015",
"evt": "ACTIVITY_JOIN",
"cmd": "SUBSCRIBE"
}
```
```json
{
"nonce": "ae9qdde3-31d0-8989-a8e9-dnakwy174he",
"evt": "ACTIVITY_SPECTATE",
"cmd": "SUBSCRIBE"
}
```
```json
{
"nonce": "5dc0c062-98c6-47a0-8922-bbb52e9d6afa",
"evt": "ACTIVITY_JOIN_REQUEST",
"cmd": "SUBSCRIBE"
}
```
To unsubscribe from these events, resend with the command `UNSUBSCRIBE`
## Responding
A discord user will request access to the game. If the ACTIVITY_JOIN_REQUEST has been subscribed too, the ACTIVITY_JOIN_REQUEST event will be sent to the host's game. Accept it with following model:
```json
{
"nonce": "5dc0c062-98c6-47a0-8922-15aerg126",
"cmd": "SEND_ACTIVITY_JOIN_INVITE",
"args":
{
"user_id": "53908232506183680"
}
}
```
To reject the request, use `CLOSE_ACTIVITY_REQUEST`:
```json
{
"nonce": "5dc0c062-98c6-47a0-8922-dasg256eafg",
"cmd": "CLOSE_ACTIVITY_REQUEST",
"args":
{
"user_id": "53908232506183680"
}
}
```
## Notes
Here are just some quick notes to help with some common troubleshooting problems.
* IPC will echo back every command you send as a response. Use this as a lock-step feature to avoid flooding messages. Can be used to validate messages such as the Presence or Subscribes.
* The pipe expects for frames to be written in a single byte array. You cannot do multiple `stream.Write(opcode);` `stream.Write(length);` as it will break the pipe. Instead create a buffer, write the data to the buffer, then send the entire buffer to the stream.
* Discord can be on any pipe ranging from `discord-ipc-0` to `discord-ipc-9`. It is a good idea to try and connect to each one and keeping the first one you connect too. For multiple clients (eg Discord and Canary), you might want to add a feature to manually select the pipe so you can more easily debug the application.
* All enums are `lower_snake_case`.
* The opcode and length in the header are `Little Endian Unsigned Integers (32bits)`. In some languages, you must convert them as they can be architecture specific.
* [Discord Rich Presence How-To](https://discordapp.com/developers/docs/rich-presence/how-to) contains a lot of the information this document doesn't. For example, it will tell you about the response payload.
* In the documentation, DISCORD_REPLY_IGNORE is just implemented the same as DISCORD_REPLY_NO.
* You can test the Join / Spectate feature by enabling them in your profile and whitelisting a test account. Use Canary to run 2 accounts on the same machine.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,8 +0,0 @@
/Library/
/Temp/
/obj/
/Assets/Plugins/
/Assets/Plugins.meta
*.sln
*.csproj
*.userprefs

View File

@ -1,129 +0,0 @@
using UnityEngine;
[System.Serializable]
public class DiscordJoinEvent : UnityEngine.Events.UnityEvent<string> { }
[System.Serializable]
public class DiscordSpectateEvent : UnityEngine.Events.UnityEvent<string> { }
[System.Serializable]
public class DiscordJoinRequestEvent : UnityEngine.Events.UnityEvent<DiscordRpc.DiscordUser> { }
public class DiscordController : MonoBehaviour
{
public DiscordRpc.RichPresence presence = new DiscordRpc.RichPresence();
public string applicationId;
public string optionalSteamId;
public int callbackCalls;
public int clickCounter;
public DiscordRpc.DiscordUser joinRequest;
public UnityEngine.Events.UnityEvent onConnect;
public UnityEngine.Events.UnityEvent onDisconnect;
public UnityEngine.Events.UnityEvent hasResponded;
public DiscordJoinEvent onJoin;
public DiscordJoinEvent onSpectate;
public DiscordJoinRequestEvent onJoinRequest;
DiscordRpc.EventHandlers handlers;
public void OnClick()
{
Debug.Log("Discord: on click!");
clickCounter++;
presence.details = string.Format("Button clicked {0} times", clickCounter);
DiscordRpc.UpdatePresence(presence);
}
public void RequestRespondYes()
{
Debug.Log("Discord: responding yes to Ask to Join request");
DiscordRpc.Respond(joinRequest.userId, DiscordRpc.Reply.Yes);
hasResponded.Invoke();
}
public void RequestRespondNo()
{
Debug.Log("Discord: responding no to Ask to Join request");
DiscordRpc.Respond(joinRequest.userId, DiscordRpc.Reply.No);
hasResponded.Invoke();
}
public void ReadyCallback(ref DiscordRpc.DiscordUser connectedUser)
{
++callbackCalls;
Debug.Log(string.Format("Discord: connected to {0}#{1}: {2}", connectedUser.username, connectedUser.discriminator, connectedUser.userId));
onConnect.Invoke();
}
public void DisconnectedCallback(int errorCode, string message)
{
++callbackCalls;
Debug.Log(string.Format("Discord: disconnect {0}: {1}", errorCode, message));
onDisconnect.Invoke();
}
public void ErrorCallback(int errorCode, string message)
{
++callbackCalls;
Debug.Log(string.Format("Discord: error {0}: {1}", errorCode, message));
}
public void JoinCallback(string secret)
{
++callbackCalls;
Debug.Log(string.Format("Discord: join ({0})", secret));
onJoin.Invoke(secret);
}
public void SpectateCallback(string secret)
{
++callbackCalls;
Debug.Log(string.Format("Discord: spectate ({0})", secret));
onSpectate.Invoke(secret);
}
public void RequestCallback(ref DiscordRpc.DiscordUser request)
{
++callbackCalls;
Debug.Log(string.Format("Discord: join request {0}#{1}: {2}", request.username, request.discriminator, request.userId));
joinRequest = request;
onJoinRequest.Invoke(request);
}
void Start()
{
}
void Update()
{
DiscordRpc.RunCallbacks();
}
void OnEnable()
{
Debug.Log("Discord: init");
callbackCalls = 0;
handlers = new DiscordRpc.EventHandlers();
handlers.readyCallback = ReadyCallback;
handlers.disconnectedCallback += DisconnectedCallback;
handlers.errorCallback += ErrorCallback;
handlers.joinCallback += JoinCallback;
handlers.spectateCallback += SpectateCallback;
handlers.requestCallback += RequestCallback;
DiscordRpc.Initialize(applicationId, ref handlers, true, optionalSteamId);
}
void OnDisable()
{
Debug.Log("Discord: shutdown");
DiscordRpc.Shutdown();
}
void OnDestroy()
{
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 27f0a5f59ffffa84c86547736e2e730a
timeCreated: 1501697692
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,198 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
public class DiscordRpc
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ReadyCallback(ref DiscordUser connectedUser);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DisconnectedCallback(int errorCode, string message);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ErrorCallback(int errorCode, string message);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void JoinCallback(string secret);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SpectateCallback(string secret);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void RequestCallback(ref DiscordUser request);
public struct EventHandlers
{
public ReadyCallback readyCallback;
public DisconnectedCallback disconnectedCallback;
public ErrorCallback errorCallback;
public JoinCallback joinCallback;
public SpectateCallback spectateCallback;
public RequestCallback requestCallback;
}
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct RichPresenceStruct
{
public IntPtr state; /* max 128 bytes */
public IntPtr details; /* max 128 bytes */
public long startTimestamp;
public long endTimestamp;
public IntPtr largeImageKey; /* max 32 bytes */
public IntPtr largeImageText; /* max 128 bytes */
public IntPtr smallImageKey; /* max 32 bytes */
public IntPtr smallImageText; /* max 128 bytes */
public IntPtr partyId; /* max 128 bytes */
public int partySize;
public int partyMax;
public IntPtr matchSecret; /* max 128 bytes */
public IntPtr joinSecret; /* max 128 bytes */
public IntPtr spectateSecret; /* max 128 bytes */
public bool instance;
}
[Serializable]
public struct DiscordUser
{
public string userId;
public string username;
public string discriminator;
public string avatar;
}
public enum Reply
{
No = 0,
Yes = 1,
Ignore = 2
}
[DllImport("discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)]
public static extern void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId);
[DllImport("discord-rpc", EntryPoint = "Discord_Shutdown", CallingConvention = CallingConvention.Cdecl)]
public static extern void Shutdown();
[DllImport("discord-rpc", EntryPoint = "Discord_RunCallbacks", CallingConvention = CallingConvention.Cdecl)]
public static extern void RunCallbacks();
[DllImport("discord-rpc", EntryPoint = "Discord_UpdatePresence", CallingConvention = CallingConvention.Cdecl)]
private static extern void UpdatePresenceNative(ref RichPresenceStruct presence);
[DllImport("discord-rpc", EntryPoint = "Discord_ClearPresence", CallingConvention = CallingConvention.Cdecl)]
public static extern void ClearPresence();
[DllImport("discord-rpc", EntryPoint = "Discord_Respond", CallingConvention = CallingConvention.Cdecl)]
public static extern void Respond(string userId, Reply reply);
[DllImport("discord-rpc", EntryPoint = "Discord_UpdateHandlers", CallingConvention = CallingConvention.Cdecl)]
public static extern void UpdateHandlers(ref EventHandlers handlers);
public static void UpdatePresence(RichPresence presence)
{
var presencestruct = presence.GetStruct();
UpdatePresenceNative(ref presencestruct);
presence.FreeMem();
}
public class RichPresence
{
private RichPresenceStruct _presence;
private readonly List<IntPtr> _buffers = new List<IntPtr>(10);
public string state; /* max 128 bytes */
public string details; /* max 128 bytes */
public long startTimestamp;
public long endTimestamp;
public string largeImageKey; /* max 32 bytes */
public string largeImageText; /* max 128 bytes */
public string smallImageKey; /* max 32 bytes */
public string smallImageText; /* max 128 bytes */
public string partyId; /* max 128 bytes */
public int partySize;
public int partyMax;
public string matchSecret; /* max 128 bytes */
public string joinSecret; /* max 128 bytes */
public string spectateSecret; /* max 128 bytes */
public bool instance;
/// <summary>
/// Get the <see cref="RichPresenceStruct"/> reprensentation of this instance
/// </summary>
/// <returns><see cref="RichPresenceStruct"/> reprensentation of this instance</returns>
internal RichPresenceStruct GetStruct()
{
if (_buffers.Count > 0)
{
FreeMem();
}
_presence.state = StrToPtr(state);
_presence.details = StrToPtr(details);
_presence.startTimestamp = startTimestamp;
_presence.endTimestamp = endTimestamp;
_presence.largeImageKey = StrToPtr(largeImageKey);
_presence.largeImageText = StrToPtr(largeImageText);
_presence.smallImageKey = StrToPtr(smallImageKey);
_presence.smallImageText = StrToPtr(smallImageText);
_presence.partyId = StrToPtr(partyId);
_presence.partySize = partySize;
_presence.partyMax = partyMax;
_presence.matchSecret = StrToPtr(matchSecret);
_presence.joinSecret = StrToPtr(joinSecret);
_presence.spectateSecret = StrToPtr(spectateSecret);
_presence.instance = instance;
return _presence;
}
/// <summary>
/// Returns a pointer to a representation of the given string with a size of maxbytes
/// </summary>
/// <param name="input">String to convert</param>
/// <returns>Pointer to the UTF-8 representation of <see cref="input"/></returns>
private IntPtr StrToPtr(string input)
{
if (string.IsNullOrEmpty(input)) return IntPtr.Zero;
var convbytecnt = Encoding.UTF8.GetByteCount(input);
var buffer = Marshal.AllocHGlobal(convbytecnt + 1);
for (int i = 0; i < convbytecnt + 1; i++)
{
Marshal.WriteByte(buffer, i , 0);
}
_buffers.Add(buffer);
Marshal.Copy(Encoding.UTF8.GetBytes(input), 0, buffer, convbytecnt);
return buffer;
}
/// <summary>
/// Convert string to UTF-8 and add null termination
/// </summary>
/// <param name="toconv">string to convert</param>
/// <returns>UTF-8 representation of <see cref="toconv"/> with added null termination</returns>
private static string StrToUtf8NullTerm(string toconv)
{
var str = toconv.Trim();
var bytes = Encoding.Default.GetBytes(str);
if (bytes.Length > 0 && bytes[bytes.Length - 1] != 0)
{
str += "\0\0";
}
return Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(str));
}
/// <summary>
/// Free the allocated memory for conversion to <see cref="RichPresenceStruct"/>
/// </summary>
internal void FreeMem()
{
for (var i = _buffers.Count - 1; i >= 0; i--)
{
Marshal.FreeHGlobal(_buffers[i]);
_buffers.RemoveAt(i);
}
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: b4474a677de9d80409e98c5393ec5b1e
timeCreated: 1501697692
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,101 +0,0 @@
using UnityEditor;
using System.Diagnostics;
using System.IO;
[InitializeOnLoad]
public class ScriptBatch
{
static ScriptBatch()
{
EnsureDLL();
}
public static bool FileExists(string filename)
{
return new FileInfo(filename).Exists;
}
public static bool RunRpcBuildScript()
{
UnityEngine.Debug.Log("Try to run build script");
Process proc = new Process();
#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX
proc.StartInfo.UseShellExecute = false;
// brew installs cmake in /usr/local/bin, which Unity seems to strip from PATH?
string newPath = proc.StartInfo.EnvironmentVariables["PATH"] + ":/usr/local/bin";
proc.StartInfo.EnvironmentVariables["PATH"] = newPath;
#endif
proc.StartInfo.FileName = "python";
proc.StartInfo.Arguments = "build.py unity";
proc.StartInfo.WorkingDirectory = "../..";
proc.Start();
proc.WaitForExit();
return proc.ExitCode == 0;
}
public static void EnsureDLL()
{
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
string[] dstDirs = { "Assets/Plugins", "Assets/Plugins/x86", "Assets/Plugins/x86_64" };
string[] dstDlls = { "Assets/Plugins/x86/discord-rpc.dll", "Assets/Plugins/x86_64/discord-rpc.dll" };
string[] srcDlls = { "../../builds/install/win64-dynamic/bin/discord-rpc.dll", "../../builds/install/win64-dynamic/bin/discord-rpc.dll" };
#elif UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX
string[] dstDirs = { "Assets/Plugins" };
string[] dstDlls = { "Assets/Plugins/discord-rpc.bundle" };
string[] srcDlls = { "../../builds/install/osx-dynamic/lib/libdiscord-rpc.dylib" };
#else
string[] dstDirs = { "Assets/Plugins", "Assets/Plugins/x86", "Assets/Plugins/x86_64" };
string[] dstDlls = { "Assets/Plugins/discord-rpc.so" };
string[] srcDlls = { "../../builds/install/linux-dynamic/lib/libdiscord-rpc.so" };
#endif
Debug.Assert(dstDlls.Length == srcDlls.Length);
bool exists = true;
foreach (string fname in dstDlls)
{
if (!FileExists(fname))
{
exists = false;
break;
}
}
if (exists)
{
return;
}
exists = true;
foreach (string fname in srcDlls)
{
if (!FileExists(fname))
{
exists = false;
break;
}
}
if (!exists)
{
if (!RunRpcBuildScript())
{
UnityEngine.Debug.LogError("Build failed");
return;
}
}
// make sure the dirs exist
foreach (string dirname in dstDirs)
{
Directory.CreateDirectory(dirname);
}
// Copy dlls
for (int i = 0; i < dstDlls.Length; ++i)
{
FileUtil.CopyFileOrDirectory(srcDlls[i], dstDlls[i]);
}
}
}

View File

@ -1,13 +0,0 @@
fileFormatVersion: 2
guid: e5aecc4633e5f594b85eaa39f49bb402
timeCreated: 1512071254
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 3b03d21bb25fa494e8694cd6e4b6d769
timeCreated: 1501696924
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,17 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!11 &1
AudioManager:
m_ObjectHideFlags: 0
m_Volume: 1
Rolloff Scale: 1
Doppler Factor: 1
Default Speaker Mode: 2
m_SampleRate: 0
m_DSPBufferSize: 0
m_VirtualVoiceCount: 512
m_RealVoiceCount: 32
m_SpatializerPlugin:
m_AmbisonicDecoderPlugin:
m_DisableAudio: 0
m_VirtualizeEffects: 1

View File

@ -1,6 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!236 &1
ClusterInputManager:
m_ObjectHideFlags: 0
m_Inputs: []

View File

@ -1,20 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!55 &1
PhysicsManager:
m_ObjectHideFlags: 0
serializedVersion: 3
m_Gravity: {x: 0, y: -9.81, z: 0}
m_DefaultMaterial: {fileID: 0}
m_BounceThreshold: 2
m_SleepThreshold: 0.005
m_DefaultContactOffset: 0.01
m_DefaultSolverIterations: 6
m_DefaultSolverVelocityIterations: 1
m_QueriesHitBackfaces: 0
m_QueriesHitTriggers: 1
m_EnableAdaptiveForce: 0
m_EnablePCM: 1
m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
m_AutoSimulation: 1
m_AutoSyncTransforms: 1

View File

@ -1,10 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1045 &1
EditorBuildSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Scenes:
- enabled: 1
path: Assets/main.unity
guid: 3b03d21bb25fa494e8694cd6e4b6d769

View File

@ -1,16 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!159 &1
EditorSettings:
m_ObjectHideFlags: 0
serializedVersion: 4
m_ExternalVersionControlSupport: Visible Meta Files
m_SerializationMode: 2
m_DefaultBehaviorMode: 1
m_SpritePackerMode: 4
m_SpritePackerPaddingPower: 1
m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd
m_ProjectGenerationRootNamespace:
m_UserGeneratedProjectSuffix:
m_CollabEditorSettings:
inProgressEnabled: 1

View File

@ -1,61 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!30 &1
GraphicsSettings:
m_ObjectHideFlags: 0
serializedVersion: 12
m_Deferred:
m_Mode: 1
m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0}
m_DeferredReflections:
m_Mode: 1
m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0}
m_ScreenSpaceShadows:
m_Mode: 1
m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0}
m_LegacyDeferred:
m_Mode: 1
m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0}
m_DepthNormals:
m_Mode: 1
m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0}
m_MotionVectors:
m_Mode: 1
m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0}
m_LightHalo:
m_Mode: 1
m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0}
m_LensFlare:
m_Mode: 1
m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0}
m_AlwaysIncludedShaders:
- {fileID: 7, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0}
m_PreloadedShaders: []
m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000,
type: 0}
m_CustomRenderPipeline: {fileID: 0}
m_TransparencySortMode: 0
m_TransparencySortAxis: {x: 0, y: 0, z: 1}
m_DefaultRenderingPath: 1
m_DefaultMobileRenderingPath: 1
m_TierSettings: []
m_LightmapStripping: 0
m_FogStripping: 0
m_InstancingStripping: 0
m_LightmapKeepPlain: 1
m_LightmapKeepDirCombined: 1
m_LightmapKeepDynamicPlain: 1
m_LightmapKeepDynamicDirCombined: 1
m_LightmapKeepShadowMask: 1
m_LightmapKeepSubtractive: 1
m_FogKeepLinear: 1
m_FogKeepExp: 1
m_FogKeepExp2: 1
m_AlbedoSwatchInfos: []
m_LightsUseLinearIntensity: 0
m_LightsUseColorTemperature: 0

View File

@ -1,295 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!13 &1
InputManager:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Axes:
- serializedVersion: 3
m_Name: Horizontal
descriptiveName:
descriptiveNegativeName:
negativeButton: left
positiveButton: right
altNegativeButton: a
altPositiveButton: d
gravity: 3
dead: 0.001
sensitivity: 3
snap: 1
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Vertical
descriptiveName:
descriptiveNegativeName:
negativeButton: down
positiveButton: up
altNegativeButton: s
altPositiveButton: w
gravity: 3
dead: 0.001
sensitivity: 3
snap: 1
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Fire1
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: left ctrl
altNegativeButton:
altPositiveButton: mouse 0
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Fire2
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: left alt
altNegativeButton:
altPositiveButton: mouse 1
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Fire3
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: left shift
altNegativeButton:
altPositiveButton: mouse 2
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Jump
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: space
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Mouse X
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton:
altNegativeButton:
altPositiveButton:
gravity: 0
dead: 0
sensitivity: 0.1
snap: 0
invert: 0
type: 1
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Mouse Y
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton:
altNegativeButton:
altPositiveButton:
gravity: 0
dead: 0
sensitivity: 0.1
snap: 0
invert: 0
type: 1
axis: 1
joyNum: 0
- serializedVersion: 3
m_Name: Mouse ScrollWheel
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton:
altNegativeButton:
altPositiveButton:
gravity: 0
dead: 0
sensitivity: 0.1
snap: 0
invert: 0
type: 1
axis: 2
joyNum: 0
- serializedVersion: 3
m_Name: Horizontal
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton:
altNegativeButton:
altPositiveButton:
gravity: 0
dead: 0.19
sensitivity: 1
snap: 0
invert: 0
type: 2
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Vertical
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton:
altNegativeButton:
altPositiveButton:
gravity: 0
dead: 0.19
sensitivity: 1
snap: 0
invert: 1
type: 2
axis: 1
joyNum: 0
- serializedVersion: 3
m_Name: Fire1
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: joystick button 0
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Fire2
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: joystick button 1
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Fire3
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: joystick button 2
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Jump
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: joystick button 3
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Submit
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: return
altNegativeButton:
altPositiveButton: joystick button 0
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Submit
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: enter
altNegativeButton:
altPositiveButton: space
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Cancel
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: escape
altNegativeButton:
altPositiveButton: joystick button 1
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0

View File

@ -1,89 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!126 &1
NavMeshProjectSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
areas:
- name: Walkable
cost: 1
- name: Not Walkable
cost: 1
- name: Jump
cost: 2
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
m_LastAgentTypeID: -887442657
m_Settings:
- serializedVersion: 2
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.75
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
accuratePlacement: 0
m_SettingNames:
- Humanoid

View File

@ -1,8 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!149 &1
NetworkManager:
m_ObjectHideFlags: 0
m_DebugLevel: 0
m_Sendrate: 15
m_AssetToPrefab: {}

View File

@ -1,37 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!19 &1
Physics2DSettings:
m_ObjectHideFlags: 0
serializedVersion: 3
m_Gravity: {x: 0, y: -9.81}
m_DefaultMaterial: {fileID: 0}
m_VelocityIterations: 8
m_PositionIterations: 3
m_VelocityThreshold: 1
m_MaxLinearCorrection: 0.2
m_MaxAngularCorrection: 8
m_MaxTranslationSpeed: 100
m_MaxRotationSpeed: 360
m_BaumgarteScale: 0.2
m_BaumgarteTimeOfImpactScale: 0.75
m_TimeToSleep: 0.5
m_LinearSleepTolerance: 0.01
m_AngularSleepTolerance: 2
m_DefaultContactOffset: 0.01
m_AutoSimulation: 1
m_QueriesHitTriggers: 1
m_QueriesStartInColliders: 1
m_ChangeStopsCallbacks: 0
m_CallbacksOnDisable: 1
m_AutoSyncTransforms: 1
m_AlwaysShowColliders: 0
m_ShowColliderSleep: 1
m_ShowColliderContacts: 0
m_ShowColliderAABB: 0
m_ContactArrowScale: 0.2
m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412}
m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432}
m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745}
m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804}
m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

View File

@ -1,610 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!129 &1
PlayerSettings:
m_ObjectHideFlags: 0
serializedVersion: 13
productGUID: 5eccc60d3e382a346a65f512d6b81b84
AndroidProfiler: 0
AndroidFilterTouchesWhenObscured: 0
defaultScreenOrientation: 4
targetDevice: 2
useOnDemandResources: 0
accelerometerFrequency: 60
companyName: Discord Inc.
productName: button-clicker
defaultCursor: {fileID: 0}
cursorHotspot: {x: 0, y: 0}
m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1}
m_ShowUnitySplashScreen: 1
m_ShowUnitySplashLogo: 1
m_SplashScreenOverlayOpacity: 1
m_SplashScreenAnimation: 1
m_SplashScreenLogoStyle: 1
m_SplashScreenDrawMode: 0
m_SplashScreenBackgroundAnimationZoom: 1
m_SplashScreenLogoAnimationZoom: 1
m_SplashScreenBackgroundLandscapeAspect: 1
m_SplashScreenBackgroundPortraitAspect: 1
m_SplashScreenBackgroundLandscapeUvs:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
m_SplashScreenBackgroundPortraitUvs:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
m_SplashScreenLogos: []
m_VirtualRealitySplashScreen: {fileID: 0}
m_HolographicTrackingLossScreen: {fileID: 0}
defaultScreenWidth: 1024
defaultScreenHeight: 768
defaultScreenWidthWeb: 960
defaultScreenHeightWeb: 600
m_StereoRenderingPath: 0
m_ActiveColorSpace: 0
m_MTRendering: 1
m_StackTraceTypes: 010000000100000001000000010000000100000001000000
iosShowActivityIndicatorOnLoading: -1
androidShowActivityIndicatorOnLoading: -1
tizenShowActivityIndicatorOnLoading: -1
iosAppInBackgroundBehavior: 0
displayResolutionDialog: 1
iosAllowHTTPDownload: 1
allowedAutorotateToPortrait: 1
allowedAutorotateToPortraitUpsideDown: 1
allowedAutorotateToLandscapeRight: 1
allowedAutorotateToLandscapeLeft: 1
useOSAutorotation: 1
use32BitDisplayBuffer: 1
disableDepthAndStencilBuffers: 0
androidBlitType: 0
defaultIsFullScreen: 1
defaultIsNativeResolution: 1
macRetinaSupport: 1
runInBackground: 0
captureSingleScreen: 0
muteOtherAudioSources: 0
Prepare IOS For Recording: 0
Force IOS Speakers When Recording: 0
submitAnalytics: 1
usePlayerLog: 1
bakeCollisionMeshes: 0
forceSingleInstance: 0
resizableWindow: 0
useMacAppStoreValidation: 0
macAppStoreCategory: public.app-category.games
gpuSkinning: 0
graphicsJobs: 0
xboxPIXTextureCapture: 0
xboxEnableAvatar: 0
xboxEnableKinect: 0
xboxEnableKinectAutoTracking: 0
xboxEnableFitness: 0
visibleInBackground: 1
allowFullscreenSwitch: 1
graphicsJobMode: 0
macFullscreenMode: 2
d3d9FullscreenMode: 1
d3d11FullscreenMode: 1
xboxSpeechDB: 0
xboxEnableHeadOrientation: 0
xboxEnableGuest: 0
xboxEnablePIXSampling: 0
metalFramebufferOnly: 0
n3dsDisableStereoscopicView: 0
n3dsEnableSharedListOpt: 1
n3dsEnableVSync: 0
ignoreAlphaClear: 0
xboxOneResolution: 0
xboxOneMonoLoggingLevel: 0
xboxOneLoggingLevel: 1
xboxOneDisableEsram: 0
xboxOnePresentImmediateThreshold: 0
videoMemoryForVertexBuffers: 0
psp2PowerMode: 0
psp2AcquireBGM: 1
wiiUTVResolution: 0
wiiUGamePadMSAA: 1
wiiUSupportsNunchuk: 0
wiiUSupportsClassicController: 0
wiiUSupportsBalanceBoard: 0
wiiUSupportsMotionPlus: 0
wiiUSupportsProController: 0
wiiUAllowScreenCapture: 1
wiiUControllerCount: 0
m_SupportedAspectRatios:
4:3: 1
5:4: 1
16:10: 1
16:9: 1
Others: 1
bundleVersion: 1.0
preloadedAssets: []
metroInputSource: 0
m_HolographicPauseOnTrackingLoss: 1
xboxOneDisableKinectGpuReservation: 0
xboxOneEnable7thCore: 0
vrSettings:
cardboard:
depthFormat: 0
enableTransitionView: 0
daydream:
depthFormat: 0
useSustainedPerformanceMode: 0
enableVideoLayer: 0
useProtectedVideoMemory: 0
hololens:
depthFormat: 1
protectGraphicsMemory: 0
useHDRDisplay: 0
m_ColorGamuts: 00000000
targetPixelDensity: 0
resolutionScalingMode: 0
androidSupportedAspectRatio: 1
androidMaxAspectRatio: 2.1
applicationIdentifier: {}
buildNumber: {}
AndroidBundleVersionCode: 1
AndroidMinSdkVersion: 16
AndroidTargetSdkVersion: 0
AndroidPreferredInstallLocation: 1
aotOptions:
stripEngineCode: 1
iPhoneStrippingLevel: 0
iPhoneScriptCallOptimization: 0
ForceInternetPermission: 0
ForceSDCardPermission: 0
CreateWallpaper: 0
APKExpansionFiles: 0
keepLoadedShadersAlive: 0
StripUnusedMeshComponents: 0
VertexChannelCompressionMask:
serializedVersion: 2
m_Bits: 238
iPhoneSdkVersion: 988
iOSTargetOSVersionString: 7.0
tvOSSdkVersion: 0
tvOSRequireExtendedGameController: 0
tvOSTargetOSVersionString: 9.0
uIPrerenderedIcon: 0
uIRequiresPersistentWiFi: 0
uIRequiresFullScreen: 1
uIStatusBarHidden: 1
uIExitOnSuspend: 0
uIStatusBarStyle: 0
iPhoneSplashScreen: {fileID: 0}
iPhoneHighResSplashScreen: {fileID: 0}
iPhoneTallHighResSplashScreen: {fileID: 0}
iPhone47inSplashScreen: {fileID: 0}
iPhone55inPortraitSplashScreen: {fileID: 0}
iPhone55inLandscapeSplashScreen: {fileID: 0}
iPadPortraitSplashScreen: {fileID: 0}
iPadHighResPortraitSplashScreen: {fileID: 0}
iPadLandscapeSplashScreen: {fileID: 0}
iPadHighResLandscapeSplashScreen: {fileID: 0}
appleTVSplashScreen: {fileID: 0}
tvOSSmallIconLayers: []
tvOSLargeIconLayers: []
tvOSTopShelfImageLayers: []
tvOSTopShelfImageWideLayers: []
iOSLaunchScreenType: 0
iOSLaunchScreenPortrait: {fileID: 0}
iOSLaunchScreenLandscape: {fileID: 0}
iOSLaunchScreenBackgroundColor:
serializedVersion: 2
rgba: 0
iOSLaunchScreenFillPct: 100
iOSLaunchScreenSize: 100
iOSLaunchScreenCustomXibPath:
iOSLaunchScreeniPadType: 0
iOSLaunchScreeniPadImage: {fileID: 0}
iOSLaunchScreeniPadBackgroundColor:
serializedVersion: 2
rgba: 0
iOSLaunchScreeniPadFillPct: 100
iOSLaunchScreeniPadSize: 100
iOSLaunchScreeniPadCustomXibPath:
iOSDeviceRequirements: []
iOSURLSchemes: []
iOSBackgroundModes: 0
iOSMetalForceHardShadows: 0
metalEditorSupport: 1
metalAPIValidation: 1
iOSRenderExtraFrameOnPause: 0
appleDeveloperTeamID:
iOSManualSigningProvisioningProfileID:
tvOSManualSigningProvisioningProfileID:
appleEnableAutomaticSigning: 0
AndroidTargetDevice: 0
AndroidSplashScreenScale: 0
androidSplashScreen: {fileID: 0}
AndroidKeystoreName:
AndroidKeyaliasName:
AndroidTVCompatibility: 1
AndroidIsGame: 1
AndroidEnableTango: 0
androidEnableBanner: 1
androidUseLowAccuracyLocation: 0
m_AndroidBanners:
- width: 320
height: 180
banner: {fileID: 0}
androidGamepadSupportLevel: 0
resolutionDialogBanner: {fileID: 0}
m_BuildTargetIcons: []
m_BuildTargetBatching: []
m_BuildTargetGraphicsAPIs: []
m_BuildTargetVRSettings: []
m_BuildTargetEnableVuforiaSettings: []
openGLRequireES31: 0
openGLRequireES31AEP: 0
m_TemplateCustomTags: {}
mobileMTRendering:
Android: 1
iPhone: 1
tvOS: 1
wiiUTitleID: 0005000011000000
wiiUGroupID: 00010000
wiiUCommonSaveSize: 4096
wiiUAccountSaveSize: 2048
wiiUOlvAccessKey: 0
wiiUTinCode: 0
wiiUJoinGameId: 0
wiiUJoinGameModeMask: 0000000000000000
wiiUCommonBossSize: 0
wiiUAccountBossSize: 0
wiiUAddOnUniqueIDs: []
wiiUMainThreadStackSize: 3072
wiiULoaderThreadStackSize: 1024
wiiUSystemHeapSize: 128
wiiUTVStartupScreen: {fileID: 0}
wiiUGamePadStartupScreen: {fileID: 0}
wiiUDrcBufferDisabled: 0
wiiUProfilerLibPath:
playModeTestRunnerEnabled: 0
actionOnDotNetUnhandledException: 1
enableInternalProfiler: 0
logObjCUncaughtExceptions: 1
enableCrashReportAPI: 0
cameraUsageDescription:
locationUsageDescription:
microphoneUsageDescription:
switchNetLibKey:
switchSocketMemoryPoolSize: 6144
switchSocketAllocatorPoolSize: 128
switchSocketConcurrencyLimit: 14
switchScreenResolutionBehavior: 2
switchUseCPUProfiler: 0
switchApplicationID: 0x01004b9000490000
switchNSODependencies:
switchTitleNames_0:
switchTitleNames_1:
switchTitleNames_2:
switchTitleNames_3:
switchTitleNames_4:
switchTitleNames_5:
switchTitleNames_6:
switchTitleNames_7:
switchTitleNames_8:
switchTitleNames_9:
switchTitleNames_10:
switchTitleNames_11:
switchPublisherNames_0:
switchPublisherNames_1:
switchPublisherNames_2:
switchPublisherNames_3:
switchPublisherNames_4:
switchPublisherNames_5:
switchPublisherNames_6:
switchPublisherNames_7:
switchPublisherNames_8:
switchPublisherNames_9:
switchPublisherNames_10:
switchPublisherNames_11:
switchIcons_0: {fileID: 0}
switchIcons_1: {fileID: 0}
switchIcons_2: {fileID: 0}
switchIcons_3: {fileID: 0}
switchIcons_4: {fileID: 0}
switchIcons_5: {fileID: 0}
switchIcons_6: {fileID: 0}
switchIcons_7: {fileID: 0}
switchIcons_8: {fileID: 0}
switchIcons_9: {fileID: 0}
switchIcons_10: {fileID: 0}
switchIcons_11: {fileID: 0}
switchSmallIcons_0: {fileID: 0}
switchSmallIcons_1: {fileID: 0}
switchSmallIcons_2: {fileID: 0}
switchSmallIcons_3: {fileID: 0}
switchSmallIcons_4: {fileID: 0}
switchSmallIcons_5: {fileID: 0}
switchSmallIcons_6: {fileID: 0}
switchSmallIcons_7: {fileID: 0}
switchSmallIcons_8: {fileID: 0}
switchSmallIcons_9: {fileID: 0}
switchSmallIcons_10: {fileID: 0}
switchSmallIcons_11: {fileID: 0}
switchManualHTML:
switchAccessibleURLs:
switchLegalInformation:
switchMainThreadStackSize: 1048576
switchPresenceGroupId: 0x01004b9000490000
switchLogoHandling: 0
switchReleaseVersion: 0
switchDisplayVersion: 1.0.0
switchStartupUserAccount: 0
switchTouchScreenUsage: 0
switchSupportedLanguagesMask: 0
switchLogoType: 0
switchApplicationErrorCodeCategory:
switchUserAccountSaveDataSize: 0
switchUserAccountSaveDataJournalSize: 0
switchApplicationAttribute: 0
switchCardSpecSize: 4
switchCardSpecClock: 25
switchRatingsMask: 0
switchRatingsInt_0: 0
switchRatingsInt_1: 0
switchRatingsInt_2: 0
switchRatingsInt_3: 0
switchRatingsInt_4: 0
switchRatingsInt_5: 0
switchRatingsInt_6: 0
switchRatingsInt_7: 0
switchRatingsInt_8: 0
switchRatingsInt_9: 0
switchRatingsInt_10: 0
switchRatingsInt_11: 0
switchLocalCommunicationIds_0: 0x01004b9000490000
switchLocalCommunicationIds_1:
switchLocalCommunicationIds_2:
switchLocalCommunicationIds_3:
switchLocalCommunicationIds_4:
switchLocalCommunicationIds_5:
switchLocalCommunicationIds_6:
switchLocalCommunicationIds_7:
switchParentalControl: 0
switchAllowsScreenshot: 1
switchDataLossConfirmation: 0
switchSupportedNpadStyles: 3
switchSocketConfigEnabled: 0
switchTcpInitialSendBufferSize: 32
switchTcpInitialReceiveBufferSize: 64
switchTcpAutoSendBufferSizeMax: 256
switchTcpAutoReceiveBufferSizeMax: 256
switchUdpSendBufferSize: 9
switchUdpReceiveBufferSize: 42
switchSocketBufferEfficiency: 4
switchSocketInitializeEnabled: 1
switchNetworkInterfaceManagerInitializeEnabled: 1
switchPlayerConnectionEnabled: 1
ps4NPAgeRating: 12
ps4NPTitleSecret:
ps4NPTrophyPackPath:
ps4ParentalLevel: 11
ps4ContentID: ED1633-NPXX51362_00-0000000000000000
ps4Category: 0
ps4MasterVersion: 01.00
ps4AppVersion: 01.00
ps4AppType: 0
ps4ParamSfxPath:
ps4VideoOutPixelFormat: 0
ps4VideoOutInitialWidth: 1920
ps4VideoOutBaseModeInitialWidth: 1920
ps4VideoOutReprojectionRate: 120
ps4PronunciationXMLPath:
ps4PronunciationSIGPath:
ps4BackgroundImagePath:
ps4StartupImagePath:
ps4SaveDataImagePath:
ps4SdkOverride:
ps4BGMPath:
ps4ShareFilePath:
ps4ShareOverlayImagePath:
ps4PrivacyGuardImagePath:
ps4NPtitleDatPath:
ps4RemotePlayKeyAssignment: -1
ps4RemotePlayKeyMappingDir:
ps4PlayTogetherPlayerCount: 0
ps4EnterButtonAssignment: 1
ps4ApplicationParam1: 0
ps4ApplicationParam2: 0
ps4ApplicationParam3: 0
ps4ApplicationParam4: 0
ps4DownloadDataSize: 0
ps4GarlicHeapSize: 2048
ps4ProGarlicHeapSize: 2560
ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ
ps4pnSessions: 1
ps4pnPresence: 1
ps4pnFriends: 1
ps4pnGameCustomData: 1
playerPrefsSupport: 0
restrictedAudioUsageRights: 0
ps4UseResolutionFallback: 0
ps4ReprojectionSupport: 0
ps4UseAudio3dBackend: 0
ps4SocialScreenEnabled: 0
ps4ScriptOptimizationLevel: 0
ps4Audio3dVirtualSpeakerCount: 14
ps4attribCpuUsage: 0
ps4PatchPkgPath:
ps4PatchLatestPkgPath:
ps4PatchChangeinfoPath:
ps4PatchDayOne: 0
ps4attribUserManagement: 0
ps4attribMoveSupport: 0
ps4attrib3DSupport: 0
ps4attribShareSupport: 0
ps4attribExclusiveVR: 0
ps4disableAutoHideSplash: 0
ps4videoRecordingFeaturesUsed: 0
ps4contentSearchFeaturesUsed: 0
ps4attribEyeToEyeDistanceSettingVR: 0
ps4IncludedModules: []
monoEnv:
psp2Splashimage: {fileID: 0}
psp2NPTrophyPackPath:
psp2NPSupportGBMorGJP: 0
psp2NPAgeRating: 12
psp2NPTitleDatPath:
psp2NPCommsID:
psp2NPCommunicationsID:
psp2NPCommsPassphrase:
psp2NPCommsSig:
psp2ParamSfxPath:
psp2ManualPath:
psp2LiveAreaGatePath:
psp2LiveAreaBackroundPath:
psp2LiveAreaPath:
psp2LiveAreaTrialPath:
psp2PatchChangeInfoPath:
psp2PatchOriginalPackage:
psp2PackagePassword: F69AzBlax3CF3EDNhm3soLBPh71Yexui
psp2KeystoneFile:
psp2MemoryExpansionMode: 0
psp2DRMType: 0
psp2StorageType: 0
psp2MediaCapacity: 0
psp2DLCConfigPath:
psp2ThumbnailPath:
psp2BackgroundPath:
psp2SoundPath:
psp2TrophyCommId:
psp2TrophyPackagePath:
psp2PackagedResourcesPath:
psp2SaveDataQuota: 10240
psp2ParentalLevel: 1
psp2ShortTitle: Not Set
psp2ContentID: IV0000-ABCD12345_00-0123456789ABCDEF
psp2Category: 0
psp2MasterVersion: 01.00
psp2AppVersion: 01.00
psp2TVBootMode: 0
psp2EnterButtonAssignment: 2
psp2TVDisableEmu: 0
psp2AllowTwitterDialog: 1
psp2Upgradable: 0
psp2HealthWarning: 0
psp2UseLibLocation: 0
psp2InfoBarOnStartup: 0
psp2InfoBarColor: 0
psp2ScriptOptimizationLevel: 0
psmSplashimage: {fileID: 0}
splashScreenBackgroundSourceLandscape: {fileID: 0}
splashScreenBackgroundSourcePortrait: {fileID: 0}
spritePackerPolicy:
webGLMemorySize: 256
webGLExceptionSupport: 1
webGLNameFilesAsHashes: 0
webGLDataCaching: 0
webGLDebugSymbols: 0
webGLEmscriptenArgs:
webGLModulesDirectory:
webGLTemplate: APPLICATION:Default
webGLAnalyzeBuildSize: 0
webGLUseEmbeddedResources: 0
webGLUseWasm: 0
webGLCompressionFormat: 1
scriptingDefineSymbols: {}
platformArchitecture: {}
scriptingBackend: {}
incrementalIl2cppBuild: {}
additionalIl2CppArgs:
scriptingRuntimeVersion: 0
apiCompatibilityLevelPerPlatform: {}
m_RenderingPath: 1
m_MobileRenderingPath: 1
metroPackageName: button-clicker
metroPackageVersion:
metroCertificatePath:
metroCertificatePassword:
metroCertificateSubject:
metroCertificateIssuer:
metroCertificateNotAfter: 0000000000000000
metroApplicationDescription: button-clicker
wsaImages: {}
metroTileShortName:
metroCommandLineArgsFile:
metroTileShowName: 0
metroMediumTileShowName: 0
metroLargeTileShowName: 0
metroWideTileShowName: 0
metroDefaultTileSize: 1
metroTileForegroundText: 2
metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0}
metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628,
a: 1}
metroSplashScreenUseBackgroundColor: 0
platformCapabilities: {}
metroFTAName:
metroFTAFileTypes: []
metroProtocolName:
metroCompilationOverrides: 1
tizenProductDescription:
tizenProductURL:
tizenSigningProfileName:
tizenGPSPermissions: 0
tizenMicrophonePermissions: 0
tizenDeploymentTarget:
tizenDeploymentTargetType: -1
tizenMinOSVersion: 1
n3dsUseExtSaveData: 0
n3dsCompressStaticMem: 1
n3dsExtSaveDataNumber: 0x12345
n3dsStackSize: 131072
n3dsTargetPlatform: 2
n3dsRegion: 7
n3dsMediaSize: 0
n3dsLogoStyle: 3
n3dsTitle: GameName
n3dsProductCode:
n3dsApplicationId: 0xFF3FF
stvDeviceAddress:
stvProductDescription:
stvProductAuthor:
stvProductAuthorEmail:
stvProductLink:
stvProductCategory: 0
XboxOneProductId:
XboxOneUpdateKey:
XboxOneSandboxId:
XboxOneContentId:
XboxOneTitleId:
XboxOneSCId:
XboxOneGameOsOverridePath:
XboxOnePackagingOverridePath:
XboxOneAppManifestOverridePath:
XboxOnePackageEncryption: 0
XboxOnePackageUpdateGranularity: 2
XboxOneDescription:
XboxOneLanguage:
- enus
XboxOneCapability: []
XboxOneGameRating: {}
XboxOneIsContentPackage: 0
XboxOneEnableGPUVariability: 0
XboxOneSockets: {}
XboxOneSplashScreen: {fileID: 0}
XboxOneAllowedProductIds: []
XboxOnePersistentLocalStorageSize: 0
xboxOneScriptCompiler: 0
vrEditorSettings:
daydream:
daydreamIconForeground: {fileID: 0}
daydreamIconBackground: {fileID: 0}
cloudServicesEnabled: {}
facebookSdkVersion: 7.9.4
apiCompatibilityLevel: 2
cloudProjectId:
projectName:
organizationId:
cloudEnabled: 0
enableNativePlatformBackendsForNewInputSystem: 0
disableOldInputManagerSupport: 0

View File

@ -1 +0,0 @@
m_EditorVersion: 2017.2.0f3

View File

@ -1,193 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!47 &1
QualitySettings:
m_ObjectHideFlags: 0
serializedVersion: 5
m_CurrentQuality: 5
m_QualitySettings:
- serializedVersion: 2
name: Very Low
pixelLightCount: 0
shadows: 0
shadowResolution: 0
shadowProjection: 1
shadowCascades: 1
shadowDistance: 15
shadowNearPlaneOffset: 3
shadowCascade2Split: 0.33333334
shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
shadowmaskMode: 0
blendWeights: 1
textureQuality: 1
anisotropicTextures: 0
antiAliasing: 0
softParticles: 0
softVegetation: 0
realtimeReflectionProbes: 0
billboardsFaceCameraPosition: 0
vSyncCount: 0
lodBias: 0.3
maximumLODLevel: 0
particleRaycastBudget: 4
asyncUploadTimeSlice: 2
asyncUploadBufferSize: 4
resolutionScalingFixedDPIFactor: 1
excludedTargetPlatforms: []
- serializedVersion: 2
name: Low
pixelLightCount: 0
shadows: 0
shadowResolution: 0
shadowProjection: 1
shadowCascades: 1
shadowDistance: 20
shadowNearPlaneOffset: 3
shadowCascade2Split: 0.33333334
shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
shadowmaskMode: 0
blendWeights: 2
textureQuality: 0
anisotropicTextures: 0
antiAliasing: 0
softParticles: 0
softVegetation: 0
realtimeReflectionProbes: 0
billboardsFaceCameraPosition: 0
vSyncCount: 0
lodBias: 0.4
maximumLODLevel: 0
particleRaycastBudget: 16
asyncUploadTimeSlice: 2
asyncUploadBufferSize: 4
resolutionScalingFixedDPIFactor: 1
excludedTargetPlatforms: []
- serializedVersion: 2
name: Medium
pixelLightCount: 1
shadows: 1
shadowResolution: 0
shadowProjection: 1
shadowCascades: 1
shadowDistance: 20
shadowNearPlaneOffset: 3
shadowCascade2Split: 0.33333334
shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
shadowmaskMode: 0
blendWeights: 2
textureQuality: 0
anisotropicTextures: 1
antiAliasing: 0
softParticles: 0
softVegetation: 0
realtimeReflectionProbes: 0
billboardsFaceCameraPosition: 0
vSyncCount: 1
lodBias: 0.7
maximumLODLevel: 0
particleRaycastBudget: 64
asyncUploadTimeSlice: 2
asyncUploadBufferSize: 4
resolutionScalingFixedDPIFactor: 1
excludedTargetPlatforms: []
- serializedVersion: 2
name: High
pixelLightCount: 2
shadows: 2
shadowResolution: 1
shadowProjection: 1
shadowCascades: 2
shadowDistance: 40
shadowNearPlaneOffset: 3
shadowCascade2Split: 0.33333334
shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
shadowmaskMode: 1
blendWeights: 2
textureQuality: 0
anisotropicTextures: 1
antiAliasing: 0
softParticles: 0
softVegetation: 1
realtimeReflectionProbes: 1
billboardsFaceCameraPosition: 1
vSyncCount: 1
lodBias: 1
maximumLODLevel: 0
particleRaycastBudget: 256
asyncUploadTimeSlice: 2
asyncUploadBufferSize: 4
resolutionScalingFixedDPIFactor: 1
excludedTargetPlatforms: []
- serializedVersion: 2
name: Very High
pixelLightCount: 3
shadows: 2
shadowResolution: 2
shadowProjection: 1
shadowCascades: 2
shadowDistance: 70
shadowNearPlaneOffset: 3
shadowCascade2Split: 0.33333334
shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
shadowmaskMode: 1
blendWeights: 4
textureQuality: 0
anisotropicTextures: 2
antiAliasing: 2
softParticles: 1
softVegetation: 1
realtimeReflectionProbes: 1
billboardsFaceCameraPosition: 1
vSyncCount: 1
lodBias: 1.5
maximumLODLevel: 0
particleRaycastBudget: 1024
asyncUploadTimeSlice: 2
asyncUploadBufferSize: 4
resolutionScalingFixedDPIFactor: 1
excludedTargetPlatforms: []
- serializedVersion: 2
name: Ultra
pixelLightCount: 4
shadows: 2
shadowResolution: 2
shadowProjection: 1
shadowCascades: 4
shadowDistance: 150
shadowNearPlaneOffset: 3
shadowCascade2Split: 0.33333334
shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
shadowmaskMode: 1
blendWeights: 4
textureQuality: 0
anisotropicTextures: 2
antiAliasing: 2
softParticles: 1
softVegetation: 1
realtimeReflectionProbes: 1
billboardsFaceCameraPosition: 1
vSyncCount: 1
lodBias: 2
maximumLODLevel: 0
particleRaycastBudget: 4096
asyncUploadTimeSlice: 2
asyncUploadBufferSize: 4
resolutionScalingFixedDPIFactor: 1
excludedTargetPlatforms: []
m_PerPlatformDefaultQuality:
Android: 2
Nintendo 3DS: 5
Nintendo Switch: 5
PS4: 5
PSM: 5
PSP2: 2
Samsung TV: 2
Standalone: 5
Tizen: 2
Web: 5
WebGL: 3
WiiU: 5
Windows Store Apps: 5
XboxOne: 5
iPhone: 2
tvOS: 2

View File

@ -1,43 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!78 &1
TagManager:
serializedVersion: 2
tags: []
layers:
- Default
- TransparentFX
- Ignore Raycast
-
- Water
- UI
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
m_SortingLayers:
- name: Default
uniqueID: 0
locked: 0

View File

@ -1,9 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!5 &1
TimeManager:
m_ObjectHideFlags: 0
Fixed Timestep: 0.02
Maximum Allowed Timestep: 0.33333334
m_TimeScale: 1
Maximum Particle Timestep: 0.03

View File

@ -1,34 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!310 &1
UnityConnectSettings:
m_ObjectHideFlags: 0
m_Enabled: 0
m_TestMode: 0
m_TestEventUrl:
m_TestConfigUrl:
m_TestInitMode: 0
CrashReportingSettings:
m_EventUrl: https://perf-events.cloud.unity3d.com/api/events/crashes
m_Enabled: 0
m_CaptureEditorExceptions: 1
UnityPurchasingSettings:
m_Enabled: 0
m_TestMode: 0
UnityAnalyticsSettings:
m_Enabled: 0
m_InitializeOnStartup: 1
m_TestMode: 0
m_TestEventUrl:
m_TestConfigUrl:
UnityAdsSettings:
m_Enabled: 0
m_InitializeOnStartup: 1
m_TestMode: 0
m_EnabledPlatforms: 4294967295
m_IosGameId:
m_AndroidGameId:
m_GameIds: {}
m_GameId:
PerformanceReportingSettings:
m_Enabled: 0

View File

@ -1,4 +0,0 @@
{
"dependencies": {
}
}

View File

@ -1,21 +0,0 @@
include_directories(${PROJECT_SOURCE_DIR}/include)
add_executable(
send-presence
MACOSX_BUNDLE
send-presence.c
)
set_target_properties(send-presence PROPERTIES
MACOSX_BUNDLE_BUNDLE_NAME "Send Presence"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.discordapp.examples.send-presence"
)
target_link_libraries(send-presence discord-rpc)
install(
TARGETS send-presence
RUNTIME
DESTINATION "bin"
CONFIGURATIONS Release
BUNDLE
DESTINATION "bin"
CONFIGURATIONS Release
)

View File

@ -1,205 +0,0 @@
/*
This is a simple example in C of using the rich presence API asynchronously.
*/
#define _CRT_SECURE_NO_WARNINGS /* thanks Microsoft */
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "discord_rpc.h"
static const char* APPLICATION_ID = "345229890980937739";
static int FrustrationLevel = 0;
static int64_t StartTime;
static int SendPresence = 1;
static int prompt(char* line, size_t size)
{
int res;
char* nl;
printf("\n> ");
fflush(stdout);
res = fgets(line, (int)size, stdin) ? 1 : 0;
line[size - 1] = 0;
nl = strchr(line, '\n');
if (nl) {
*nl = 0;
}
return res;
}
static void updateDiscordPresence()
{
if (SendPresence) {
char buffer[256];
DiscordRichPresence discordPresence;
memset(&discordPresence, 0, sizeof(discordPresence));
discordPresence.state = "West of House";
sprintf(buffer, "Frustration level: %d", FrustrationLevel);
discordPresence.details = buffer;
discordPresence.startTimestamp = StartTime;
discordPresence.endTimestamp = time(0) + 5 * 60;
discordPresence.largeImageKey = "canary-large";
discordPresence.smallImageKey = "ptb-small";
discordPresence.partyId = "party1234";
discordPresence.partySize = 1;
discordPresence.partyMax = 6;
discordPresence.matchSecret = "xyzzy";
discordPresence.joinSecret = "join";
discordPresence.spectateSecret = "look";
discordPresence.instance = 0;
Discord_UpdatePresence(&discordPresence);
}
else {
Discord_ClearPresence();
}
}
static void handleDiscordReady(const DiscordUser* connectedUser)
{
printf("\nDiscord: connected to user %s#%s - %s\n",
connectedUser->username,
connectedUser->discriminator,
connectedUser->userId);
}
static void handleDiscordDisconnected(int errcode, const char* message)
{
printf("\nDiscord: disconnected (%d: %s)\n", errcode, message);
}
static void handleDiscordError(int errcode, const char* message)
{
printf("\nDiscord: error (%d: %s)\n", errcode, message);
}
static void handleDiscordJoin(const char* secret)
{
printf("\nDiscord: join (%s)\n", secret);
}
static void handleDiscordSpectate(const char* secret)
{
printf("\nDiscord: spectate (%s)\n", secret);
}
static void handleDiscordJoinRequest(const DiscordUser* request)
{
int response = -1;
char yn[4];
printf("\nDiscord: join request from %s#%s - %s\n",
request->username,
request->discriminator,
request->userId);
do {
printf("Accept? (y/n)");
if (!prompt(yn, sizeof(yn))) {
break;
}
if (!yn[0]) {
continue;
}
if (yn[0] == 'y') {
response = DISCORD_REPLY_YES;
break;
}
if (yn[0] == 'n') {
response = DISCORD_REPLY_NO;
break;
}
} while (1);
if (response != -1) {
Discord_Respond(request->userId, response);
}
}
static void discordInit()
{
DiscordEventHandlers handlers;
memset(&handlers, 0, sizeof(handlers));
handlers.ready = handleDiscordReady;
handlers.disconnected = handleDiscordDisconnected;
handlers.errored = handleDiscordError;
handlers.joinGame = handleDiscordJoin;
handlers.spectateGame = handleDiscordSpectate;
handlers.joinRequest = handleDiscordJoinRequest;
Discord_Initialize(APPLICATION_ID, &handlers, 1, NULL);
}
static void gameLoop()
{
char line[512];
char* space;
StartTime = time(0);
printf("You are standing in an open field west of a white house.\n");
while (prompt(line, sizeof(line))) {
if (line[0]) {
if (line[0] == 'q') {
break;
}
if (line[0] == 't') {
printf("Shutting off Discord.\n");
Discord_Shutdown();
continue;
}
if (line[0] == 'c') {
if (SendPresence) {
printf("Clearing presence information.\n");
SendPresence = 0;
}
else {
printf("Restoring presence information.\n");
SendPresence = 1;
}
updateDiscordPresence();
continue;
}
if (line[0] == 'y') {
printf("Reinit Discord.\n");
discordInit();
continue;
}
if (time(NULL) & 1) {
printf("I don't understand that.\n");
}
else {
space = strchr(line, ' ');
if (space) {
*space = 0;
}
printf("I don't know the word \"%s\".\n", line);
}
++FrustrationLevel;
updateDiscordPresence();
}
#ifdef DISCORD_DISABLE_IO_THREAD
Discord_UpdateConnection();
#endif
Discord_RunCallbacks();
}
}
int main(int argc, char* argv[])
{
discordInit();
gameLoop();
Discord_Shutdown();
return 0;
}

View File

@ -1,78 +0,0 @@
# Visual Studio 2015 user specific files
.vs/
# Visual Studio 2015 database file
*.VC.db
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
*.ipa
# These project files can be generated by the engine
*.xcodeproj
*.xcworkspace
*.sln
*.suo
*.opensdf
*.sdf
*.VC.db
*.VC.opendb
# Precompiled Assets
SourceArt/**/*.png
SourceArt/**/*.tga
# Binary Files
Binaries/
# Builds
Build/*
# Whitelist PakBlacklist-<BuildConfiguration>.txt files
!Build/*/
Build/*/**
!Build/*/PakBlacklist*.txt
# Don't ignore icon files in Build
!Build/**/*.ico
# Built data for maps
*_BuiltData.uasset
# Configuration files generated by the Editor
Saved/*
# Compiled source files for the engine to use
Intermediate/
# Cache files for the editor to use
DerivedDataCache/
# Library headers must be copied automatically by the build script (build.py unreal)
Plugins/DiscordRpc/Source/ThirdParty/DiscordRpcLibrary/Include

View File

@ -1,54 +0,0 @@
[URL]
[/Script/HardwareTargeting.HardwareTargetingSettings]
TargetedHardwareClass=Desktop
AppliedTargetedHardwareClass=Desktop
DefaultGraphicsPerformance=Maximum
AppliedDefaultGraphicsPerformance=Maximum
[/Script/Engine.EndUserSettings]
bSendAnonymousUsageDataToEpic=False
[/Script/Engine.PhysicsSettings]
DefaultGravityZ=-980.000000
DefaultTerminalVelocity=4000.000000
DefaultFluidFriction=0.300000
SimulateScratchMemorySize=262144
RagdollAggregateThreshold=4
TriangleMeshTriangleMinAreaThreshold=5.000000
bEnableAsyncScene=False
bEnableShapeSharing=False
bEnablePCM=False
bEnableStabilization=False
bWarnMissingLocks=True
bEnable2DPhysics=False
LockedAxis=Invalid
DefaultDegreesOfFreedom=Full3D
BounceThresholdVelocity=200.000000
FrictionCombineMode=Average
RestitutionCombineMode=Average
MaxAngularVelocity=3600.000000
MaxDepenetrationVelocity=0.000000
ContactOffsetMultiplier=0.010000
MinContactOffset=0.000100
MaxContactOffset=1.000000
bSimulateSkeletalMeshOnDedicatedServer=True
DefaultShapeComplexity=CTF_UseSimpleAndComplex
bDefaultHasComplexCollision=True
bSuppressFaceRemapTable=False
bSupportUVFromHitResults=False
bDisableActiveActors=False
bDisableCCD=False
MaxPhysicsDeltaTime=0.033333
bSubstepping=False
bSubsteppingAsync=False
MaxSubstepDeltaTime=0.016667
MaxSubsteps=6
SyncSceneSmoothingFactor=0.000000
AsyncSceneSmoothingFactor=0.990000
InitialAverageFrameRate=0.016667
[/Script/EngineSettings.GameMapsSettings]
EditorStartupMap=/Game/ShowTheUILevel.ShowTheUILevel
GameDefaultMap=/Game/ShowTheUILevel.ShowTheUILevel

View File

@ -1,7 +0,0 @@
[/Script/EngineSettings.GeneralProjectSettings]
ProjectID=E5977A24492699DF20B8ADBF736AF6C6
ProjectName=Discord RPC Example
CompanyName=Discord Inc.
Homepage="https://discordapp.com/"
CopyrightNotice=

View File

@ -1,29 +0,0 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "Discord RPC",
"Description": "Wrap the Discord RPC library.",
"Category": "Messaging",
"CreatedBy": "Chris Marsh <chris@discordapp.com>",
"CreatedByURL": "https://discordapp.com/",
"DocsURL": "",
"MarketplaceURL": "",
"SupportURL": "",
"CanContainContent": true,
"IsBetaVersion": true,
"Installed": false,
"Modules": [
{
"Name": "DiscordRpc",
"Type": "Runtime",
"LoadingPhase": "PreDefault",
"WhitelistPlatforms" :
[
"Win64",
"Linux",
"Mac"
]
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

View File

@ -1,57 +0,0 @@
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.IO;
public class DiscordRpc : ModuleRules
{
#if WITH_FORWARDED_MODULE_RULES_CTOR
public DiscordRpc(ReadOnlyTargetRules Target) : base(Target)
#else
public DiscordRpc(TargetInfo Target)
#endif
{
Definitions.Add("DISCORD_DYNAMIC_LIB=1");
PublicIncludePaths.AddRange(
new string[] {
"DiscordRpc/Public"
}
);
PrivateIncludePaths.AddRange(
new string[] {
"DiscordRpc/Private"
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"DiscordRpcLibrary"
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"Projects"
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
string BaseDirectory = Path.GetFullPath(Path.Combine(ModuleDirectory, "..", "..", "Source", "ThirdParty", "DiscordRpcLibrary"));
PublicIncludePaths.Add(Path.Combine(BaseDirectory, "Include"));
}
}

View File

@ -1,76 +0,0 @@
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#include "DiscordRpcPrivatePCH.h"
#include "IPluginManager.h"
#include "ModuleManager.h"
#define LOCTEXT_NAMESPACE "FDiscordRpcModule"
void FDiscordRpcModule::StartupModule()
{
#if !PLATFORM_LINUX
#if defined(DISCORD_DYNAMIC_LIB)
// Get the base directory of this plugin
FString BaseDir = IPluginManager::Get().FindPlugin("DiscordRpc")->GetBaseDir();
const FString SDKDir =
FPaths::Combine(*BaseDir, TEXT("Source"), TEXT("ThirdParty"), TEXT("DiscordRpcLibrary"));
#if PLATFORM_WINDOWS
const FString LibName = TEXT("discord-rpc");
const FString LibDir = FPaths::Combine(*SDKDir, TEXT("Win64"));
if (!LoadDependency(LibDir, LibName, DiscordRpcLibraryHandle)) {
FMessageDialog::Open(
EAppMsgType::Ok,
LOCTEXT(LOCTEXT_NAMESPACE,
"Failed to load DiscordRpc plugin. Plug-in will not be functional."));
FreeDependency(DiscordRpcLibraryHandle);
}
#elif PLATFORM_MAC
const FString LibName = TEXT("libdiscord-rpc");
const FString LibDir = FPaths::Combine(*SDKDir, TEXT("Mac"));
if (!LoadDependency(LibDir, LibName, DiscordRpcLibraryHandle)) {
FMessageDialog::Open(
EAppMsgType::Ok,
LOCTEXT(LOCTEXT_NAMESPACE,
"Failed to load DiscordRpc plugin. Plug-in will not be functional."));
FreeDependency(DiscordRpcLibraryHandle);
}
#endif
#endif
#endif
}
void FDiscordRpcModule::ShutdownModule()
{
// Free the dll handle
#if !PLATFORM_LINUX
#if defined(DISCORD_DYNAMIC_LIB)
FreeDependency(DiscordRpcLibraryHandle);
#endif
#endif
}
bool FDiscordRpcModule::LoadDependency(const FString& Dir, const FString& Name, void*& Handle)
{
FString Lib = Name + TEXT(".") + FPlatformProcess::GetModuleExtension();
FString Path = Dir.IsEmpty() ? *Lib : FPaths::Combine(*Dir, *Lib);
Handle = FPlatformProcess::GetDllHandle(*Path);
if (Handle == nullptr) {
return false;
}
return true;
}
void FDiscordRpcModule::FreeDependency(void*& Handle)
{
if (Handle != nullptr) {
FPlatformProcess::FreeDllHandle(Handle);
Handle = nullptr;
}
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FDiscordRpcModule, DiscordRpc)

View File

@ -1,171 +0,0 @@
#include "DiscordRpcPrivatePCH.h"
#include "DiscordRpcBlueprint.h"
#include "discord_rpc.h"
DEFINE_LOG_CATEGORY(Discord)
static UDiscordRpc* self = nullptr;
static void ReadyHandler(const DiscordUser* connectedUser)
{
FDiscordUserData ud;
ud.userId = ANSI_TO_TCHAR(connectedUser->userId);
ud.username = ANSI_TO_TCHAR(connectedUser->username);
ud.discriminator = ANSI_TO_TCHAR(connectedUser->discriminator);
ud.avatar = ANSI_TO_TCHAR(connectedUser->avatar);
UE_LOG(Discord,
Log,
TEXT("Discord connected to %s - %s#%s"),
*ud.userId,
*ud.username,
*ud.discriminator);
if (self) {
self->IsConnected = true;
self->OnConnected.Broadcast(ud);
}
}
static void DisconnectHandler(int errorCode, const char* message)
{
auto msg = FString(message);
UE_LOG(Discord, Log, TEXT("Discord disconnected (%d): %s"), errorCode, *msg);
if (self) {
self->IsConnected = false;
self->OnDisconnected.Broadcast(errorCode, msg);
}
}
static void ErroredHandler(int errorCode, const char* message)
{
auto msg = FString(message);
UE_LOG(Discord, Log, TEXT("Discord error (%d): %s"), errorCode, *msg);
if (self) {
self->OnErrored.Broadcast(errorCode, msg);
}
}
static void JoinGameHandler(const char* joinSecret)
{
auto secret = FString(joinSecret);
UE_LOG(Discord, Log, TEXT("Discord join %s"), *secret);
if (self) {
self->OnJoin.Broadcast(secret);
}
}
static void SpectateGameHandler(const char* spectateSecret)
{
auto secret = FString(spectateSecret);
UE_LOG(Discord, Log, TEXT("Discord spectate %s"), *secret);
if (self) {
self->OnSpectate.Broadcast(secret);
}
}
static void JoinRequestHandler(const DiscordUser* request)
{
FDiscordUserData ud;
ud.userId = ANSI_TO_TCHAR(request->userId);
ud.username = ANSI_TO_TCHAR(request->username);
ud.discriminator = ANSI_TO_TCHAR(request->discriminator);
ud.avatar = ANSI_TO_TCHAR(request->avatar);
UE_LOG(Discord,
Log,
TEXT("Discord join request from %s - %s#%s"),
*ud.userId,
*ud.username,
*ud.discriminator);
if (self) {
self->OnJoinRequest.Broadcast(ud);
}
}
void UDiscordRpc::Initialize(const FString& applicationId,
bool autoRegister,
const FString& optionalSteamId)
{
self = this;
IsConnected = false;
DiscordEventHandlers handlers{};
handlers.ready = ReadyHandler;
handlers.disconnected = DisconnectHandler;
handlers.errored = ErroredHandler;
if (OnJoin.IsBound()) {
handlers.joinGame = JoinGameHandler;
}
if (OnSpectate.IsBound()) {
handlers.spectateGame = SpectateGameHandler;
}
if (OnJoinRequest.IsBound()) {
handlers.joinRequest = JoinRequestHandler;
}
auto appId = StringCast<ANSICHAR>(*applicationId);
auto steamId = StringCast<ANSICHAR>(*optionalSteamId);
Discord_Initialize(
(const char*)appId.Get(), &handlers, autoRegister, (const char*)steamId.Get());
}
void UDiscordRpc::Shutdown()
{
Discord_Shutdown();
self = nullptr;
}
void UDiscordRpc::RunCallbacks()
{
Discord_RunCallbacks();
}
void UDiscordRpc::UpdatePresence()
{
DiscordRichPresence rp{};
auto state = StringCast<ANSICHAR>(*RichPresence.state);
rp.state = state.Get();
auto details = StringCast<ANSICHAR>(*RichPresence.details);
rp.details = details.Get();
auto largeImageKey = StringCast<ANSICHAR>(*RichPresence.largeImageKey);
rp.largeImageKey = largeImageKey.Get();
auto largeImageText = StringCast<ANSICHAR>(*RichPresence.largeImageText);
rp.largeImageText = largeImageText.Get();
auto smallImageKey = StringCast<ANSICHAR>(*RichPresence.smallImageKey);
rp.smallImageKey = smallImageKey.Get();
auto smallImageText = StringCast<ANSICHAR>(*RichPresence.smallImageText);
rp.smallImageText = smallImageText.Get();
auto partyId = StringCast<ANSICHAR>(*RichPresence.partyId);
rp.partyId = partyId.Get();
auto matchSecret = StringCast<ANSICHAR>(*RichPresence.matchSecret);
rp.matchSecret = matchSecret.Get();
auto joinSecret = StringCast<ANSICHAR>(*RichPresence.joinSecret);
rp.joinSecret = joinSecret.Get();
auto spectateSecret = StringCast<ANSICHAR>(*RichPresence.spectateSecret);
rp.spectateSecret = spectateSecret.Get();
rp.startTimestamp = RichPresence.startTimestamp;
rp.endTimestamp = RichPresence.endTimestamp;
rp.partySize = RichPresence.partySize;
rp.partyMax = RichPresence.partyMax;
rp.instance = RichPresence.instance;
Discord_UpdatePresence(&rp);
}
void UDiscordRpc::ClearPresence()
{
Discord_ClearPresence();
}
void UDiscordRpc::Respond(const FString& userId, int reply)
{
UE_LOG(Discord, Log, TEXT("Responding %d to join request from %s"), reply, *userId);
FTCHARToUTF8 utf8_userid(*userId);
Discord_Respond(utf8_userid.Get(), reply);
}

View File

@ -1,2 +0,0 @@
#include "Core.h"
#include "DiscordRpc.h"

View File

@ -1,20 +0,0 @@
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "ModuleManager.h"
class FDiscordRpcModule : public IModuleInterface {
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
private:
/** Handle to the test dll we will load */
void* DiscordRpcLibraryHandle;
/** StartupModule is covered with defines, these functions are the place to put breakpoints */
static bool LoadDependency(const FString& Dir, const FString& Name, void*& Handle);
static void FreeDependency(void*& Handle);
};

View File

@ -1,169 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "Engine.h"
#include "DiscordRpcBlueprint.generated.h"
// unreal's header tool hates clang-format
// clang-format off
/**
* Ask to join callback data
*/
USTRUCT(BlueprintType)
struct FDiscordUserData {
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadOnly)
FString userId;
UPROPERTY(BlueprintReadOnly)
FString username;
UPROPERTY(BlueprintReadOnly)
FString discriminator;
UPROPERTY(BlueprintReadOnly)
FString avatar;
};
/**
* Valid response codes for Respond function
*/
UENUM(BlueprintType)
enum class EDiscordJoinResponseCodes : uint8
{
DISCORD_REPLY_NO UMETA(DisplayName="No"),
DISCORD_REPLY_YES UMETA(DisplayName="Yes"),
DISCORD_REPLY_IGNORE UMETA(DisplayName="Ignore")
};
DECLARE_LOG_CATEGORY_EXTERN(Discord, Log, All);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordConnected, const FDiscordUserData&, joinRequest);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDiscordDisconnected, int, errorCode, const FString&, errorMessage);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDiscordErrored, int, errorCode, const FString&, errorMessage);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordJoin, const FString&, joinSecret);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordSpectate, const FString&, spectateSecret);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordJoinRequest, const FDiscordUserData&, joinRequest);
// clang-format on
/**
* Rich presence data
*/
USTRUCT(BlueprintType)
struct FDiscordRichPresence {
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadWrite)
FString state;
UPROPERTY(BlueprintReadWrite)
FString details;
// todo, timestamps are 64bit, does that even matter?
UPROPERTY(BlueprintReadWrite)
int startTimestamp;
UPROPERTY(BlueprintReadWrite)
int endTimestamp;
UPROPERTY(BlueprintReadWrite)
FString largeImageKey;
UPROPERTY(BlueprintReadWrite)
FString largeImageText;
UPROPERTY(BlueprintReadWrite)
FString smallImageKey;
UPROPERTY(BlueprintReadWrite)
FString smallImageText;
UPROPERTY(BlueprintReadWrite)
FString partyId;
UPROPERTY(BlueprintReadWrite)
int partySize;
UPROPERTY(BlueprintReadWrite)
int partyMax;
UPROPERTY(BlueprintReadWrite)
FString matchSecret;
UPROPERTY(BlueprintReadWrite)
FString joinSecret;
UPROPERTY(BlueprintReadWrite)
FString spectateSecret;
UPROPERTY(BlueprintReadWrite)
bool instance;
};
/**
*
*/
UCLASS(BlueprintType, meta = (DisplayName = "Discord RPC"), Category = "Discord")
class DISCORDRPC_API UDiscordRpc : public UObject {
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable,
meta = (DisplayName = "Initialize connection", Keywords = "Discord rpc"),
Category = "Discord")
void Initialize(const FString& applicationId,
bool autoRegister,
const FString& optionalSteamId);
UFUNCTION(BlueprintCallable,
meta = (DisplayName = "Shut down connection", Keywords = "Discord rpc"),
Category = "Discord")
void Shutdown();
UFUNCTION(BlueprintCallable,
meta = (DisplayName = "Check for callbacks", Keywords = "Discord rpc"),
Category = "Discord")
void RunCallbacks();
UFUNCTION(BlueprintCallable,
meta = (DisplayName = "Send presence", Keywords = "Discord rpc"),
Category = "Discord")
void UpdatePresence();
UFUNCTION(BlueprintCallable,
meta = (DisplayName = "Clear presence", Keywords = "Discord rpc"),
Category = "Discord")
void ClearPresence();
UFUNCTION(BlueprintCallable,
meta = (DisplayName = "Respond to join request", Keywords = "Discord rpc"),
Category = "Discord")
void Respond(const FString& userId, int reply);
UPROPERTY(BlueprintReadOnly,
meta = (DisplayName = "Is Discord connected", Keywords = "Discord rpc"),
Category = "Discord")
bool IsConnected;
UPROPERTY(BlueprintAssignable,
meta = (DisplayName = "On connection", Keywords = "Discord rpc"),
Category = "Discord")
FDiscordConnected OnConnected;
UPROPERTY(BlueprintAssignable,
meta = (DisplayName = "On disconnection", Keywords = "Discord rpc"),
Category = "Discord")
FDiscordDisconnected OnDisconnected;
UPROPERTY(BlueprintAssignable,
meta = (DisplayName = "On error message", Keywords = "Discord rpc"),
Category = "Discord")
FDiscordErrored OnErrored;
UPROPERTY(BlueprintAssignable,
meta = (DisplayName = "When Discord user presses join", Keywords = "Discord rpc"),
Category = "Discord")
FDiscordJoin OnJoin;
UPROPERTY(BlueprintAssignable,
meta = (DisplayName = "When Discord user presses spectate", Keywords = "Discord rpc"),
Category = "Discord")
FDiscordSpectate OnSpectate;
UPROPERTY(BlueprintAssignable,
meta = (DisplayName = "When Discord another user sends a join request",
Keywords = "Discord rpc"),
Category = "Discord")
FDiscordJoinRequest OnJoinRequest;
UPROPERTY(BlueprintReadWrite,
meta = (DisplayName = "Rich presence info", Keywords = "Discord rpc"),
Category = "Discord")
FDiscordRichPresence RichPresence;
};

View File

@ -1,59 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
using System.IO;
using UnrealBuildTool;
public class DiscordRpcLibrary : ModuleRules
{
#if WITH_FORWARDED_MODULE_RULES_CTOR
public DiscordRpcLibrary(ReadOnlyTargetRules Target) : base(Target)
#else
public DiscordRpcLibrary(TargetInfo Target)
#endif
{
Type = ModuleType.External;
Definitions.Add("DISCORD_DYNAMIC_LIB=1");
string BaseDirectory = Path.GetFullPath(Path.Combine(ModuleDirectory, "..", "..", "ThirdParty", "DiscordRpcLibrary"));
if (Target.Platform == UnrealTargetPlatform.Win64)
{
string lib = Path.Combine(BaseDirectory, "Win64");
// Include headers
PublicIncludePaths.Add(Path.Combine(BaseDirectory, "Include"));
// Add the import library
PublicLibraryPaths.Add(lib);
PublicAdditionalLibraries.Add(Path.Combine(lib, "discord-rpc.lib"));
// Dynamic
RuntimeDependencies.Add(new RuntimeDependency(Path.Combine(lib, "discord-rpc.dll")));
PublicDelayLoadDLLs.Add("discord-rpc.dll");
}
else if (Target.Platform == UnrealTargetPlatform.Linux)
{
string lib = Path.Combine(BaseDirectory, "Linux", "x86_64-unknown-linux-gnu");
// Include headers
PublicIncludePaths.Add(Path.Combine(BaseDirectory, "Include"));
// Add the import library
PublicLibraryPaths.Add(lib);
PublicAdditionalLibraries.Add(Path.Combine(lib, "libdiscord-rpc.so"));
RuntimeDependencies.Add(new RuntimeDependency(Path.Combine(lib, "libdiscord-rpc.so")));
}
else if (Target.Platform == UnrealTargetPlatform.Mac)
{
string lib = Path.Combine(BaseDirectory, "Mac");
// Include headers
PublicIncludePaths.Add(Path.Combine(BaseDirectory, "Include"));
// Add the import library
PublicLibraryPaths.Add(lib);
PublicAdditionalLibraries.Add(Path.Combine(lib, "libdiscord-rpc.dylib"));
RuntimeDependencies.Add(new RuntimeDependency(Path.Combine(lib, "libdiscord-rpc.dylib")));
}
}
}

View File

@ -1,14 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
using UnrealBuildTool;
using System.Collections.Generic;
public class unrealstatusTarget : TargetRules
{
public unrealstatusTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Game;
ExtraModuleNames.AddRange( new string[] { "unrealstatus" } );
}
}

View File

@ -1,23 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
using UnrealBuildTool;
public class unrealstatus : ModuleRules
{
public unrealstatus(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
PrivateDependencyModuleNames.AddRange(new string[] { });
// Uncomment if you are using Slate UI
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
// Uncomment if you are using online features
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
}
}

View File

@ -1,6 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "unrealstatus.h"
#include "Modules/ModuleManager.h"
IMPLEMENT_PRIMARY_GAME_MODULE(FDefaultGameModuleImpl, unrealstatus, "unrealstatus");

View File

@ -1,5 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"

View File

@ -1,3 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "unrealstatusGameModeBase.h"

View File

@ -1,15 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "unrealstatusGameModeBase.generated.h"
/**
*
*/
UCLASS()
class UNREALSTATUS_API AunrealstatusGameModeBase : public AGameModeBase {
GENERATED_BODY()
};

View File

@ -1,14 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
using UnrealBuildTool;
using System.Collections.Generic;
public class unrealstatusEditorTarget : TargetRules
{
public unrealstatusEditorTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Editor;
ExtraModuleNames.AddRange( new string[] { "unrealstatus" } );
}
}

View File

@ -1,19 +0,0 @@
{
"FileVersion": 3,
"EngineAssociation": "4.18",
"Category": "",
"Description": "",
"Modules": [
{
"Name": "unrealstatus",
"Type": "Runtime",
"LoadingPhase": "Default"
}
],
"TargetPlatforms": [
"LinuxNoEditor",
"MacNoEditor",
"WindowsNoEditor",
"AllDesktop"
]
}

View File

@ -1,18 +1,6 @@
#pragma once
#if defined(DISCORD_DYNAMIC_LIB)
#if defined(_WIN32)
#if defined(DISCORD_BUILDING_SDK)
#define DISCORD_EXPORT __declspec(dllexport)
#else
#define DISCORD_EXPORT __declspec(dllimport)
#endif
#else
#define DISCORD_EXPORT __attribute__((visibility("default")))
#endif
#else
#define DISCORD_EXPORT
#endif
#ifdef __cplusplus
extern "C" {

View File

@ -1,19 +1,20 @@
#pragma once
#include <stdint.h>
// clang-format off
/* clang-format off */
# define DISCORD_EXPORT
// clang-format on
/* clang-format on */
#ifdef __cplusplus
extern "C" {
#endif
typedef struct DiscordRichPresence {
const char* state; /* max 128 bytes */
const char* details; /* max 128 bytes */
typedef struct DiscordRichPresence
{
const char* state; /* max 128 bytes */
const char* details; /* max 128 bytes */
int64_t startTimestamp;
int64_t endTimestamp;
const char* largeImageKey; /* max 32 bytes */
@ -29,14 +30,16 @@ typedef struct DiscordRichPresence {
int8_t instance;
} DiscordRichPresence;
typedef struct DiscordUser {
typedef struct DiscordUser
{
const char* userId;
const char* username;
const char* discriminator;
const char* avatar;
} DiscordUser;
typedef struct DiscordEventHandlers {
typedef struct DiscordEventHandlers
{
void (*ready)(const DiscordUser* request);
void (*disconnected)(int errorCode, const char* message);
void (*errored)(int errorCode, const char* message);
@ -58,7 +61,8 @@ DISCORD_EXPORT void Discord_Shutdown(void);
/* checks for incoming messages, dispatches callbacks */
DISCORD_EXPORT void Discord_RunCallbacks(void);
/* If you disable the lib starting its own io thread, you'll need to call this from your own */
/* If you disable the lib starting its own I/O thread,
* you'll need to call this from your own */
#ifdef DISCORD_DISABLE_IO_THREAD
DISCORD_EXPORT void Discord_UpdateConnection(void);
#endif
@ -66,7 +70,8 @@ DISCORD_EXPORT void Discord_UpdateConnection(void);
DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence);
DISCORD_EXPORT void Discord_ClearPresence(void);
DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply);
DISCORD_EXPORT void Discord_Respond(const char* userid,
/* DISCORD_REPLY_ */ int reply);
DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers);

View File

@ -1,11 +1,11 @@
#pragma once
// This is to wrap the platform specific kinds of connect/read/write.
/* This is to wrap the platform specific kinds of connect/read/write. */
#include <stdint.h>
#include <stdlib.h>
// not really connectiony, but need per-platform
/* not really connectiony, but need per-platform */
int GetProcessId();
struct BaseConnection {

View File

@ -4,7 +4,6 @@
#define NOMCX
#define NOSERVICE
#define NOIME
#include <assert.h>
#include <windows.h>
int GetProcessId()
@ -48,7 +47,7 @@ bool BaseConnection::Open()
return true;
}
auto lastError = GetLastError();
DWORD lastError = GetLastError();
if (lastError == ERROR_FILE_NOT_FOUND)
{
if (pipeName[pipeDigit] < L'9')
@ -71,26 +70,24 @@ bool BaseConnection::Close()
{
auto self = reinterpret_cast<BaseConnectionWin*>(this);
::CloseHandle(self->pipe);
self->pipe = INVALID_HANDLE_VALUE;
self->pipe = INVALID_HANDLE_VALUE;
self->isOpen = false;
return true;
}
bool BaseConnection::Write(const void* data, size_t length)
{
DWORD bytesWritten = 0;
const DWORD bytesLength = (DWORD)length;
if (length == 0)
return true;
auto self = reinterpret_cast<BaseConnectionWin*>(this);
assert(self);
if (!self)
return false;
if (self->pipe == INVALID_HANDLE_VALUE)
return false;
assert(data);
if (!data)
return false;
const DWORD bytesLength = (DWORD)length;
DWORD bytesWritten = 0;
return ::WriteFile(self->pipe, data, bytesLength,
&bytesWritten, nullptr) == TRUE &&
bytesWritten == bytesLength;
@ -98,27 +95,27 @@ bool BaseConnection::Write(const void* data, size_t length)
bool BaseConnection::Read(void* data, size_t length)
{
assert(data);
DWORD bytesAvailable = 0;
if (!data)
return false;
auto self = reinterpret_cast<BaseConnectionWin*>(this);
assert(self);
if (!self)
return false;
if (self->pipe == INVALID_HANDLE_VALUE)
return false;
DWORD bytesAvailable = 0;
if (::PeekNamedPipe(self->pipe, nullptr, 0, nullptr, &bytesAvailable, nullptr)) {
if (::PeekNamedPipe(self->pipe, nullptr, 0, nullptr,
&bytesAvailable, nullptr))
{
if (bytesAvailable >= length)
{
DWORD bytesToRead = (DWORD)length;
DWORD bytesRead = 0;
if (::ReadFile(self->pipe, data, bytesToRead, &bytesRead, nullptr) == TRUE) {
assert(bytesToRead == bytesRead);
DWORD bytesRead = 0;
if (::ReadFile(self->pipe, data, bytesToRead,
&bytesRead, nullptr) == TRUE)
return true;
}
else
Close();
Close();
}
}
else

View File

@ -10,16 +10,8 @@
#include <unistd.h>
#include <boolean.h>
static bool Mkdir(const char* path)
{
int result = mkdir(path, 0755);
if (result == 0)
return true;
if (errno == EEXIST)
return true;
return false;
}
#include <file/file_path.h>
#include <compat/strl.h>
/* we want to register games so we can run them from
* Discord client as discord-<appid>:// */
@ -64,15 +56,15 @@ void Discord_Register(const char* applicationId, const char* command)
snprintf(desktopFilename, sizeof(desktopFilename), "/discord-%s.desktop", applicationId);
snprintf(desktopFilePath, sizeof(desktopFilePath), "%s/.local", home);
if (!Mkdir(desktopFilePath))
if (!path_mkdir(desktopFilePath))
return;
strcat(desktopFilePath, "/share");
if (!Mkdir(desktopFilePath))
strlcat(desktopFilePath, "/share", sizeof(desktopFilePath));
if (!path_mkdir(desktopFilePath))
return;
strcat(desktopFilePath, "/applications");
if (!Mkdir(desktopFilePath))
strlcat(desktopFilePath, "/applications", sizeof(desktopFilePath));
if (!path_mkdir(desktopFilePath))
return;
strcat(desktopFilePath, desktopFilename);
strlcat(desktopFilePath, desktopFilename, sizeof(desktopFilePath));
fp = fopen(desktopFilePath, "w");
if (!fp)
@ -95,6 +87,6 @@ void Discord_RegisterSteamGame(
const char* steamId)
{
char command[256];
sprintf(command, "xdg-open steam://rungameid/%s", steamId);
snprintf(command, sizeof(command), "xdg-open steam://rungameid/%s", steamId);
Discord_Register(applicationId, command);
}

View File

@ -7,26 +7,26 @@
static void RegisterCommand(const char* applicationId, const char* command)
{
// There does not appear to be a way to register arbitrary commands on OSX, so instead we'll save the command
// to a file in the Discord config path, and when it is needed, Discord can try to load the file there, open
// the command therein (will pass to js's window.open, so requires a url-like thing)
/* There does not appear to be a way to register arbitrary commands on OSX, so instead we'll save the command
* to a file in the Discord config path, and when it is needed, Discord can try to load the file there, open
* the command therein (will pass to js's window.open, so requires a url-like thing)
*/
// Note: will not work for sandboxed apps
NSString *home = NSHomeDirectory();
if (!home) {
return;
}
/* Note: will not work for sandboxed apps */
NSString *home = NSHomeDirectory();
if (!home)
return;
NSString *path = [[[[[[home stringByAppendingPathComponent:@"Library"]
stringByAppendingPathComponent:@"Application Support"]
stringByAppendingPathComponent:@"discord"]
stringByAppendingPathComponent:@"games"]
stringByAppendingPathComponent:[NSString stringWithUTF8String:applicationId]]
stringByAppendingPathExtension:@"json"];
[[NSFileManager defaultManager] createDirectoryAtPath:[path stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
NSString *path = [[[[[[home stringByAppendingPathComponent:@"Library"]
stringByAppendingPathComponent:@"Application Support"]
stringByAppendingPathComponent:@"discord"]
stringByAppendingPathComponent:@"games"]
stringByAppendingPathComponent:[NSString stringWithUTF8String:applicationId]]
stringByAppendingPathExtension:@"json"];
[[NSFileManager defaultManager] createDirectoryAtPath:[path stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
NSString *jsonBuffer = [NSString stringWithFormat:@"{\"command\": \"%s\"}", command];
[jsonBuffer writeToFile:path atomically:NO encoding:NSUTF8StringEncoding error:nil];
NSString *jsonBuffer = [NSString stringWithFormat:@"{\"command\": \"%s\"}", command];
[jsonBuffer writeToFile:path atomically:NO encoding:NSUTF8StringEncoding error:nil];
}
static void RegisterURL(const char* applicationId)
@ -34,38 +34,42 @@ static void RegisterURL(const char* applicationId)
char url[256];
snprintf(url, sizeof(url), "discord-%s", applicationId);
CFStringRef cfURL = CFStringCreateWithCString(NULL, url, kCFStringEncodingUTF8);
NSString* myBundleId = [[NSBundle mainBundle] bundleIdentifier];
if (!myBundleId) {
if (!myBundleId)
{
fprintf(stderr, "No bundle id found\n");
return;
}
NSURL* myURL = [[NSBundle mainBundle] bundleURL];
if (!myURL) {
if (!myURL)
{
fprintf(stderr, "No bundle url found\n");
return;
}
OSStatus status = LSSetDefaultHandlerForURLScheme(cfURL, (__bridge CFStringRef)myBundleId);
if (status != noErr) {
if (status != noErr)
{
fprintf(stderr, "Error in LSSetDefaultHandlerForURLScheme: %d\n", (int)status);
return;
}
status = LSRegisterURL((__bridge CFURLRef)myURL, true);
if (status != noErr) {
if (status != noErr)
{
fprintf(stderr, "Error in LSRegisterURL: %d\n", (int)status);
}
}
void Discord_Register(const char* applicationId, const char* command)
{
if (command) {
if (command)
RegisterCommand(applicationId, command);
}
else {
// raii lite
else
{
/* RAII Lite */
@autoreleasepool {
RegisterURL(applicationId);
}
@ -75,6 +79,6 @@ void Discord_Register(const char* applicationId, const char* command)
void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
{
char command[256];
snprintf(command, 256, "steam://rungameid/%s", steamId);
snprintf(command, sizeof(command), "steam://rungameid/%s", steamId);
Discord_Register(applicationId, command);
}

View File

@ -34,17 +34,20 @@ struct QueuedMessage
struct User
{
// snowflake (64bit int), turned into a ascii decimal string, at most 20 chars +1 null
// terminator = 21
/* snowflake (64bit int), turned into a ASCII decimal string,
* at most 20 chars +1 null
* terminator = 21 */
char userId[32];
// 32 unicode glyphs is max name size => 4 bytes per glyph in the worst case, +1 for null
// terminator = 129
/* 32 unicode glyphs is max name size => 4 bytes per glyph
* in the worst case, +1 for null
* terminator = 129 */
char username[344];
// 4 decimal digits + 1 null terminator = 5
/* 4 decimal digits + 1 null terminator = 5 */
char discriminator[8];
// optional 'a_' + md5 hex digest (32 bytes) + null terminator = 35
/* optional 'a_' + md5 hex digest (32 bytes) + null terminator = 35 */
char avatar[128];
// Rounded way up because I'm paranoid about games breaking from future changes in these sizes
/* Rounded way up because I'm paranoid about games breaking
* from future changes in these sizes */
};
static RpcConnection* Connection{nullptr};
@ -68,8 +71,9 @@ static MsgQueue<QueuedMessage, MessageQueueSize> SendQueue;
static MsgQueue<User, JoinQueueSize> JoinAskQueue;
static User connectedUser;
// We want to auto connect, and retry on failure, but not as fast as possible. This does expoential
// backoff from 0.5 seconds to 1 minute
/* We want to auto connect, and retry on failure,
* but not as fast as possible. This does exponential
* backoff from 0.5 seconds to 1 minute */
static Backoff ReconnectTimeMs(500, 60 * 1000);
static auto NextConnect = std::chrono::system_clock::now();
static int Pid{0};
@ -118,7 +122,8 @@ public:
void Stop() {}
void Notify() {}
};
#endif // DISCORD_DISABLE_IO_THREAD
#endif /* DISCORD_DISABLE_IO_THREAD */
static IoThreadHolder* IoThread{nullptr};
static void UpdateReconnectTime()
@ -146,7 +151,7 @@ static void Discord_UpdateConnection(void)
}
else
{
// reads
/* reads */
for (;;)
{
@ -160,7 +165,8 @@ static void Discord_UpdateConnection(void)
if (nonce)
{
// in responses only -- should use to match up response when needed.
/* in responses only --
* should use to match up response when needed. */
if (evtName && strcmp(evtName, "ERROR") == 0)
{
@ -172,8 +178,8 @@ static void Discord_UpdateConnection(void)
}
else
{
// should have evt == name of event, optional data
if (evtName == nullptr)
/* should have evt == name of event, optional data */
if (!evtName)
continue;
auto data = GetObjMember(&message, "data");
@ -198,11 +204,12 @@ static void Discord_UpdateConnection(void)
}
else if (strcmp(evtName, "ACTIVITY_JOIN_REQUEST") == 0)
{
auto user = GetObjMember(data, "user");
auto userId = GetStrMember(user, "id");
auto user = GetObjMember(data, "user");
auto userId = GetStrMember(user, "id");
auto username = GetStrMember(user, "username");
auto avatar = GetStrMember(user, "avatar");
auto joinReq = JoinAskQueue.GetNextAddMessage();
auto avatar = GetStrMember(user, "avatar");
auto joinReq = JoinAskQueue.GetNextAddMessage();
if (userId && username && joinReq)
{
StringCopy(joinReq->userId, userId);
@ -220,7 +227,7 @@ static void Discord_UpdateConnection(void)
}
}
// writes
/* writes */
if (QueuedPresence.length)
{
QueuedMessage local;
@ -229,8 +236,9 @@ static void Discord_UpdateConnection(void)
local.Copy(QueuedPresence);
QueuedPresence.length = 0;
}
if (!Connection->Write(local.buffer, local.length)) {
// if we fail to send, requeue
if (!Connection->Write(local.buffer, local.length))
{
/* if we fail to send, requeue */
std::lock_guard<std::mutex> guard(PresenceMutex);
QueuedPresence.Copy(local);
}
@ -263,7 +271,8 @@ static bool RegisterForEvent(const char* evtName)
static bool DeregisterForEvent(const char* evtName)
{
auto qmessage = SendQueue.GetNextAddMessage();
if (qmessage) {
if (qmessage)
{
qmessage->length =
JsonWriteUnsubscribeCommand(qmessage->buffer, sizeof(qmessage->buffer), Nonce++, evtName);
SendQueue.CommitAdd();
@ -274,13 +283,14 @@ static bool DeregisterForEvent(const char* evtName)
return false;
}
extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
DiscordEventHandlers* handlers,
int autoRegister,
const char* optionalSteamId)
extern "C" DISCORD_EXPORT void Discord_Initialize(
const char* applicationId,
DiscordEventHandlers* handlers,
int autoRegister,
const char* optionalSteamId)
{
IoThread = new (std::nothrow) IoThreadHolder();
if (IoThread == nullptr)
if (!IoThread)
return;
if (autoRegister)
@ -353,7 +363,7 @@ extern "C" DISCORD_EXPORT void Discord_Shutdown(void)
Connection->onConnect = nullptr;
Connection->onDisconnect = nullptr;
Handlers = {};
if (IoThread != nullptr)
if (IoThread)
{
IoThread->Stop();
delete IoThread;
@ -382,7 +392,7 @@ extern "C" DISCORD_EXPORT void Discord_ClearPresence(void)
extern "C" DISCORD_EXPORT void Discord_Respond(const char* userId, /* DISCORD_REPLY_ */ int reply)
{
// if we are not connected, let's not batch up stale messages for later
/* if we are not connected, let's not batch up stale messages for later */
if (!Connection || !Connection->IsOpen())
return;
auto qmessage = SendQueue.GetNextAddMessage();
@ -398,9 +408,10 @@ extern "C" DISCORD_EXPORT void Discord_Respond(const char* userId, /* DISCORD_RE
extern "C" DISCORD_EXPORT void Discord_RunCallbacks(void)
{
// Note on some weirdness: internally we might connect, get other signals, disconnect any number
// of times inbetween calls here. Externally, we want the sequence to seem sane, so any other
// signals are book-ended by calls to ready and disconnect.
/* Note on some weirdness: internally we might connect, get other signals, disconnect any number
* of times inbetween calls here. Externally, we want the sequence to seem sane, so any other
* signals are book-ended by calls to ready and disconnect.
*/
if (!Connection)
return;
@ -410,7 +421,7 @@ extern "C" DISCORD_EXPORT void Discord_RunCallbacks(void)
if (isConnected)
{
// if we are connected, disconnect cb first
/* if we are connected, disconnect cb first */
std::lock_guard<std::mutex> guard(HandlerMutex);
if (wasDisconnected && Handlers.disconnected)
Handlers.disconnected(LastDisconnectErrorCode, LastDisconnectErrorMessage);
@ -450,11 +461,12 @@ extern "C" DISCORD_EXPORT void Discord_RunCallbacks(void)
Handlers.spectateGame(SpectateGameSecret);
}
// Right now this batches up any requests and sends them all in a burst; I could imagine a world
// where the implementer would rather sequentially accept/reject each one before the next invite
// is sent. I left it this way because I could also imagine wanting to process these all and
// maybe show them in one common dialog and/or start fetching the avatars in parallel, and if
// not it should be trivial for the implementer to make a queue themselves.
/* Right now this batches up any requests and sends them all in a burst; I could imagine a world
* where the implementer would rather sequentially accept/reject each one before the next invite
* is sent. I left it this way because I could also imagine wanting to process these all and
* maybe show them in one common dialog and/or start fetching the avatars in parallel, and if
* not it should be trivial for the implementer to make a queue themselves.
*/
while (JoinAskQueue.HavePendingSends())
{
auto req = JoinAskQueue.GetNextSendMessage();
@ -471,7 +483,7 @@ extern "C" DISCORD_EXPORT void Discord_RunCallbacks(void)
if (!isConnected)
{
// if we are not connected, disconnect message last
/* if we are not connected, disconnect message last */
std::lock_guard<std::mutex> guard(HandlerMutex);
if (wasDisconnected && Handlers.disconnected)
Handlers.disconnected(LastDisconnectErrorCode, LastDisconnectErrorMessage);

View File

@ -54,7 +54,8 @@ void RpcConnection::Open()
sendFrame.length = (uint32_t)JsonWriteHandshakeObj(
sendFrame.message, sizeof(sendFrame.message), RpcVersion, appId);
if (connection->Write(&sendFrame, sizeof(MessageFrameHeader) + sendFrame.length))
if (connection->Write(&sendFrame,
sizeof(MessageFrameHeader) + sendFrame.length))
state = State::SentHandshake;
else
Close();
@ -84,12 +85,16 @@ bool RpcConnection::Write(const void* data, size_t length)
bool RpcConnection::Read(JsonDocument& message)
{
MessageFrame readFrame;
if (state != State::Connected && state != State::SentHandshake)
return false;
MessageFrame readFrame;
for (;;)
{
bool didRead = connection->Read(&readFrame, sizeof(MessageFrameHeader));
bool didRead = connection->Read(
&readFrame, sizeof(MessageFrameHeader));
if (!didRead)
{
if (!connection->isOpen)
@ -117,13 +122,11 @@ bool RpcConnection::Read(JsonDocument& message)
switch (readFrame.opcode)
{
case Opcode::Close:
{
message.ParseInsitu(readFrame.message);
lastErrorCode = GetIntMember(&message, "code");
StringCopy(lastErrorMessage, GetStrMember(&message, "message", ""));
Close();
return false;
}
message.ParseInsitu(readFrame.message);
lastErrorCode = GetIntMember(&message, "code");
StringCopy(lastErrorMessage, GetStrMember(&message, "message", ""));
Close();
return false;
case Opcode::Frame:
message.ParseInsitu(readFrame.message);
return true;
@ -136,7 +139,7 @@ bool RpcConnection::Read(JsonDocument& message)
break;
case Opcode::Handshake:
default:
// something bad happened
/* something bad happened */
lastErrorCode = (int)ErrorCode::ReadCorrupt;
StringCopy(lastErrorMessage, "Bad ipc frame");
Close();

View File

@ -3,57 +3,63 @@
#include "connection.h"
#include "serialization.h"
// I took this from the buffer size libuv uses for named pipes; I suspect ours would usually be much
// smaller.
/* I took this from the buffer size libuv uses for named pipes;
* I suspect ours would usually be much smaller. */
constexpr size_t MaxRpcFrameSize = 64 * 1024;
struct RpcConnection {
enum class ErrorCode : int {
Success = 0,
PipeClosed = 1,
ReadCorrupt = 2,
};
struct RpcConnection
{
enum class ErrorCode : int
{
Success = 0,
PipeClosed = 1,
ReadCorrupt = 2,
};
enum class Opcode : uint32_t {
Handshake = 0,
Frame = 1,
Close = 2,
Ping = 3,
Pong = 4,
};
enum class Opcode : uint32_t
{
Handshake = 0,
Frame = 1,
Close = 2,
Ping = 3,
Pong = 4,
};
struct MessageFrameHeader {
Opcode opcode;
uint32_t length;
};
struct MessageFrameHeader
{
Opcode opcode;
uint32_t length;
};
struct MessageFrame : public MessageFrameHeader {
char message[MaxRpcFrameSize - sizeof(MessageFrameHeader)];
};
struct MessageFrame : public MessageFrameHeader
{
char message[MaxRpcFrameSize - sizeof(MessageFrameHeader)];
};
enum class State : uint32_t {
Disconnected,
SentHandshake,
AwaitingResponse,
Connected,
};
enum class State : uint32_t
{
Disconnected,
SentHandshake,
AwaitingResponse,
Connected,
};
BaseConnection* connection{nullptr};
State state{State::Disconnected};
void (*onConnect)(JsonDocument& message){nullptr};
void (*onDisconnect)(int errorCode, const char* message){nullptr};
char appId[64]{};
int lastErrorCode{0};
char lastErrorMessage[256]{};
RpcConnection::MessageFrame sendFrame;
BaseConnection* connection{nullptr};
State state{State::Disconnected};
void (*onConnect)(JsonDocument& message){nullptr};
void (*onDisconnect)(int errorCode, const char* message){nullptr};
char appId[64]{};
int lastErrorCode{0};
char lastErrorMessage[256]{};
RpcConnection::MessageFrame sendFrame;
static RpcConnection* Create(const char* applicationId);
static void Destroy(RpcConnection*&);
static RpcConnection* Create(const char* applicationId);
static void Destroy(RpcConnection*&);
inline bool IsOpen() const { return state == State::Connected; }
inline bool IsOpen() const { return state == State::Connected; }
void Open();
void Close();
bool Write(const void* data, size_t length);
bool Read(JsonDocument& message);
void Open();
void Close();
bool Write(const void* data, size_t length);
bool Read(JsonDocument& message);
};

View File

@ -5,25 +5,30 @@
template <typename T>
void NumberToString(char* dest, T number)
{
if (!number) {
char temp[32];
int place = 0;
if (!number)
{
*dest++ = '0';
*dest++ = 0;
return;
}
if (number < 0) {
if (number < 0)
{
*dest++ = '-';
number = -number;
}
char temp[32];
int place = 0;
while (number) {
while (number)
{
auto digit = number % 10;
number = number / 10;
temp[place++] = '0' + (char)digit;
}
for (--place; place >= 0; --place) {
for (--place; place >= 0; --place)
*dest++ = temp[place];
}
*dest = 0;
}
@ -66,7 +71,8 @@ struct WriteArray {
template <typename T>
void WriteOptionalString(JsonWriter& w, T& k, const char* value)
{
if (value && value[0]) {
if (value && value[0])
{
w.Key(k, sizeof(T) - 1);
w.String(value);
}
@ -74,8 +80,8 @@ void WriteOptionalString(JsonWriter& w, T& k, const char* value)
static void JsonWriteNonce(JsonWriter& writer, int nonce)
{
WriteKey(writer, "nonce");
char nonceBuffer[32];
WriteKey(writer, "nonce");
NumberToString(nonceBuffer, nonce);
writer.String(nonceBuffer);
}
@ -102,24 +108,28 @@ size_t JsonWriteRichPresenceObj(char* dest,
WriteKey(writer, "pid");
writer.Int(pid);
if (presence != nullptr) {
if (presence)
{
WriteObject activity(writer, "activity");
WriteOptionalString(writer, "state", presence->state);
WriteOptionalString(writer, "details", presence->details);
if (presence->startTimestamp || presence->endTimestamp) {
WriteObject timestamps(writer, "timestamps");
if (presence->startTimestamp || presence->endTimestamp)
{
WriteObject timestamps(writer, "timestamps");
if (presence->startTimestamp) {
WriteKey(writer, "start");
writer.Int64(presence->startTimestamp);
}
if (presence->startTimestamp)
{
WriteKey(writer, "start");
writer.Int64(presence->startTimestamp);
}
if (presence->endTimestamp) {
WriteKey(writer, "end");
writer.Int64(presence->endTimestamp);
}
if (presence->endTimestamp)
{
WriteKey(writer, "end");
writer.Int64(presence->endTimestamp);
}
}
if ((presence->largeImageKey && presence->largeImageKey[0]) ||
@ -133,20 +143,25 @@ size_t JsonWriteRichPresenceObj(char* dest,
WriteOptionalString(writer, "small_text", presence->smallImageText);
}
if ((presence->partyId && presence->partyId[0]) || presence->partySize ||
presence->partyMax) {
WriteObject party(writer, "party");
WriteOptionalString(writer, "id", presence->partyId);
if (presence->partySize && presence->partyMax) {
WriteArray size(writer, "size");
writer.Int(presence->partySize);
writer.Int(presence->partyMax);
}
if ((
presence->partyId && presence->partyId[0]) ||
presence->partySize ||
presence->partyMax)
{
WriteObject party(writer, "party");
WriteOptionalString(writer, "id", presence->partyId);
if (presence->partySize && presence->partyMax)
{
WriteArray size(writer, "size");
writer.Int(presence->partySize);
writer.Int(presence->partyMax);
}
}
if ((presence->matchSecret && presence->matchSecret[0]) ||
(presence->joinSecret && presence->joinSecret[0]) ||
(presence->spectateSecret && presence->spectateSecret[0])) {
(presence->spectateSecret && presence->spectateSecret[0]))
{
WriteObject secrets(writer, "secrets");
WriteOptionalString(writer, "match", presence->matchSecret);
WriteOptionalString(writer, "join", presence->joinSecret);
@ -223,12 +238,10 @@ size_t JsonWriteJoinReply(char* dest, size_t maxLen, const char* userId, int rep
WriteObject obj(writer);
WriteKey(writer, "cmd");
if (reply == DISCORD_REPLY_YES) {
if (reply == DISCORD_REPLY_YES)
writer.String("SEND_ACTIVITY_JOIN_INVITE");
}
else {
else
writer.String("CLOSE_ACTIVITY_JOIN_REQUEST");
}
WriteKey(writer, "args");
{

View File

@ -5,12 +5,12 @@
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4061) // enum is not explicitly handled by a case label
#pragma warning(disable : 4365) // signed/unsigned mismatch
#pragma warning(disable : 4464) // relative include path contains
#pragma warning(disable : 4668) // is not defined as a preprocessor macro
#pragma warning(disable : 6313) // Incorrect operator
#endif // _MSC_VER
#pragma warning(disable : 4061) /* enum is not explicitly handled by a case label */
#pragma warning(disable : 4365) /* signed/unsigned mismatch */
#pragma warning(disable : 4464) /* relative include path contains */
#pragma warning(disable : 4668) /* is not defined as a preprocessor macro */
#pragma warning(disable : 6313) /* Incorrect operator */
#endif /* _MSC_VER */
#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
@ -18,27 +18,26 @@
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
#endif /* _MSC_VER */
// if only there was a standard library function for this
/* if only there was a standard library function for this */
template <size_t Len>
inline size_t StringCopy(char (&dest)[Len], const char* src)
{
if (!src || !Len) {
return 0;
}
size_t copied;
char* out = dest;
for (copied = 1; *src && copied < Len; ++copied) {
if (!src || !Len)
return 0;
for (copied = 1; *src && copied < Len; ++copied)
*out++ = *src++;
}
*out = 0;
return copied - 1;
}
size_t JsonWriteHandshakeObj(char* dest, size_t maxLen, int version, const char* applicationId);
size_t JsonWriteHandshakeObj(char* dest, size_t maxLen,
int version, const char* applicationId);
// Commands
/* Commands */
struct DiscordRichPresence;
size_t JsonWriteRichPresenceObj(char* dest,
size_t maxLen,
@ -51,50 +50,42 @@ size_t JsonWriteUnsubscribeCommand(char* dest, size_t maxLen, int nonce, const c
size_t JsonWriteJoinReply(char* dest, size_t maxLen, const char* userId, int reply, int nonce);
// I want to use as few allocations as I can get away with, and to do that with RapidJson, you need
// to supply some of your own allocators for stuff rather than use the defaults
/* I want to use as few allocations as I can get away with, and to do that with RapidJson, you need
* to supply some of your own allocators for stuff rather than use the defaults
*/
class LinearAllocator {
public:
char* buffer_;
char* end_;
LinearAllocator()
{
assert(0); // needed for some default case in rapidjson, should not use
}
LinearAllocator(char* buffer, size_t size)
: buffer_(buffer)
, end_(buffer + size)
{
}
static const bool kNeedFree = false;
void* Malloc(size_t size)
{
char* res = buffer_;
buffer_ += size;
if (buffer_ > end_) {
class LinearAllocator
{
public:
char* buffer_;
char* end_;
LinearAllocator() { }
LinearAllocator(char* buffer, size_t size)
: buffer_(buffer)
, end_(buffer + size) { }
static const bool kNeedFree = false;
void* Malloc(size_t size)
{
char* res = buffer_;
buffer_ += size;
if (buffer_ > end_)
{
buffer_ = res;
return nullptr;
}
return res;
}
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize)
{
if (newSize == 0) {
}
return res;
}
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize)
{
if (newSize == 0)
return nullptr;
}
// allocate how much you need in the first place
assert(!originalPtr && !originalSize);
// unused parameter warning
(void)(originalPtr);
(void)(originalSize);
return Malloc(newSize);
}
static void Free(void* ptr)
{
/* shrug */
(void)ptr;
}
return Malloc(newSize);
}
static void Free(void* ptr)
{
/* shrug */
(void)ptr;
}
};
template <size_t Size>
@ -102,13 +93,11 @@ class FixedLinearAllocator : public LinearAllocator {
public:
char fixedBuffer_[Size];
FixedLinearAllocator()
: LinearAllocator(fixedBuffer_, Size)
{
}
: LinearAllocator(fixedBuffer_, Size) { }
static const bool kNeedFree = false;
};
// wonder why this isn't a thing already, maybe I missed it
/* wonder why this isn't a thing already, maybe I missed it */
class DirectStringBuffer {
public:
using Ch = char;
@ -119,15 +108,12 @@ public:
DirectStringBuffer(char* buffer, size_t maxLen)
: buffer_(buffer)
, end_(buffer + maxLen)
, current_(buffer)
{
}
, current_(buffer) { }
void Put(char c)
{
if (current_ < end_) {
*current_++ = c;
}
if (current_ < end_)
*current_++ = c;
}
void Flush() {}
size_t GetSize() const { return (size_t)(current_ - buffer_); }
@ -136,80 +122,84 @@ public:
using MallocAllocator = rapidjson::CrtAllocator;
using PoolAllocator = rapidjson::MemoryPoolAllocator<MallocAllocator>;
using UTF8 = rapidjson::UTF8<char>;
// Writer appears to need about 16 bytes per nested object level (with 64bit size_t)
/* Writer appears to need about 16 bytes per nested object level (with 64bit size_t) */
using StackAllocator = FixedLinearAllocator<2048>;
constexpr size_t WriterNestingLevels = 2048 / (2 * sizeof(size_t));
using JsonWriterBase =
rapidjson::Writer<DirectStringBuffer, UTF8, UTF8, StackAllocator, rapidjson::kWriteNoFlags>;
class JsonWriter : public JsonWriterBase {
public:
DirectStringBuffer stringBuffer_;
StackAllocator stackAlloc_;
class JsonWriter : public JsonWriterBase
{
public:
DirectStringBuffer stringBuffer_;
StackAllocator stackAlloc_;
JsonWriter(char* dest, size_t maxLen)
: JsonWriterBase(stringBuffer_, &stackAlloc_, WriterNestingLevels)
, stringBuffer_(dest, maxLen)
, stackAlloc_()
{
}
JsonWriter(char* dest, size_t maxLen)
: JsonWriterBase(stringBuffer_, &stackAlloc_, WriterNestingLevels)
, stringBuffer_(dest, maxLen)
, stackAlloc_()
{
}
size_t Size() const { return stringBuffer_.GetSize(); }
size_t Size() const { return stringBuffer_.GetSize(); }
};
using JsonDocumentBase = rapidjson::GenericDocument<UTF8, PoolAllocator, StackAllocator>;
class JsonDocument : public JsonDocumentBase {
public:
static const int kDefaultChunkCapacity = 32 * 1024;
// json parser will use this buffer first, then allocate more if needed; I seriously doubt we
// send any messages that would use all of this, though.
char parseBuffer_[32 * 1024];
MallocAllocator mallocAllocator_;
PoolAllocator poolAllocator_;
StackAllocator stackAllocator_;
JsonDocument()
: JsonDocumentBase(rapidjson::kObjectType,
&poolAllocator_,
sizeof(stackAllocator_.fixedBuffer_),
&stackAllocator_)
, poolAllocator_(parseBuffer_, sizeof(parseBuffer_), kDefaultChunkCapacity, &mallocAllocator_)
, stackAllocator_()
{
}
class JsonDocument : public JsonDocumentBase
{
public:
static const int kDefaultChunkCapacity = 32 * 1024;
/* json parser will use this buffer first, then allocate more if needed; I seriously doubt we
* send any messages that would use all of this, though. */
char parseBuffer_[32 * 1024];
MallocAllocator mallocAllocator_;
PoolAllocator poolAllocator_;
StackAllocator stackAllocator_;
JsonDocument()
: JsonDocumentBase(rapidjson::kObjectType,
&poolAllocator_,
sizeof(stackAllocator_.fixedBuffer_),
&stackAllocator_)
, poolAllocator_(parseBuffer_, sizeof(parseBuffer_), kDefaultChunkCapacity, &mallocAllocator_)
, stackAllocator_()
{
}
};
using JsonValue = rapidjson::GenericValue<UTF8, PoolAllocator>;
inline JsonValue* GetObjMember(JsonValue* obj, const char* name)
{
if (obj) {
auto member = obj->FindMember(name);
if (member != obj->MemberEnd() && member->value.IsObject()) {
return &member->value;
}
}
return nullptr;
if (obj)
{
auto member = obj->FindMember(name);
if (member != obj->MemberEnd() && member->value.IsObject())
return &member->value;
}
return nullptr;
}
inline int GetIntMember(JsonValue* obj, const char* name, int notFoundDefault = 0)
inline int GetIntMember(JsonValue* obj,
const char* name,
int notFoundDefault = 0)
{
if (obj) {
auto member = obj->FindMember(name);
if (member != obj->MemberEnd() && member->value.IsInt()) {
return member->value.GetInt();
}
}
return notFoundDefault;
if (obj)
{
auto member = obj->FindMember(name);
if (member != obj->MemberEnd() && member->value.IsInt())
return member->value.GetInt();
}
return notFoundDefault;
}
inline const char* GetStrMember(JsonValue* obj,
const char* name,
const char* notFoundDefault = nullptr)
const char* name,
const char* notFoundDefault = nullptr)
{
if (obj) {
auto member = obj->FindMember(name);
if (member != obj->MemberEnd() && member->value.IsString()) {
return member->value.GetString();
}
}
return notFoundDefault;
if (obj)
{
auto member = obj->FindMember(name);
if (member != obj->MemberEnd() && member->value.IsString())
return member->value.GetString();
}
return notFoundDefault;
}

View File

@ -149,7 +149,7 @@ static bool discord_download_avatar(
strlcpy(transf->path, buf, sizeof(transf->path));
RARCH_LOG("[discord] downloading avatar from: %s\n", url_encoded);
task_push_http_transfer(url_encoded, true, NULL, cb_generic_download, transf);
task_push_http_transfer_file(url_encoded, true, NULL, cb_generic_download, transf);
return false;
}

774
disk_control_interface.c Normal file
View File

@ -0,0 +1,774 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (disk_control_interface.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <string/stdstring.h>
#include <file/file_path.h>
#include "paths.h"
#include "retroarch.h"
#include "verbosity.h"
#include "msg_hash.h"
#include "disk_control_interface.h"
/*****************/
/* Configuration */
/*****************/
/* Sets all disk interface callback functions
* to NULL */
static void disk_control_reset_callback(
disk_control_interface_t *disk_control)
{
if (!disk_control)
return;
memset(&disk_control->cb, 0, sizeof(struct retro_disk_control_ext_callback));
}
/* Set v0 disk interface callback functions */
void disk_control_set_callback(
disk_control_interface_t *disk_control,
const struct retro_disk_control_callback *cb)
{
if (!disk_control)
return;
disk_control_reset_callback(disk_control);
if (!cb)
return;
disk_control->cb.set_eject_state = cb->set_eject_state;
disk_control->cb.get_eject_state = cb->get_eject_state;
disk_control->cb.get_image_index = cb->get_image_index;
disk_control->cb.set_image_index = cb->set_image_index;
disk_control->cb.get_num_images = cb->get_num_images;
disk_control->cb.replace_image_index = cb->replace_image_index;
disk_control->cb.add_image_index = cb->add_image_index;
}
/* Set v1+ disk interface callback functions */
void disk_control_set_ext_callback(
disk_control_interface_t *disk_control,
const struct retro_disk_control_ext_callback *cb)
{
if (!disk_control)
return;
disk_control_reset_callback(disk_control);
if (!cb)
return;
disk_control->cb.set_eject_state = cb->set_eject_state;
disk_control->cb.get_eject_state = cb->get_eject_state;
disk_control->cb.get_image_index = cb->get_image_index;
disk_control->cb.set_image_index = cb->set_image_index;
disk_control->cb.get_num_images = cb->get_num_images;
disk_control->cb.replace_image_index = cb->replace_image_index;
disk_control->cb.add_image_index = cb->add_image_index;
disk_control->cb.set_initial_image = cb->set_initial_image;
disk_control->cb.get_image_path = cb->get_image_path;
disk_control->cb.get_image_label = cb->get_image_label;
}
/**********/
/* Status */
/**********/
/* Returns true if core supports basic disk
* control functionality
* - set_eject_state
* - get_eject_state
* - get_image_index
* - set_image_index
* - get_num_images */
bool disk_control_enabled(
disk_control_interface_t *disk_control)
{
if (!disk_control)
return false;
if (disk_control->cb.set_eject_state &&
disk_control->cb.get_eject_state &&
disk_control->cb.get_image_index &&
disk_control->cb.set_image_index &&
disk_control->cb.get_num_images)
return true;
return false;
}
/* Returns true if core supports disk append
* functionality
* - replace_image_index
* - add_image_index */
bool disk_control_append_enabled(
disk_control_interface_t *disk_control)
{
if (!disk_control)
return false;
if (disk_control->cb.replace_image_index &&
disk_control->cb.add_image_index)
return true;
return false;
}
/* Returns true if core supports image
* labels
* - get_image_label */
bool disk_control_image_label_enabled(
disk_control_interface_t *disk_control)
{
if (!disk_control)
return false;
if (disk_control->cb.get_image_label)
return true;
return false;
}
/* Returns true if core supports setting
* initial disk index
* - set_initial_image
* - get_image_path */
bool disk_control_initial_image_enabled(
disk_control_interface_t *disk_control)
{
if (!disk_control)
return false;
if (disk_control->cb.set_initial_image &&
disk_control->cb.get_image_path)
return true;
return false;
}
/***********/
/* Getters */
/***********/
/* Returns true if disk is currently ejected */
bool disk_control_get_eject_state(
disk_control_interface_t *disk_control)
{
if (!disk_control)
return false;
if (!disk_control->cb.get_eject_state)
return false;
return disk_control->cb.get_eject_state();
}
/* Returns number of disk images registered
* by the core */
unsigned disk_control_get_num_images(
disk_control_interface_t *disk_control)
{
if (!disk_control)
return 0;
if (!disk_control->cb.get_num_images)
return 0;
return disk_control->cb.get_num_images();
}
/* Returns currently selected disk image index */
unsigned disk_control_get_image_index(
disk_control_interface_t *disk_control)
{
if (!disk_control)
return 0;
if (!disk_control->cb.get_image_index)
return 0;
return disk_control->cb.get_image_index();
}
/* Fetches core-provided disk image label
* (label is set to an empty string if core
* does not support image labels) */
void disk_control_get_image_label(
disk_control_interface_t *disk_control,
unsigned index, char *label, size_t len)
{
if (!label || len < 1)
return;
if (!disk_control)
goto error;
if (!disk_control->cb.get_image_label)
goto error;
if (!disk_control->cb.get_image_label(index, label, len))
goto error;
return;
error:
label[0] = '\0';
return;
}
/***********/
/* Setters */
/***********/
/* Sets the eject state of the virtual disk tray */
bool disk_control_set_eject_state(
disk_control_interface_t *disk_control,
bool eject, bool verbose)
{
bool error = false;
char msg[128];
msg[0] = '\0';
if (!disk_control)
return false;
if (!disk_control->cb.set_eject_state)
return false;
/* Set eject state */
if (disk_control->cb.set_eject_state(eject))
snprintf(
msg, sizeof(msg), "%s %s",
eject ? msg_hash_to_str(MSG_DISK_EJECTED) :
msg_hash_to_str(MSG_DISK_CLOSED),
msg_hash_to_str(MSG_VIRTUAL_DISK_TRAY));
else
{
error = true;
snprintf(
msg, sizeof(msg), "%s %s %s",
msg_hash_to_str(MSG_FAILED_TO),
eject ? msg_hash_to_str(MSG_VIRTUAL_DISK_TRAY_EJECT) :
msg_hash_to_str(MSG_VIRTUAL_DISK_TRAY_CLOSE),
msg_hash_to_str(MSG_VIRTUAL_DISK_TRAY));
}
if (!string_is_empty(msg))
{
if (error)
RARCH_ERR("%s\n", msg);
else
RARCH_LOG("%s\n", msg);
/* Errors should always be displayed */
if (verbose || error)
runloop_msg_queue_push(
msg, 1, error ? 180 : 60,
true, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
}
return !error;
}
/* Sets currently selected disk index
* NOTE: Will fail if disk is not currently ejected */
bool disk_control_set_index(
disk_control_interface_t *disk_control,
unsigned index, bool verbose)
{
bool error = false;
unsigned num_images = 0;
char msg[128];
msg[0] = '\0';
if (!disk_control)
return false;
if (!disk_control->cb.get_eject_state ||
!disk_control->cb.get_num_images ||
!disk_control->cb.set_image_index)
return false;
/* Ensure that disk is currently ejected */
if (!disk_control->cb.get_eject_state())
return false;
num_images = disk_control->cb.get_num_images();
if (disk_control->cb.set_image_index(index))
{
if (index < num_images)
snprintf(
msg, sizeof(msg), "%s: %u/%u",
msg_hash_to_str(MSG_SETTING_DISK_IN_TRAY),
index + 1, num_images);
else
strlcpy(
msg,
msg_hash_to_str(MSG_REMOVED_DISK_FROM_TRAY),
sizeof(msg));
}
else
{
error = true;
if (index < num_images)
snprintf(
msg, sizeof(msg), "%s %u/%u",
msg_hash_to_str(MSG_FAILED_TO_SET_DISK),
index + 1, num_images);
else
strlcpy(
msg,
msg_hash_to_str(MSG_FAILED_TO_REMOVE_DISK_FROM_TRAY),
sizeof(msg));
}
if (!string_is_empty(msg))
{
if (error)
RARCH_ERR("%s\n", msg);
else
RARCH_LOG("%s\n", msg);
/* Errors should always be displayed */
if (verbose || error)
runloop_msg_queue_push(
msg, 1, error ? 180 : 60,
true, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
}
/* If operation was successful, update disk
* index record (if enabled) */
if (!error && disk_control->record_enabled)
{
if (disk_control->cb.get_image_index &&
disk_control->cb.get_image_path)
{
bool image_path_valid = false;
unsigned new_image_index = 0;
char new_image_path[PATH_MAX_LENGTH];
new_image_path[0] = '\0';
/* Get current image index + path */
new_image_index = disk_control->cb.get_image_index();
image_path_valid = disk_control->cb.get_image_path(
new_image_index, new_image_path, sizeof(new_image_path));
if (image_path_valid)
disk_index_file_set(
&disk_control->index_record,
new_image_index, new_image_path);
else
disk_index_file_set(
&disk_control->index_record, 0, NULL);
}
}
return !error;
}
/* Increments selected disk index */
bool disk_control_set_index_next(
disk_control_interface_t *disk_control,
bool verbose)
{
unsigned num_images = 0;
unsigned image_index = 0;
bool disk_next_enable = false;
if (!disk_control)
return false;
if (!disk_control->cb.get_num_images ||
!disk_control->cb.get_image_index)
return false;
num_images = disk_control->cb.get_num_images();
image_index = disk_control->cb.get_image_index();
/* Would seem more sensible to check (num_images > 1)
* here, but seems we need to be able to cycle the
* same image for legacy reasons... */
disk_next_enable = (num_images > 0) && (num_images != UINT_MAX);
if (!disk_next_enable)
{
RARCH_ERR("%s.\n", msg_hash_to_str(MSG_GOT_INVALID_DISK_INDEX));
return false;
}
if (image_index < (num_images - 1))
image_index++;
return disk_control_set_index(disk_control, image_index, verbose);
}
/* Decrements selected disk index */
bool disk_control_set_index_prev(
disk_control_interface_t *disk_control,
bool verbose)
{
unsigned num_images = 0;
unsigned image_index = 0;
bool disk_prev_enable = false;
if (!disk_control)
return false;
if (!disk_control->cb.get_num_images ||
!disk_control->cb.get_image_index)
return false;
num_images = disk_control->cb.get_num_images();
image_index = disk_control->cb.get_image_index();
/* Would seem more sensible to check (num_images > 1)
* here, but seems we need to be able to cycle the
* same image for legacy reasons... */
disk_prev_enable = (num_images > 0);
if (!disk_prev_enable)
{
RARCH_ERR("%s.\n", msg_hash_to_str(MSG_GOT_INVALID_DISK_INDEX));
return false;
}
if (image_index > 0)
image_index--;
return disk_control_set_index(disk_control, image_index, verbose);
}
/* Appends specified image file to disk image list */
bool disk_control_append_image(
disk_control_interface_t *disk_control,
const char *image_path)
{
unsigned initial_index = 0;
unsigned new_index = 0;
const char *image_filename = NULL;
struct retro_game_info info = {0};
char msg[128];
msg[0] = '\0';
/* Sanity check. If any of these fail then a
* frontend error has occurred - we will not
* deal with that here */
if (!disk_control)
return false;
if (!disk_control->cb.get_image_index ||
!disk_control->cb.get_num_images ||
!disk_control->cb.add_image_index ||
!disk_control->cb.replace_image_index ||
!disk_control->cb.get_eject_state)
return false;
if (string_is_empty(image_path))
return false;
image_filename = path_basename(image_path);
if (string_is_empty(image_filename))
return false;
/* Cache initial image index */
initial_index = disk_control->cb.get_image_index();
/* Eject disk */
if (!disk_control_set_eject_state(disk_control, true, false))
goto error;
/* Append image */
if (!disk_control->cb.add_image_index())
goto error;
new_index = disk_control->cb.get_num_images();
if (new_index < 1)
goto error;
new_index--;
info.path = image_path;
if (!disk_control->cb.replace_image_index(new_index, &info))
goto error;
/* Set new index */
if (!disk_control_set_index(disk_control, new_index, false))
goto error;
/* Insert disk */
if (!disk_control_set_eject_state(disk_control, false, false))
goto error;
/* Display log */
snprintf(
msg, sizeof(msg), "%s: %s",
msg_hash_to_str(MSG_APPENDED_DISK), image_filename);
RARCH_LOG("%s\n", msg);
/* This message should always be displayed, since
* the menu itself does not provide sufficient
* visual feedback */
runloop_msg_queue_push(
msg, 0, 120,
true, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return true;
error:
/* If we reach this point then everything is
* broken and the disk control interface is
* in an undefined state. Try to restore some
* sanity by reinserting the original disk...
* NOTE: If this fails then it's game over -
* just display the error notification and
* hope for the best... */
if (!disk_control->cb.get_eject_state())
disk_control_set_eject_state(disk_control, true, false);
disk_control_set_index(disk_control, initial_index, false);
disk_control_set_eject_state(disk_control, false, false);
snprintf(
msg, sizeof(msg), "%s: %s",
msg_hash_to_str(MSG_FAILED_TO_APPEND_DISK), image_filename);
runloop_msg_queue_push(
msg, 0, 180,
true, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return false;
}
/*****************************/
/* 'Initial index' functions */
/*****************************/
/* Attempts to set current core's initial disk index.
* > disk_control->record_enabled will be set to
* 'false' if core does not support initial
* index functionality
* > disk_control->index_record will be loaded
* from file (if an existing record is found)
* NOTE: Must be called immediately before
* loading content */
bool disk_control_set_initial_index(
disk_control_interface_t *disk_control,
const char *content_path,
const char *dir_savefile)
{
if (!disk_control)
return false;
if (string_is_empty(content_path))
goto error;
/* Check that 'initial index' functionality is enabled */
if (!disk_control->cb.set_initial_image ||
!disk_control->cb.get_num_images ||
!disk_control->cb.get_image_index ||
!disk_control->cb.get_image_path)
goto error;
/* Attempt to initialise disk index record (reading
* from disk, if file exists) */
disk_control->record_enabled = disk_index_file_init(
&disk_control->index_record,
content_path, dir_savefile);
/* If record is enabled and initial index is *not*
* zero, notify current core */
if (disk_control->record_enabled &&
(disk_control->index_record.image_index != 0))
{
if (!disk_control->cb.set_initial_image(
disk_control->index_record.image_index,
disk_control->index_record.image_path))
{
/* Note: We don't bother with an on-screen
* notification at this stage, since an error
* here may not matter (have to wait until
* disk index is verified) */
RARCH_ERR(
"Failed to set initial disk index: [%u] %s\n",
disk_control->index_record.image_index,
disk_control->index_record.image_path);
return false;
}
}
return true;
error:
disk_control->record_enabled = false;
return false;
}
/* Checks that initial index has been set correctly
* and provides user notification.
* > Sets disk_control->initial_num_images if
* if functionality is supported by core
* NOTE: Must be called immediately after
* loading content */
bool disk_control_verify_initial_index(disk_control_interface_t *disk_control)
{
bool success = false;
unsigned image_index = 0;
char image_path[PATH_MAX_LENGTH];
image_path[0] = '\0';
if (!disk_control)
return false;
/* If index record is disabled, can return immediately */
if (!disk_control->record_enabled)
return false;
/* Check that 'initial index' functionality is enabled */
if (!disk_control->cb.set_initial_image ||
!disk_control->cb.get_num_images ||
!disk_control->cb.get_image_index ||
!disk_control->cb.get_image_path)
return false;
/* Cache initial number of images
* (required for error checking when saving
* disk index file) */
disk_control->initial_num_images =
disk_control->cb.get_num_images();
/* Get current image index + path */
image_index = disk_control->cb.get_image_index();
if (disk_control->cb.get_image_path(
image_index, image_path, sizeof(image_path)))
{
/* Check whether index + path match set
* values
* > Note that if set index was zero and
* set path was empty, we ignore the path
* read here (since this corresponds to a
* 'first run', where no existing disk index
* file was present) */
if ((image_index == disk_control->index_record.image_index) &&
(string_is_equal(image_path, disk_control->index_record.image_path) ||
((disk_control->index_record.image_index == 0) &&
string_is_empty(disk_control->index_record.image_path))))
success = true;
}
/* If current disk is incorrect, notify user */
if (!success)
{
RARCH_ERR(
"Failed to set initial disk index:\n> Expected [%u] %s\n> Detected [%u] %s\n",
disk_control->index_record.image_index + 1,
disk_control->index_record.image_path,
image_index + 1,
image_path);
runloop_msg_queue_push(
msg_hash_to_str(MSG_FAILED_TO_SET_INITIAL_DISK),
0, 60,
true, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
/* Since a failure here typically means that the
* original M3U content file has been altered,
* any existing disk index record file will be
* invalid. We therefore 'reset' and save the disk
* index record to prevent a repeat of the error on
* the next run */
disk_index_file_set(&disk_control->index_record, 0, NULL);
disk_index_file_save(&disk_control->index_record);
}
/* If current disk is correct and recorded image
* path is empty (i.e. first run), need to register
* current image path */
else if (string_is_empty(disk_control->index_record.image_path))
disk_index_file_set(
&disk_control->index_record, image_index, image_path);
/* Regardless of success/failure, notify user of
* current disk index *if* more than one disk
* is available */
if (disk_control->initial_num_images > 1)
{
char msg[128];
msg[0] = '\0';
snprintf(
msg, sizeof(msg), "%s: %u/%u",
msg_hash_to_str(MSG_SETTING_DISK_IN_TRAY),
image_index + 1, disk_control->initial_num_images);
RARCH_LOG("%s\n", msg);
runloop_msg_queue_push(
msg,
0, 60,
true, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
}
return success;
}
/* Saves current disk index to file, if supported
* by current core */
bool disk_control_save_image_index(disk_control_interface_t *disk_control)
{
if (!disk_control)
return false;
/* If index record is disabled, can return immediately */
if (!disk_control->record_enabled)
return false;
/* If core started with less than two disks,
* then a disk index record is unnecessary */
if (disk_control->initial_num_images < 2)
return false;
/* If current index is greater than initial
* number of disks then user has appended a
* disk and it is currently active. This setup
* *cannot* be restored, so cancel the file save */
if (disk_control->index_record.image_index >=
disk_control->initial_num_images)
return false;
/* Save record */
return disk_index_file_save(&disk_control->index_record);
}

178
disk_control_interface.h Normal file
View File

@ -0,0 +1,178 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (disk_control_interface.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __DISK_CONTROL_INTERFACE_H
#define __DISK_CONTROL_INTERFACE_H
#include <retro_common_api.h>
#include <libretro.h>
#include <boolean.h>
#include "disk_index_file.h"
RETRO_BEGIN_DECLS
/* Holds all all objects to operate the disk
* control interface */
typedef struct
{
bool record_enabled;
disk_index_file_t index_record;
unsigned initial_num_images;
struct retro_disk_control_ext_callback cb;
} disk_control_interface_t;
/*****************/
/* Configuration */
/*****************/
/* Set v0 disk interface callback functions */
void disk_control_set_callback(
disk_control_interface_t *disk_control,
const struct retro_disk_control_callback *cb);
/* Set v1+ disk interface callback functions */
void disk_control_set_ext_callback(
disk_control_interface_t *disk_control,
const struct retro_disk_control_ext_callback *cb);
/**********/
/* Status */
/**********/
/* Returns true if core supports basic disk
* control functionality
* - set_eject_state
* - get_eject_state
* - get_image_index
* - set_image_index
* - get_num_images */
bool disk_control_enabled(
disk_control_interface_t *disk_control);
/* Returns true if core supports disk append
* functionality
* - replace_image_index
* - add_image_index */
bool disk_control_append_enabled(
disk_control_interface_t *disk_control);
/* Returns true if core supports image
* labels
* - get_image_label */
bool disk_control_image_label_enabled(
disk_control_interface_t *disk_control);
/* Returns true if core supports setting
* initial disk index
* - set_initial_image
* - get_image_path */
bool disk_control_initial_image_enabled(
disk_control_interface_t *disk_control);
/***********/
/* Getters */
/***********/
/* Returns true if disk is currently ejected */
bool disk_control_get_eject_state(
disk_control_interface_t *disk_control);
/* Returns number of disk images registered
* by the core */
unsigned disk_control_get_num_images(
disk_control_interface_t *disk_control);
/* Returns currently selected disk image index */
unsigned disk_control_get_image_index(
disk_control_interface_t *disk_control);
/* Fetches core-provided disk image label
* (label is set to an empty string if core
* does not support image labels) */
void disk_control_get_image_label(
disk_control_interface_t *disk_control,
unsigned index, char *label, size_t len);
/***********/
/* Setters */
/***********/
/* Sets the eject state of the virtual disk tray */
bool disk_control_set_eject_state(
disk_control_interface_t *disk_control,
bool eject, bool verbose);
/* Sets currently selected disk index
* NOTE: Will fail if disk is not currently ejected */
bool disk_control_set_index(
disk_control_interface_t *disk_control,
unsigned index, bool verbose);
/* Increments selected disk index */
bool disk_control_set_index_next(
disk_control_interface_t *disk_control,
bool verbose);
/* Decrements selected disk index */
bool disk_control_set_index_prev(
disk_control_interface_t *disk_control,
bool verbose);
/* Appends specified image file to disk image list */
bool disk_control_append_image(
disk_control_interface_t *disk_control,
const char *image_path);
/*****************************/
/* 'Initial index' functions */
/*****************************/
/* Attempts to set current core's initial disk index.
* > disk_control->record_enabled will be set to
* 'false' if core does not support initial
* index functionality
* > disk_control->index_record will be loaded
* from file (if an existing record is found)
* NOTE: Must be called immediately before
* loading content */
bool disk_control_set_initial_index(
disk_control_interface_t *disk_control,
const char *content_path,
const char *dir_savefile);
/* Checks that initial index has been set correctly
* and provides user notification.
* > Sets disk_control->initial_num_images if
* if functionality is supported by core
* NOTE: Must be called immediately after
* loading content */
bool disk_control_verify_initial_index(disk_control_interface_t *disk_control);
/* Saves current disk index to file, if supported
* by current core */
bool disk_control_save_image_index(disk_control_interface_t *disk_control);
RETRO_END_DECLS
#endif

523
disk_index_file.c Normal file
View File

@ -0,0 +1,523 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (disk_index_file.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <file/file_path.h>
#include <string/stdstring.h>
#include <streams/file_stream.h>
#include <formats/jsonsax_full.h>
#include "file_path_special.h"
#include "verbosity.h"
#include "msg_hash.h"
#include "disk_index_file.h"
/****************/
/* JSON Helpers */
/****************/
typedef struct
{
JSON_Parser parser;
JSON_Writer writer;
RFILE *file;
unsigned *current_entry_uint_val;
char **current_entry_str_val;
unsigned image_index;
char *image_path;
} DCifJSONContext;
static JSON_Parser_HandlerResult DCifJSONObjectMemberHandler(JSON_Parser parser, char *pValue, size_t length, JSON_StringAttributes attributes)
{
DCifJSONContext *pCtx = (DCifJSONContext*)JSON_Parser_GetUserData(parser);
(void)attributes; /* unused */
if (pCtx->current_entry_str_val)
{
/* something went wrong */
RARCH_ERR("[disk index file] JSON parsing failed at line %d.\n", __LINE__);
return JSON_Parser_Abort;
}
if (length)
{
if (string_is_equal(pValue, "image_index"))
pCtx->current_entry_uint_val = &pCtx->image_index;
else if (string_is_equal(pValue, "image_path"))
pCtx->current_entry_str_val = &pCtx->image_path;
/* ignore unknown members */
}
return JSON_Parser_Continue;
}
static JSON_Parser_HandlerResult DCifJSONNumberHandler(JSON_Parser parser, char *pValue, size_t length, JSON_StringAttributes attributes)
{
DCifJSONContext *pCtx = (DCifJSONContext*)JSON_Parser_GetUserData(parser);
(void)attributes; /* unused */
if (pCtx->current_entry_uint_val && length && !string_is_empty(pValue))
*pCtx->current_entry_uint_val = string_to_unsigned(pValue);
/* ignore unknown members */
pCtx->current_entry_uint_val = NULL;
return JSON_Parser_Continue;
}
static JSON_Parser_HandlerResult DCifJSONStringHandler(JSON_Parser parser, char *pValue, size_t length, JSON_StringAttributes attributes)
{
DCifJSONContext *pCtx = (DCifJSONContext*)JSON_Parser_GetUserData(parser);
(void)attributes; /* unused */
if (pCtx->current_entry_str_val && length && !string_is_empty(pValue))
{
if (*pCtx->current_entry_str_val)
free(*pCtx->current_entry_str_val);
*pCtx->current_entry_str_val = strdup(pValue);
}
/* ignore unknown members */
pCtx->current_entry_str_val = NULL;
return JSON_Parser_Continue;
}
static JSON_Writer_HandlerResult DCifJSONOutputHandler(JSON_Writer writer, const char *pBytes, size_t length)
{
DCifJSONContext *context = (DCifJSONContext*)JSON_Writer_GetUserData(writer);
(void)writer; /* unused */
return filestream_write(context->file, pBytes, length) == length ? JSON_Writer_Continue : JSON_Writer_Abort;
}
static void DCifJSONLogError(DCifJSONContext *pCtx)
{
if (pCtx->parser && JSON_Parser_GetError(pCtx->parser) != JSON_Error_AbortedByHandler)
{
JSON_Error error = JSON_Parser_GetError(pCtx->parser);
JSON_Location errorLocation = { 0, 0, 0 };
(void)JSON_Parser_GetErrorLocation(pCtx->parser, &errorLocation);
RARCH_ERR("[disk index file] Error: Invalid JSON at line %d, column %d (input byte %d) - %s.\n",
(int)errorLocation.line + 1,
(int)errorLocation.column + 1,
(int)errorLocation.byte,
JSON_ErrorString(error));
}
else if (pCtx->writer && JSON_Writer_GetError(pCtx->writer) != JSON_Error_AbortedByHandler)
{
RARCH_ERR("[disk index file] Error: could not write output - %s.\n", JSON_ErrorString(JSON_Writer_GetError(pCtx->writer)));
}
}
/******************/
/* Initialisation */
/******************/
/* Resets existing disk index record */
static void disk_index_file_reset(disk_index_file_t *disk_index_file)
{
if (!disk_index_file)
return;
disk_index_file->modified = false;
disk_index_file->image_index = 0;
disk_index_file->image_path[0] = '\0';
disk_index_file->file_path[0] = '\0';
}
/* Parses disk index file referenced by
* disk_index_file->file_path.
* Does nothing if disk index file does not exist. */
static bool disk_index_file_read(disk_index_file_t *disk_index_file)
{
bool success = false;
DCifJSONContext context = {0};
RFILE *file = NULL;
/* Sanity check */
if (!disk_index_file)
return false;
if (string_is_empty(disk_index_file->file_path))
return false;
if (!path_is_valid(disk_index_file->file_path))
return false;
/* Attempt to open disk index file */
file = filestream_open(
disk_index_file->file_path,
RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!file)
{
RARCH_ERR(
"[disk index file] Failed to open disk index record file: %s\n",
disk_index_file->file_path);
return false;
}
/* Initialise JSON parser */
context.image_index = 0;
context.image_path = NULL;
context.parser = JSON_Parser_Create(NULL);
context.file = file;
if (!context.parser)
{
RARCH_ERR("[disk index file] Failed to create JSON parser.\n");
goto end;
}
/* Configure parser */
JSON_Parser_SetAllowBOM(context.parser, JSON_True);
JSON_Parser_SetNumberHandler(context.parser, &DCifJSONNumberHandler);
JSON_Parser_SetStringHandler(context.parser, &DCifJSONStringHandler);
JSON_Parser_SetObjectMemberHandler(context.parser, &DCifJSONObjectMemberHandler);
JSON_Parser_SetUserData(context.parser, &context);
/* Read file */
while (!filestream_eof(file))
{
/* Disk index files are tiny - use small chunk size */
char chunk[128] = {0};
int64_t length = filestream_read(file, chunk, sizeof(chunk));
/* Error checking... */
if (!length && !filestream_eof(file))
{
RARCH_ERR(
"[disk index file] Failed to read disk index file: %s\n",
disk_index_file->file_path);
JSON_Parser_Free(context.parser);
goto end;
}
/* Parse chunk */
if (!JSON_Parser_Parse(context.parser, chunk, length, JSON_False))
{
RARCH_ERR(
"[disk index file] Error parsing chunk of disk index file: %s\n---snip---\n%s\n---snip---\n",
disk_index_file->file_path, chunk);
DCifJSONLogError(&context);
JSON_Parser_Free(context.parser);
goto end;
}
}
/* Finalise parsing */
if (!JSON_Parser_Parse(context.parser, NULL, 0, JSON_True))
{
RARCH_WARN(
"[disk index file] Error parsing disk index file: %s\n",
disk_index_file->file_path);
DCifJSONLogError(&context);
JSON_Parser_Free(context.parser);
goto end;
}
/* Free parser */
JSON_Parser_Free(context.parser);
/* Copy values read from JSON file */
disk_index_file->image_index = context.image_index;
if (!string_is_empty(context.image_path))
strlcpy(
disk_index_file->image_path, context.image_path,
sizeof(disk_index_file->image_path));
else
disk_index_file->image_path[0] = '\0';
success = true;
end:
/* Clean up leftover strings */
if (context.image_path)
free(context.image_path);
/* Close log file */
filestream_close(file);
return success;
}
/* Initialises existing disk index record, loading
* current parameters if a record file exists.
* Returns false if arguments are invalid. */
bool disk_index_file_init(
disk_index_file_t *disk_index_file,
const char *content_path,
const char *dir_savefile)
{
const char *content_file = NULL;
char content_name[PATH_MAX_LENGTH];
char disk_index_file_dir[PATH_MAX_LENGTH];
char disk_index_file_path[PATH_MAX_LENGTH];
content_name[0] = '\0';
disk_index_file_dir[0] = '\0';
disk_index_file_path[0] = '\0';
/* Sanity check */
if (!disk_index_file)
return false;
/* Disk index records are only valid when loading
* content (i.e. they do not apply to contentless
* cores) */
if (string_is_empty(content_path))
goto error;
/* Build disk index file path */
/* > Get content name */
content_file = path_basename(content_path);
if (string_is_empty(content_file))
goto error;
strlcpy(content_name, content_file, sizeof(content_name));
path_remove_extension(content_name);
if (string_is_empty(content_name))
goto error;
/* > Get disk index file directory */
if (!string_is_empty(dir_savefile))
strlcpy(disk_index_file_dir, dir_savefile, sizeof(disk_index_file_dir));
else
{
/* Use content directory */
strlcpy(disk_index_file_dir, content_path, sizeof(disk_index_file_dir));
path_basedir(disk_index_file_dir);
}
/* > Create directory, if required */
if (!path_is_directory(disk_index_file_dir))
{
if (!path_mkdir(disk_index_file_dir))
{
RARCH_ERR(
"[disk index file] failed to create directory for disk index file: %s\n",
disk_index_file_dir);
goto error;
}
}
/* > Generate final path */
fill_pathname_join(
disk_index_file_path, disk_index_file_dir,
content_name, sizeof(disk_index_file_path));
strlcat(
disk_index_file_path,
file_path_str(FILE_PATH_DISK_CONTROL_INDEX_EXTENSION),
sizeof(disk_index_file_path));
if (string_is_empty(disk_index_file_path))
goto error;
/* All is well - reset disk_index_file_t and
* attempt to load values from file */
disk_index_file_reset(disk_index_file);
strlcpy(
disk_index_file->file_path,
disk_index_file_path,
sizeof(disk_index_file->file_path));
/* > If file does not exist (or some other
* error occurs) then this is a new record
* - in this case, 'modified' flag should
* be set to 'true' */
if (!disk_index_file_read(disk_index_file))
disk_index_file->modified = true;
return true;
error:
disk_index_file_reset(disk_index_file);
return false;
}
/***********/
/* Setters */
/***********/
/* Sets image index and path */
void disk_index_file_set(
disk_index_file_t *disk_index_file,
unsigned image_index,
const char *image_path)
{
if (!disk_index_file)
return;
/* Check whether image index should be updated */
if (disk_index_file->image_index != image_index)
{
disk_index_file->image_index = image_index;
disk_index_file->modified = true;
}
/* Check whether image path should be updated */
if (!string_is_empty(image_path))
{
if (!string_is_equal(disk_index_file->image_path, image_path))
{
strlcpy(
disk_index_file->image_path, image_path,
sizeof(disk_index_file->image_path));
disk_index_file->modified = true;
}
}
else if (!string_is_empty(disk_index_file->image_path))
{
disk_index_file->image_path[0] = '\0';
disk_index_file->modified = true;
}
}
/**********/
/* Saving */
/**********/
/* Saves specified disk index file to disk */
bool disk_index_file_save(disk_index_file_t *disk_index_file)
{
char value_string[32];
DCifJSONContext context = {0};
RFILE *file = NULL;
bool success = false;
int n;
value_string[0] = '\0';
/* Sanity check */
if (!disk_index_file)
return false;
/* > Only save file if record has been modified.
* We return true in this case - since there
* was nothing to write, there can be no
* 'failure' */
if (!disk_index_file->modified)
return true;
if (string_is_empty(disk_index_file->file_path))
return false;
RARCH_LOG(
"[disk index file] Saving disk index file: %s\n",
disk_index_file->file_path);
/* Attempt to open disk index file */
file = filestream_open(
disk_index_file->file_path,
RETRO_VFS_FILE_ACCESS_WRITE,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!file)
{
RARCH_ERR(
"[disk index file] Failed to open disk index file: %s\n",
disk_index_file->file_path);
return false;
}
/* Initialise JSON writer */
context.writer = JSON_Writer_Create(NULL);
context.file = file;
if (!context.writer)
{
RARCH_ERR("[disk index file] Failed to create JSON writer.\n");
goto end;
}
/* Configure JSON writer */
JSON_Writer_SetOutputEncoding(context.writer, JSON_UTF8);
JSON_Writer_SetOutputHandler(context.writer, &DCifJSONOutputHandler);
JSON_Writer_SetUserData(context.writer, &context);
/* Write output file */
JSON_Writer_WriteStartObject(context.writer);
JSON_Writer_WriteNewLine(context.writer);
/* > Version entry */
JSON_Writer_WriteSpace(context.writer, 2);
JSON_Writer_WriteString(context.writer, "version",
STRLEN_CONST("version"), JSON_UTF8);
JSON_Writer_WriteColon(context.writer);
JSON_Writer_WriteSpace(context.writer, 1);
JSON_Writer_WriteString(context.writer, "1.0",
STRLEN_CONST("1.0"), JSON_UTF8);
JSON_Writer_WriteComma(context.writer);
JSON_Writer_WriteNewLine(context.writer);
/* > image index entry */
n = snprintf(
value_string, sizeof(value_string),
"%u", disk_index_file->image_index);
if ((n < 0) || (n >= 32))
n = 0; /* Silence GCC warnings... */
JSON_Writer_WriteSpace(context.writer, 2);
JSON_Writer_WriteString(context.writer, "image_index",
STRLEN_CONST("image_index"), JSON_UTF8);
JSON_Writer_WriteColon(context.writer);
JSON_Writer_WriteSpace(context.writer, 1);
JSON_Writer_WriteNumber(context.writer, value_string,
strlen(value_string), JSON_UTF8);
JSON_Writer_WriteComma(context.writer);
JSON_Writer_WriteNewLine(context.writer);
/* > image path entry */
JSON_Writer_WriteSpace(context.writer, 2);
JSON_Writer_WriteString(context.writer, "image_path",
STRLEN_CONST("image_path"), JSON_UTF8);
JSON_Writer_WriteColon(context.writer);
JSON_Writer_WriteSpace(context.writer, 1);
JSON_Writer_WriteString(context.writer,
disk_index_file->image_path,
strlen(disk_index_file->image_path), JSON_UTF8);
JSON_Writer_WriteNewLine(context.writer);
/* > Finalise */
JSON_Writer_WriteEndObject(context.writer);
JSON_Writer_WriteNewLine(context.writer);
/* Free JSON writer */
JSON_Writer_Free(context.writer);
/* Changes have been written - record
* is no longer considered to be in a
* 'modified' state */
disk_index_file->modified = false;
success = true;
end:
/* Close disk index file */
filestream_close(file);
return success;
}

76
disk_index_file.h Normal file
View File

@ -0,0 +1,76 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (disk_index_file.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __DISK_INDEX_FILE_H
#define __DISK_INDEX_FILE_H
#include <retro_common_api.h>
#include <libretro.h>
#include <retro_miscellaneous.h>
#include <boolean.h>
RETRO_BEGIN_DECLS
/* Holds all parameters required for recording
* the last disk image selected via the disk
* control interface */
typedef struct
{
bool modified;
unsigned image_index;
char image_path[PATH_MAX_LENGTH];
char file_path[PATH_MAX_LENGTH];
} disk_index_file_t;
/******************/
/* Initialisation */
/******************/
/* Initialises existing disk index record, loading
* current parameters if a record file exists.
* Returns false if arguments are invalid. */
bool disk_index_file_init(
disk_index_file_t *disk_index_file,
const char *content_path,
const char *dir_savefile);
/***********/
/* Setters */
/***********/
/* Sets image index and path */
void disk_index_file_set(
disk_index_file_t *disk_index_file,
unsigned image_index,
const char *image_path);
/**********/
/* Saving */
/**********/
/* Saves specified disk index file to disk */
bool disk_index_file_save(disk_index_file_t *disk_index_file);
RETRO_END_DECLS
#endif

View File

@ -95,7 +95,8 @@ enum file_path_enum
FILE_PATH_CORE_INFO_EXTENSION,
FILE_PATH_RUNTIME_EXTENSION,
FILE_PATH_DEFAULT_EVENT_LOG,
FILE_PATH_EVENT_LOG_EXTENSION
FILE_PATH_EVENT_LOG_EXTENSION,
FILE_PATH_DISK_CONTROL_INDEX_EXTENSION
};
enum application_special_type

View File

@ -227,6 +227,9 @@ const char *file_path_str(enum file_path_enum enum_idx)
case FILE_PATH_EVENT_LOG_EXTENSION:
str = ".log";
break;
case FILE_PATH_DISK_CONTROL_INDEX_EXTENSION:
str = ".ldci";
break;
case FILE_PATH_UNKNOWN:
default:
break;

View File

@ -63,7 +63,7 @@ bool platform_switch_has_focus = true;
/* Splash */
static uint32_t *splashData = NULL;
static bool psmInitialized = false;
static bool psmInitialized = false;
static AppletHookCookie applet_hook_cookie;
@ -71,12 +71,19 @@ static AppletHookCookie applet_hook_cookie;
extern bool nxlink_connected;
#endif
void libnx_apply_overclock() {
const size_t profiles_count = sizeof(SWITCH_CPU_PROFILES) / sizeof(SWITCH_CPU_PROFILES[1]);
if (config_get_ptr()->uints.libnx_overclock >= 0 && config_get_ptr()->uints.libnx_overclock <= profiles_count){
if(hosversionBefore(8, 0, 0)) {
void libnx_apply_overclock(void)
{
const size_t profiles_count = sizeof(SWITCH_CPU_PROFILES)
/ sizeof(SWITCH_CPU_PROFILES[1]);
if (config_get_ptr()->uints.libnx_overclock >= 0 && config_get_ptr()->uints.libnx_overclock <= profiles_count)
{
if (hosversionBefore(8, 0, 0))
{
pcvSetClockRate(PcvModule_CpuBus, SWITCH_CPU_SPEEDS_VALUES[config_get_ptr()->uints.libnx_overclock]);
} else {
}
else
{
ClkrstSession session = {0};
clkrstOpenSession(&session, PcvModuleId_CpuBus, 3);
clkrstSetClockRate(&session, SWITCH_CPU_SPEEDS_VALUES[config_get_ptr()->uints.libnx_overclock]);
@ -85,52 +92,59 @@ void libnx_apply_overclock() {
}
}
static void on_applet_hook(AppletHookType hook, void *param) {
static void on_applet_hook(AppletHookType hook, void *param)
{
u32 performance_mode;
AppletFocusState focus_state;
/* Exit request */
switch (hook)
{
case AppletHookType_OnExitRequest:
RARCH_LOG("Got AppletHook OnExitRequest, exiting.\n");
retroarch_main_quit();
break;
case AppletHookType_OnExitRequest:
RARCH_LOG("Got AppletHook OnExitRequest, exiting.\n");
retroarch_main_quit();
break;
/* Focus state*/
case AppletHookType_OnFocusState:
focus_state = appletGetFocusState();
RARCH_LOG("Got AppletHook OnFocusState - new focus state is %d\n", focus_state);
platform_switch_has_focus = focus_state == AppletFocusState_Focused;
if(!platform_switch_has_focus) {
if(hosversionBefore(8, 0, 0)) {
pcvSetClockRate(PcvModule_CpuBus, 1020000000);
} else {
ClkrstSession session = {0};
clkrstOpenSession(&session, PcvModuleId_CpuBus, 3);
clkrstSetClockRate(&session, 1020000000);
clkrstCloseSession(&session);
/* Focus state*/
case AppletHookType_OnFocusState:
focus_state = appletGetFocusState();
RARCH_LOG("Got AppletHook OnFocusState - new focus state is %d\n", focus_state);
platform_switch_has_focus = focus_state == AppletFocusState_Focused;
if (!platform_switch_has_focus)
{
if (hosversionBefore(8, 0, 0))
{
pcvSetClockRate(PcvModule_CpuBus, 1020000000);
}
else
{
ClkrstSession session = {0};
clkrstOpenSession(&session, PcvModuleId_CpuBus, 3);
clkrstSetClockRate(&session, 1020000000);
clkrstCloseSession(&session);
}
}
} else {
else
libnx_apply_overclock();
break;
/* Performance mode */
case AppletHookType_OnPerformanceMode:
/* 0 == Handheld, 1 == Docked
* Since CPU doesn't change we just re-apply */
performance_mode = appletGetPerformanceMode();
libnx_apply_overclock();
}
break;
break;
/* Performance mode */
case AppletHookType_OnPerformanceMode:
// 0 == Handheld, 1 == Docked
// Since CPU doesn't change we just re-apply
performance_mode = appletGetPerformanceMode();
libnx_apply_overclock();
break;
default:
break;
default:
break;
}
}
#endif /* HAVE_LIBNX */
#ifdef IS_SALAMANDER
static void get_first_valid_core(char *path_return)
{
DIR *dir;
@ -140,11 +154,11 @@ static void get_first_valid_core(char *path_return)
path_return[0] = '\0';
dir = opendir(SD_PREFIX "/retroarch/cores");
if (dir != NULL)
if (dir)
{
while ((ent = readdir(dir)) != NULL)
while ((ent = readdir(dir)))
{
if (ent == NULL)
if (!ent)
break;
if (strlen(ent->d_name) > strlen(extension) && !strcmp(ent->d_name + strlen(ent->d_name) - strlen(extension), extension))
{
@ -157,8 +171,10 @@ static void get_first_valid_core(char *path_return)
closedir(dir);
}
}
#endif
static void frontend_switch_get_environment_settings(int *argc, char *argv[], void *args, void *params_data)
static void frontend_switch_get_environment_settings(
int *argc, char *argv[], void *args, void *params_data)
{
unsigned i;
(void)args;
@ -266,10 +282,13 @@ static void frontend_switch_deinit(void *data)
#ifdef HAVE_LIBNX
nifmExit();
if(hosversionBefore(8, 0, 0)) {
if (hosversionBefore(8, 0, 0))
{
pcvSetClockRate(PcvModule_CpuBus, 1020000000);
pcvExit();
} else {
}
else
{
ClkrstSession session = {0};
clkrstOpenSession(&session, PcvModuleId_CpuBus, 3);
clkrstSetClockRate(&session, 1020000000);
@ -548,26 +567,21 @@ ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize)
/* Taken from glibc */
char *realpath(const char *name, char *resolved)
{
char *rpath, *dest, *extra_buf = NULL;
char *rpath, *dest = NULL;
const char *start, *end, *rpath_limit;
long int path_max;
int num_links = 0;
if (name == NULL)
{
/* As per Single Unix Specification V2 we must return an error if
either parameter is a null pointer. We extend this to allow
the RESOLVED parameter to be NULL in case the we are expected to
allocate the room for the return value. */
/* As per Single Unix Specification V2 we must return an error if
either parameter is a null pointer. We extend this to allow
the RESOLVED parameter to be NULL in case the we are expected to
allocate the room for the return value. */
if (!name)
return NULL;
}
/* As per Single Unix Specification V2 we must return an error if
the name argument points to an empty string. */
if (name[0] == '\0')
{
/* As per Single Unix Specification V2 we must return an error if
the name argument points to an empty string. */
return NULL;
}
#ifdef PATH_MAX
path_max = PATH_MAX;
@ -577,10 +591,10 @@ char *realpath(const char *name, char *resolved)
path_max = 1024;
#endif
if (resolved == NULL)
if (!resolved)
{
rpath = malloc(path_max);
if (rpath == NULL)
if (!rpath)
return NULL;
}
else
@ -650,7 +664,7 @@ char *realpath(const char *name, char *resolved)
else
new_size += path_max;
new_rpath = (char *)realloc(rpath, new_size);
if (new_rpath == NULL)
if (!new_rpath)
goto error;
rpath = new_rpath;
rpath_limit = rpath + new_size;
@ -669,7 +683,7 @@ char *realpath(const char *name, char *resolved)
return rpath;
error:
if (resolved == NULL)
if (!resolved)
free(rpath);
return NULL;
}
@ -688,12 +702,10 @@ static void frontend_switch_init(void *data)
{
#ifdef HAVE_LIBNX
bool recording_supported = false;
uint32_t width = 0;
uint32_t height = 0;
nifmInitialize(NifmServiceType_User);
if(hosversionBefore(8, 0, 0))
if (hosversionBefore(8, 0, 0))
pcvInitialize();
else
clkrstInitialize();
@ -703,7 +715,7 @@ static void frontend_switch_init(void *data)
appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend);
appletIsGamePlayRecordingSupported(&recording_supported);
if(recording_supported)
if (recording_supported)
appletInitializeGamePlayRecording();
#ifdef NXLINK
@ -723,65 +735,6 @@ static void frontend_switch_init(void *data)
RARCH_WARN("Error initializing psm\n");
}
#if 0
#ifndef HAVE_OPENGL
/* Load splash */
if (!splashData)
{
rarch_system_info_t *sys_info = runloop_get_system_info();
retro_get_system_info(sys_info);
if (sys_info)
{
const char *core_name = sys_info->info.library_name;
char *full_core_splash_path = (char*)malloc(PATH_MAX);
snprintf(full_core_splash_path,
PATH_MAX, "/retroarch/rgui/splash/%s.png", core_name);
rpng_load_image_argb((const char *)
full_core_splash_path, &splashData, &width, &height);
if (splashData)
{
argb_to_rgba8(splashData, height, width);
frontend_switch_showsplash();
}
else
{
rpng_load_image_argb(
"/retroarch/rgui/splash/RetroArch.png",
&splashData, &width, &height);
if (splashData)
{
argb_to_rgba8(splashData, height, width);
frontend_switch_showsplash();
}
}
free(full_core_splash_path);
}
else
{
rpng_load_image_argb(
"/retroarch/rgui/splash/RetroArch.png",
&splashData, &width, &height);
if (splashData)
{
argb_to_rgba8(splashData, height, width);
frontend_switch_showsplash();
}
}
}
else
{
frontend_switch_showsplash();
}
#endif
#endif
#endif /* HAVE_LIBNX (splash) */
}

View File

@ -564,7 +564,6 @@ bool egl_init_context(egl_ctx_data_t *egl,
EGLint *count, const EGLint *attrib_ptr,
egl_accept_config_cb_t cb)
{
int config_index = -1;
EGLDisplay dpy = get_egl_display(platform, display_data);
if (dpy == EGL_NO_DISPLAY)

View File

@ -139,6 +139,30 @@ typedef struct gl_core
void gl_core_bind_scratch_vbo(gl_core_t *gl, const void *data, size_t size);
GLuint gl_core_compile_shader(GLenum stage, const char *source);
void gl_core_framebuffer_clear(GLuint id);
void gl_core_framebuffer_copy(
GLuint fb_id,
GLuint quad_program,
GLuint quad_vbo,
GLint flat_ubo_vertex,
struct Size2D size,
GLuint image);
void gl_core_framebuffer_copy_partial(
GLuint fb_id,
GLuint quad_program,
GLint flat_ubo_vertex,
struct Size2D size,
GLuint image,
float rx, float ry);
void gl_core_build_default_matrix(float *data);
uint32_t gl_core_get_cross_compiler_target_version(void);
RETRO_END_DECLS
#endif

View File

@ -3175,3 +3175,313 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
return true;
}
void vulkan_initialize_render_pass(VkDevice device, VkFormat format,
VkRenderPass *render_pass)
{
VkRenderPassCreateInfo rp_info = {
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
VkAttachmentReference color_ref = { 0,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
VkAttachmentDescription attachment = {0};
VkSubpassDescription subpass = {0};
/* We will always write to the entire framebuffer,
* so we don't really need to clear. */
attachment.format = format;
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_ref;
rp_info.attachmentCount = 1;
rp_info.pAttachments = &attachment;
rp_info.subpassCount = 1;
rp_info.pSubpasses = &subpass;
vkCreateRenderPass(device, &rp_info, NULL, render_pass);
}
void vulkan_set_uniform_buffer(
VkDevice device,
VkDescriptorSet set,
unsigned binding,
VkBuffer buffer,
VkDeviceSize offset,
VkDeviceSize range)
{
VkDescriptorBufferInfo buffer_info;
VkWriteDescriptorSet write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
buffer_info.buffer = buffer;
buffer_info.offset = offset;
buffer_info.range = range;
write.dstSet = set;
write.dstBinding = binding;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
write.pBufferInfo = &buffer_info;
vkUpdateDescriptorSets(device, 1, &write, 0, NULL);
}
void vulkan_framebuffer_generate_mips(
VkFramebuffer framebuffer,
VkImage image,
struct Size2D size,
VkCommandBuffer cmd,
unsigned levels
)
{
unsigned i;
/* This is run every frame, so make sure
* we aren't opting into the "lazy" way of doing this. :) */
VkImageMemoryBarrier barriers[2] = {
{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER },
{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER },
};
/* First, transfer the input mip level to TRANSFER_SRC_OPTIMAL.
* This should allow the surface to stay compressed.
* All subsequent mip-layers are now transferred into DST_OPTIMAL from
* UNDEFINED at this point.
*/
/* Input */
barriers[0].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barriers[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barriers[0].oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
barriers[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barriers[0].image = image;
barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barriers[0].subresourceRange.baseMipLevel = 0;
barriers[0].subresourceRange.levelCount = 1;
barriers[0].subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
/* The rest of the mip chain */
barriers[1].srcAccessMask = 0;
barriers[1].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barriers[1].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barriers[1].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barriers[1].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barriers[1].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barriers[1].image = image;
barriers[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barriers[1].subresourceRange.baseMipLevel = 1;
barriers[1].subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
barriers[1].subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
vkCmdPipelineBarrier(cmd,
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
false,
0,
NULL,
0,
NULL,
2,
barriers);
for (i = 1; i < levels; i++)
{
unsigned src_width, src_height, target_width, target_height;
VkImageBlit blit_region = {};
/* For subsequent passes, we have to transition
* from DST_OPTIMAL to SRC_OPTIMAL,
* but only do so one mip-level at a time. */
if (i > 1)
{
barriers[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barriers[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barriers[0].subresourceRange.baseMipLevel = i - 1;
barriers[0].subresourceRange.levelCount = 1;
barriers[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barriers[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
vkCmdPipelineBarrier(cmd,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
false,
0,
NULL,
0,
NULL,
1,
barriers);
}
src_width = MAX(size.width >> (i - 1), 1u);
src_height = MAX(size.height >> (i - 1), 1u);
target_width = MAX(size.width >> i, 1u);
target_height = MAX(size.height >> i, 1u);
blit_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit_region.srcSubresource.mipLevel = i - 1;
blit_region.srcSubresource.baseArrayLayer = 0;
blit_region.srcSubresource.layerCount = 1;
blit_region.dstSubresource = blit_region.srcSubresource;
blit_region.dstSubresource.mipLevel = i;
blit_region.srcOffsets[1].x = src_width;
blit_region.srcOffsets[1].y = src_height;
blit_region.srcOffsets[1].z = 1;
blit_region.dstOffsets[1].x = target_width;
blit_region.dstOffsets[1].y = target_height;
blit_region.dstOffsets[1].z = 1;
vkCmdBlitImage(cmd,
image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &blit_region, VK_FILTER_LINEAR);
}
/* We are now done, and we have all mip-levels except
* the last in TRANSFER_SRC_OPTIMAL,
* and the last one still on TRANSFER_DST_OPTIMAL,
* so do a final barrier which
* moves everything to SHADER_READ_ONLY_OPTIMAL in
* one go along with the execution barrier to next pass.
* Read-to-read memory barrier, so only need execution
* barrier for first transition.
*/
barriers[0].srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barriers[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
barriers[0].subresourceRange.baseMipLevel = 0;
barriers[0].subresourceRange.levelCount = levels - 1;
barriers[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barriers[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
/* This is read-after-write barrier. */
barriers[1].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barriers[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
barriers[1].subresourceRange.baseMipLevel = levels - 1;
barriers[1].subresourceRange.levelCount = 1;
barriers[1].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barriers[1].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
vkCmdPipelineBarrier(cmd,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
false,
0,
NULL,
0,
NULL,
2, barriers);
/* Next pass will wait for ALL_GRAPHICS_BIT, and since
* we have dstStage as FRAGMENT_SHADER,
* the dependency chain will ensure we don't start
* next pass until the mipchain is complete. */
}
void vulkan_framebuffer_copy(VkImage image,
struct Size2D size,
VkCommandBuffer cmd,
VkImage src_image, VkImageLayout src_layout)
{
VkImageCopy region;
vulkan_image_layout_transition_levels(cmd, image,VK_REMAINING_MIP_LEVELS,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
0, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT);
memset(&region, 0, sizeof(region));
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.layerCount = 1;
region.dstSubresource = region.srcSubresource;
region.extent.width = size.width;
region.extent.height = size.height;
region.extent.depth = 1;
vkCmdCopyImage(cmd,
src_image, src_layout,
image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &region);
vulkan_image_layout_transition_levels(cmd,
image,
VK_REMAINING_MIP_LEVELS,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_SHADER_READ_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
void vulkan_framebuffer_clear(VkImage image, VkCommandBuffer cmd)
{
VkClearColorValue color;
VkImageSubresourceRange range;
vulkan_image_layout_transition_levels(cmd,
image,
VK_REMAINING_MIP_LEVELS,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
0,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT);
memset(&color, 0, sizeof(color));
memset(&range, 0, sizeof(range));
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
range.levelCount = 1;
range.layerCount = 1;
vkCmdClearColorImage(cmd,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
&color,
1,
&range);
vulkan_image_layout_transition_levels(cmd,
image,
VK_REMAINING_MIP_LEVELS,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_SHADER_READ_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
void vulkan_pass_set_texture(
VkDevice device,
VkDescriptorSet set, VkSampler sampler,
unsigned binding,
VkImageView imageView, VkImageLayout imageLayout)
{
VkDescriptorImageInfo image_info;
VkWriteDescriptorSet write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
image_info.sampler = sampler;
image_info.imageView = imageView;
image_info.imageLayout = imageLayout;
write.dstSet = set;
write.dstBinding = binding;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write.pImageInfo = &image_info;
vkUpdateDescriptorSets(device, 1, &write, 0, NULL);
}

View File

@ -42,6 +42,7 @@
#include <libretro.h>
#include <libretro_vulkan.h>
#include "../video_defines.h"
#include "../../driver.h"
#include "../../retroarch.h"
#include "../../verbosity.h"
@ -592,6 +593,38 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
unsigned width, unsigned height,
unsigned swap_interval);
void vulkan_set_uniform_buffer(
VkDevice device,
VkDescriptorSet set,
unsigned binding,
VkBuffer buffer,
VkDeviceSize offset,
VkDeviceSize range);
void vulkan_framebuffer_generate_mips(
VkFramebuffer framebuffer,
VkImage image,
struct Size2D size,
VkCommandBuffer cmd,
unsigned levels
);
void vulkan_framebuffer_copy(VkImage image,
struct Size2D size,
VkCommandBuffer cmd,
VkImage src_image, VkImageLayout src_layout);
void vulkan_framebuffer_clear(VkImage image, VkCommandBuffer cmd);
void vulkan_initialize_render_pass(VkDevice device,
VkFormat format, VkRenderPass *render_pass);
void vulkan_pass_set_texture(
VkDevice device,
VkDescriptorSet set, VkSampler sampler,
unsigned binding,
VkImageView imageView, VkImageLayout imageLayout);
RETRO_END_DECLS
#endif

View File

@ -89,6 +89,28 @@
#define EDS_ROTATEDMODE 4
#endif
/* These are defined in later SDKs, thus ifdeffed. */
#ifndef WM_MOUSEHWHEEL
#define WM_MOUSEHWHEEL 0x20e
#endif
#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL 0x020A
#endif
#ifndef WM_POINTERUPDATE
#define WM_POINTERUPDATE 0x0245
#endif
#ifndef WM_POINTERDOWN
#define WM_POINTERDOWN 0x0246
#endif
#ifndef WM_POINTERUP
#define WM_POINTERUP 0x0247
#endif
const GUID GUID_DEVINTERFACE_HID = { 0x4d1e55b2, 0xf16f, 0x11Cf, { 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x501
static HDEVNOTIFY notification_handler;
@ -203,7 +225,7 @@ typedef LONG (WINAPI *GETDISPLAYCONFIGBUFFERSIZES)(UINT32, UINT32*, UINT32*);
static bool g_win32_resized = false;
bool g_win32_restore_desktop = false;
static bool doubleclick_on_titlebar = false;
static bool g_taskbar_is_created = false;
static bool taskbar_is_created = false;
bool g_win32_inited = false;
static bool g_win32_quit = false;
@ -273,7 +295,7 @@ static HMONITOR win32_monitor_all[MAX_MONITORS];
bool win32_taskbar_is_created(void)
{
return g_taskbar_is_created;
return taskbar_is_created;
}
bool doubleclick_on_titlebar_pressed(void)
@ -500,9 +522,10 @@ bool win32_load_content_from_gui(const char *szFilename)
{
bool okay = false;
settings_t *settings = config_get_ptr();
bool video_is_fs = settings->bools.video_fullscreen;
/* Fullscreen: Show mouse cursor for dialog */
if (settings->bools.video_fullscreen)
if (video_is_fs)
video_driver_show_mouse();
/* Pick one core that could be compatible, ew */
@ -515,7 +538,7 @@ bool win32_load_content_from_gui(const char *szFilename)
}
/* Fullscreen: Hide mouse cursor after dialog */
if (settings->bools.video_fullscreen)
if (video_is_fs)
video_driver_hide_mouse();
return okay;
}
@ -604,14 +627,222 @@ static void win32_save_position(void)
}
}
static bool win32_browser(
HWND owner,
char *filename,
size_t filename_size,
const char *extensions,
const char *title,
const char *initial_dir)
{
bool result = false;
const ui_browser_window_t *browser =
ui_companion_driver_get_browser_window_ptr();
if (browser)
{
ui_browser_window_state_t browser_state;
/* These need to be big enough to hold the
* path/name of any file the user may select.
* FIXME: We should really handle the
* error case when this isn't big enough. */
char new_title[PATH_MAX];
char new_file[32768];
new_title[0] = '\0';
new_file[0] = '\0';
if (!string_is_empty(title))
strlcpy(new_title, title, sizeof(new_title));
if (filename && *filename)
strlcpy(new_file, filename, sizeof(new_file));
/* OPENFILENAME.lpstrFilters is actually const,
* so this cast should be safe */
browser_state.filters = (char*)extensions;
browser_state.title = new_title;
browser_state.startdir = strdup(initial_dir);
browser_state.path = new_file;
browser_state.window = owner;
result = browser->open(&browser_state);
if (filename && browser_state.path)
strlcpy(filename, browser_state.path, filename_size);
free(browser_state.startdir);
}
return result;
}
static LRESULT win32_menu_loop(HWND owner, WPARAM wparam)
{
WPARAM mode = wparam & 0xffff;
switch (mode)
{
case ID_M_LOAD_CORE:
{
char win32_file[PATH_MAX_LENGTH] = {0};
settings_t *settings = config_get_ptr();
char *title_cp = NULL;
size_t converted = 0;
const char *extensions = "Libretro core (.dll)\0*.dll\0All Files\0*.*\0\0";
const char *title = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_LIST);
const char *initial_dir = settings->paths.directory_libretro;
/* Convert UTF8 to UTF16, then back to the
* local code page.
* This is needed for proper multi-byte
* string display until Unicode is
* fully supported.
*/
wchar_t *title_wide = utf8_to_utf16_string_alloc(title);
if (title_wide)
title_cp = utf16_to_utf8_string_alloc(title_wide);
if (!win32_browser(owner, win32_file, sizeof(win32_file),
extensions, title_cp, initial_dir))
{
if (title_wide)
free(title_wide);
if (title_cp)
free(title_cp);
break;
}
if (title_wide)
free(title_wide);
if (title_cp)
free(title_cp);
path_set(RARCH_PATH_CORE, win32_file);
command_event(CMD_EVENT_LOAD_CORE, NULL);
}
break;
case ID_M_LOAD_CONTENT:
{
char win32_file[PATH_MAX_LENGTH] = {0};
char *title_cp = NULL;
size_t converted = 0;
const char *extensions = "All Files (*.*)\0*.*\0\0";
const char *title = msg_hash_to_str(
MENU_ENUM_LABEL_VALUE_LOAD_CONTENT_LIST);
settings_t *settings = config_get_ptr();
const char *initial_dir = settings->paths.directory_menu_content;
/* Convert UTF8 to UTF16, then back to the
* local code page.
* This is needed for proper multi-byte
* string display until Unicode is
* fully supported.
*/
wchar_t *title_wide = utf8_to_utf16_string_alloc(title);
if (title_wide)
title_cp = utf16_to_utf8_string_alloc(title_wide);
if (!win32_browser(owner, win32_file, sizeof(win32_file),
extensions, title_cp, initial_dir))
{
if (title_wide)
free(title_wide);
if (title_cp)
free(title_cp);
break;
}
if (title_wide)
free(title_wide);
if (title_cp)
free(title_cp);
win32_load_content_from_gui(win32_file);
}
break;
case ID_M_RESET:
command_event(CMD_EVENT_RESET, NULL);
break;
case ID_M_MUTE_TOGGLE:
command_event(CMD_EVENT_AUDIO_MUTE_TOGGLE, NULL);
break;
case ID_M_MENU_TOGGLE:
command_event(CMD_EVENT_MENU_TOGGLE, NULL);
break;
case ID_M_PAUSE_TOGGLE:
command_event(CMD_EVENT_PAUSE_TOGGLE, NULL);
break;
case ID_M_LOAD_STATE:
command_event(CMD_EVENT_LOAD_STATE, NULL);
break;
case ID_M_SAVE_STATE:
command_event(CMD_EVENT_SAVE_STATE, NULL);
break;
case ID_M_DISK_CYCLE:
command_event(CMD_EVENT_DISK_EJECT_TOGGLE, NULL);
break;
case ID_M_DISK_NEXT:
command_event(CMD_EVENT_DISK_NEXT, NULL);
break;
case ID_M_DISK_PREV:
command_event(CMD_EVENT_DISK_PREV, NULL);
break;
case ID_M_FULL_SCREEN:
command_event(CMD_EVENT_FULLSCREEN_TOGGLE, NULL);
break;
case ID_M_MOUSE_GRAB:
command_event(CMD_EVENT_GRAB_MOUSE_TOGGLE, NULL);
break;
case ID_M_TAKE_SCREENSHOT:
command_event(CMD_EVENT_TAKE_SCREENSHOT, NULL);
break;
case ID_M_QUIT:
PostMessage(owner, WM_CLOSE, 0, 0);
break;
case ID_M_TOGGLE_DESKTOP:
command_event(CMD_EVENT_UI_COMPANION_TOGGLE, NULL);
break;
default:
if (mode >= ID_M_WINDOW_SCALE_1X && mode <= ID_M_WINDOW_SCALE_10X)
{
unsigned idx = (mode - (ID_M_WINDOW_SCALE_1X-1));
rarch_ctl(RARCH_CTL_SET_WINDOWED_SCALE, &idx);
command_event(CMD_EVENT_RESIZE_WINDOWED_SCALE, NULL);
}
else if (mode == ID_M_STATE_INDEX_AUTO)
{
signed idx = -1;
settings_t *settings = config_get_ptr();
configuration_set_int(
settings, settings->ints.state_slot, idx);
}
else if (mode >= (ID_M_STATE_INDEX_AUTO+1)
&& mode <= (ID_M_STATE_INDEX_AUTO+10))
{
signed idx = (mode - (ID_M_STATE_INDEX_AUTO+1));
settings_t *settings = config_get_ptr();
configuration_set_int(
settings, settings->ints.state_slot, idx);
}
break;
}
return 0L;
}
static LRESULT CALLBACK WndProcCommon(bool *quit, HWND hwnd, UINT message,
WPARAM wparam, LPARAM lparam)
{
if (message == WM_NCLBUTTONDBLCLK)
doubleclick_on_titlebar = true;
bool keydown = true;
switch (message)
{
case WM_NCLBUTTONDBLCLK:
doubleclick_on_titlebar = true;
break;
case WM_SYSCOMMAND:
/* Prevent screensavers, etc, while running. */
switch (wparam)
@ -653,13 +884,15 @@ static LRESULT CALLBACK WndProcCommon(bool *quit, HWND hwnd, UINT message,
return TRUE;
case WM_KEYUP:
case WM_SYSKEYUP:
/* Key released */
keydown = false;
/* fall-through */
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
*quit = true;
{
uint16_t mod = 0;
unsigned keycode = 0;
bool keydown = true;
unsigned keysym = (lparam >> 16) & 0xff;
#if _WIN32_WINNT >= 0x0501 /* XP */
settings_t *settings = config_get_ptr();
@ -679,7 +912,8 @@ static LRESULT CALLBACK WndProcCommon(bool *quit, HWND hwnd, UINT message,
mod |= RETROKMOD_META;
#if _WIN32_WINNT >= 0x0501 /* XP */
if (settings && string_is_equal(settings->arrays.input_driver, "raw"))
if (settings &&
string_is_equal(settings->arrays.input_driver, "raw"))
keysym = (unsigned)wparam;
else
#endif
@ -693,10 +927,6 @@ static LRESULT CALLBACK WndProcCommon(bool *quit, HWND hwnd, UINT message,
#endif
}
/* Key released? */
if (message == WM_KEYUP || message == WM_SYSKEYUP)
keydown = false;
keycode = input_keymaps_translate_keysym_to_rk(keysym);
input_keyboard_event(keydown, keycode,
@ -757,11 +987,36 @@ LRESULT CALLBACK WndProcD3D(HWND hwnd, UINT message,
LRESULT ret;
bool quit = false;
if (message == WM_NCLBUTTONDBLCLK)
doubleclick_on_titlebar = true;
switch (message)
{
case WM_MOUSEMOVE:
case WM_POINTERDOWN:
case WM_POINTERUP:
case WM_POINTERUPDATE:
case WM_DEVICECHANGE:
case WM_MOUSEWHEEL:
case WM_MOUSEHWHEEL:
#if _WIN32_WINNT >= 0x0500 /* 2K */
if (g_taskbar_message && message == g_taskbar_message)
taskbar_is_created = true;
#endif
#ifdef HAVE_DINPUT
if (input_get_ptr() == &input_dinput)
{
void* input_data = input_get_data();
if (input_data && dinput_handle_message(input_data,
message, wparam, lparam))
return 0;
}
#endif
break;
case WM_NCLBUTTONDBLCLK:
doubleclick_on_titlebar = true;
#if _WIN32_WINNT >= 0x0500 /* 2K */
if (g_taskbar_message && message == g_taskbar_message)
taskbar_is_created = true;
#endif
break;
case WM_DROPFILES:
case WM_SYSCOMMAND:
case WM_CHAR:
@ -778,6 +1033,10 @@ LRESULT CALLBACK WndProcD3D(HWND hwnd, UINT message,
ret = WndProcCommon(&quit, hwnd, message, wparam, lparam);
if (quit)
return ret;
#if _WIN32_WINNT >= 0x0500 /* 2K */
if (g_taskbar_message && message == g_taskbar_message)
taskbar_is_created = true;
#endif
break;
case WM_CREATE:
{
@ -791,20 +1050,6 @@ LRESULT CALLBACK WndProcD3D(HWND hwnd, UINT message,
return 0;
}
#if _WIN32_WINNT >= 0x0500 /* 2K */
if (g_taskbar_message && message == g_taskbar_message)
g_taskbar_is_created = true;
#endif
#ifdef HAVE_DINPUT
if (input_get_ptr() == &input_dinput)
{
void* input_data = input_get_data();
if (input_data && dinput_handle_message(input_data,
message, wparam, lparam))
return 0;
}
#endif
return DefWindowProc(hwnd, message, wparam, lparam);
}
#endif
@ -816,11 +1061,32 @@ LRESULT CALLBACK WndProcGL(HWND hwnd, UINT message,
LRESULT ret;
bool quit = false;
if (message == WM_NCLBUTTONDBLCLK)
doubleclick_on_titlebar = true;
switch (message)
{
case WM_MOUSEMOVE:
case WM_POINTERDOWN:
case WM_POINTERUP:
case WM_POINTERUPDATE:
case WM_DEVICECHANGE:
case WM_MOUSEWHEEL:
case WM_MOUSEHWHEEL:
#if _WIN32_WINNT >= 0x0500 /* 2K */
if (g_taskbar_message && message == g_taskbar_message)
taskbar_is_created = true;
#endif
#ifdef HAVE_DINPUT
if (dinput_wgl && dinput_handle_message(dinput_wgl,
message, wparam, lparam))
return 0;
#endif
break;
case WM_NCLBUTTONDBLCLK:
doubleclick_on_titlebar = true;
#if _WIN32_WINNT >= 0x0500 /* 2K */
if (g_taskbar_message && message == g_taskbar_message)
taskbar_is_created = true;
#endif
break;
case WM_DROPFILES:
case WM_SYSCOMMAND:
case WM_CHAR:
@ -838,25 +1104,18 @@ LRESULT CALLBACK WndProcGL(HWND hwnd, UINT message,
hwnd, message, wparam, lparam);
if (quit)
return ret;
#if _WIN32_WINNT >= 0x0500 /* 2K */
if (g_taskbar_message && message == g_taskbar_message)
taskbar_is_created = true;
#endif
break;
case WM_CREATE:
create_graphics_context(hwnd, &g_win32_quit);
if (DragAcceptFiles_func)
DragAcceptFiles_func(hwnd, true);
return 0;
}
#if _WIN32_WINNT >= 0x0500 /* 2K */
if (g_taskbar_message && message == g_taskbar_message)
g_taskbar_is_created = true;
#endif
#ifdef HAVE_DINPUT
if (dinput_wgl && dinput_handle_message(dinput_wgl,
message, wparam, lparam))
return 0;
#endif
return DefWindowProc(hwnd, message, wparam, lparam);
}
#endif
@ -868,11 +1127,32 @@ LRESULT CALLBACK WndProcGDI(HWND hwnd, UINT message,
LRESULT ret;
bool quit = false;
if (message == WM_NCLBUTTONDBLCLK)
doubleclick_on_titlebar = true;
switch (message)
{
case WM_MOUSEMOVE:
case WM_POINTERDOWN:
case WM_POINTERUP:
case WM_POINTERUPDATE:
case WM_DEVICECHANGE:
case WM_MOUSEWHEEL:
case WM_MOUSEHWHEEL:
#if _WIN32_WINNT >= 0x0500 /* 2K */
if (g_taskbar_message && message == g_taskbar_message)
taskbar_is_created = true;
#endif
#ifdef HAVE_DINPUT
if (dinput_gdi && dinput_handle_message(dinput_gdi,
message, wparam, lparam))
return 0;
#endif
break;
case WM_NCLBUTTONDBLCLK:
doubleclick_on_titlebar = true;
#if _WIN32_WINNT >= 0x0500 /* 2K */
if (g_taskbar_message && message == g_taskbar_message)
taskbar_is_created = true;
#endif
break;
case WM_PAINT:
{
gdi_t *gdi = (gdi_t*)video_driver_get_ptr(false);
@ -906,6 +1186,10 @@ LRESULT CALLBACK WndProcGDI(HWND hwnd, UINT message,
SelectObject(gdi->memDC, gdi->bmp_old);
}
#if _WIN32_WINNT >= 0x0500 /* 2K */
if (g_taskbar_message && message == g_taskbar_message)
taskbar_is_created = true;
#endif
break;
}
case WM_DROPFILES:
@ -924,25 +1208,18 @@ LRESULT CALLBACK WndProcGDI(HWND hwnd, UINT message,
ret = WndProcCommon(&quit, hwnd, message, wparam, lparam);
if (quit)
return ret;
#if _WIN32_WINNT >= 0x0500 /* 2K */
if (g_taskbar_message && message == g_taskbar_message)
taskbar_is_created = true;
#endif
break;
case WM_CREATE:
create_gdi_context(hwnd, &g_win32_quit);
if (DragAcceptFiles_func)
DragAcceptFiles_func(hwnd, true);
return 0;
}
#if _WIN32_WINNT >= 0x0500 /* 2K */
if (g_taskbar_message && message == g_taskbar_message)
g_taskbar_is_created = true;
#endif
#ifdef HAVE_DINPUT
if (dinput_gdi && dinput_handle_message(dinput_gdi,
message, wparam, lparam))
return 0;
#endif
return DefWindowProc(hwnd, message, wparam, lparam);
}
#endif
@ -979,7 +1256,7 @@ bool win32_window_create(void *data, unsigned style,
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500 /* 2K */
g_taskbar_message = RegisterWindowMessage("TaskbarButtonCreated");
ZeroMemory(&notification_filter, sizeof(notification_filter) );
memset(&notification_filter, 0, sizeof(notification_filter));
notification_filter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
notification_filter.dbcc_classguid = GUID_DEVINTERFACE_HID;

View File

@ -138,8 +138,6 @@ LRESULT CALLBACK WndProcGDI(HWND hwnd, UINT message,
BOOL IsIconic(HWND hwnd);
#endif
LRESULT win32_menu_loop(HWND owner, WPARAM wparam);
bool win32_load_content_from_gui(const char *szFilename);
void win32_setup_pixel_format(HDC hdc, bool supports_gl);

Some files were not shown because too many files have changed in this diff Show More