Merge branch 'ios' of git://github.com/meancoot/RetroArch

This commit is contained in:
twinaphex 2013-03-15 20:05:15 +01:00
commit 3bc487be87
80 changed files with 8752 additions and 54 deletions

View File

@ -22,8 +22,7 @@
#include "../boolean.h"
#include <pthread.h>
#include <CoreAudio/CoreAudio.h>
#include <CoreServices/CoreServices.h>
#include <CoreAudio/CoreAudioTypes.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioUnit/AUComponent.h>
@ -32,7 +31,7 @@ typedef struct coreaudio
pthread_mutex_t lock;
pthread_cond_t cond;
ComponentInstance dev;
AudioComponentInstance dev;
bool dev_alive;
fifo_buffer_t *buffer;
@ -49,7 +48,7 @@ static void coreaudio_free(void *data)
if (dev->dev_alive)
{
AudioOutputUnitStop(dev->dev);
CloseComponent(dev->dev);
AudioComponentInstanceDispose(dev->dev);
}
if (dev->buffer)
@ -102,43 +101,32 @@ static void *coreaudio_init(const char *device, unsigned rate, unsigned latency)
if (!dev)
return NULL;
CFRunLoopRef run_loop = NULL;
AudioObjectPropertyAddress addr = {
kAudioHardwarePropertyRunLoop,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
pthread_mutex_init(&dev->lock, NULL);
pthread_cond_init(&dev->cond, NULL);
AudioObjectSetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL,
sizeof(CFRunLoopRef), &run_loop);
ComponentDescription desc = {0};
// Create AudioComponent
AudioComponentDescription desc = {0};
desc.componentType = kAudioUnitType_Output;
#ifdef IOS
desc.componentSubType = kAudioUnitSubType_RemoteIO;
#else
desc.componentSubType = kAudioUnitSubType_HALOutput;
#endif
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
AudioStreamBasicDescription stream_desc = {0};
AudioStreamBasicDescription real_desc;
AudioChannelLayout layout = {0};
AURenderCallbackStruct cb = {0};
Component comp = NULL;
OSStatus res = noErr;
UInt32 i_size = 0;
size_t fifo_size;
comp = FindNextComponent(NULL, &desc);
AudioComponent comp = AudioComponentFindNext(NULL, &desc);
if (comp == NULL)
goto error;
res = OpenAComponent(comp, &dev->dev);
if (res != noErr)
if (AudioComponentInstanceNew(comp, &dev->dev) != noErr)
goto error;
dev->dev_alive = true;
// Set audio format
AudioStreamBasicDescription stream_desc = {0};
AudioStreamBasicDescription real_desc;
stream_desc.mSampleRate = rate;
stream_desc.mBitsPerChannel = sizeof(float) * CHAR_BIT;
stream_desc.mChannelsPerFrame = 2;
@ -147,20 +135,16 @@ static void *coreaudio_init(const char *device, unsigned rate, unsigned latency)
stream_desc.mFramesPerPacket = 1;
stream_desc.mFormatID = kAudioFormatLinearPCM;
stream_desc.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | (is_little_endian() ? 0 : kAudioFormatFlagIsBigEndian);
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, &stream_desc, sizeof(stream_desc));
if (res != noErr)
if (AudioUnitSetProperty(dev->dev, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, &stream_desc, sizeof(stream_desc)) != noErr)
goto error;
i_size = sizeof(real_desc);
res = AudioUnitGetProperty(dev->dev, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &real_desc, &i_size);
if (res != noErr)
// Check returned audio format
UInt32 i_size = sizeof(real_desc);;
if (AudioUnitGetProperty(dev->dev, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &real_desc, &i_size) != noErr)
goto error;
RARCH_LOG("[CoreAudio]: Using output sample rate of %.1f Hz\n", (float)real_desc.mSampleRate);
g_settings.audio.out_rate = real_desc.mSampleRate;
if (real_desc.mChannelsPerFrame != stream_desc.mChannelsPerFrame)
goto error;
if (real_desc.mBitsPerChannel != stream_desc.mBitsPerChannel)
@ -170,26 +154,34 @@ static void *coreaudio_init(const char *device, unsigned rate, unsigned latency)
if (real_desc.mFormatID != stream_desc.mFormatID)
goto error;
layout.mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelBitmap;
layout.mChannelBitmap = 0x03;
RARCH_LOG("[CoreAudio]: Using output sample rate of %.1f Hz\n", (float)real_desc.mSampleRate);
g_settings.audio.out_rate = real_desc.mSampleRate;
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_AudioChannelLayout,
kAudioUnitScope_Input, 0, &layout, sizeof(layout));
if (res != noErr)
// Set channel layout (fails on iOS)
#ifndef IOS
AudioChannelLayout layout = {0};
layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
if (AudioUnitSetProperty(dev->dev, kAudioUnitProperty_AudioChannelLayout,
kAudioUnitScope_Input, 0, &layout, sizeof(layout)) != noErr)
goto error;
#endif
// Set callbacks and finish up
AURenderCallbackStruct cb = {0};
cb.inputProc = audio_cb;
cb.inputProcRefCon = dev;
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0, &cb, sizeof(cb));
if (res != noErr)
if (AudioUnitSetProperty(dev->dev, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0, &cb, sizeof(cb)) != noErr)
goto error;
res = AudioUnitInitialize(dev->dev);
if (res != noErr)
if (AudioUnitInitialize(dev->dev) != noErr)
goto error;
size_t fifo_size;
fifo_size = (latency * g_settings.audio.out_rate) / 1000;
fifo_size *= 2 * sizeof(float);
dev->buffer_size = fifo_size;
@ -200,8 +192,7 @@ static void *coreaudio_init(const char *device, unsigned rate, unsigned latency)
RARCH_LOG("[CoreAudio]: Using buffer size of %u bytes: (latency = %u ms)\n", (unsigned)fifo_size, latency);
res = AudioOutputUnitStart(dev->dev);
if (res != noErr)
if (AudioOutputUnitStart(dev->dev) != noErr)
goto error;
return dev;

View File

@ -72,6 +72,7 @@ enum
INPUT_WII,
INPUT_XINPUT,
INPUT_LINUXRAW,
INPUT_IOS,
INPUT_NULL
};
@ -157,6 +158,8 @@ enum
#define INPUT_DEFAULT_DRIVER INPUT_WII
#elif defined(HAVE_XVIDEO)
#define INPUT_DEFAULT_DRIVER INPUT_X
#elif defined(IOS)
#define INPUT_DEFAULT_DRIVER INPUT_IOS
#else
#define INPUT_DEFAULT_DRIVER INPUT_NULL
#endif

View File

@ -152,6 +152,9 @@ static const input_driver_t *input_drivers[] = {
#if defined(__linux__) && !defined(ANDROID)
&input_linuxraw,
#endif
#ifdef IOS
&input_ios,
#endif
#ifdef HAVE_NULLINPUT
&input_null,
#endif

View File

@ -488,6 +488,7 @@ extern const input_driver_t input_xenon360;
extern const input_driver_t input_gx;
extern const input_driver_t input_xinput;
extern const input_driver_t input_linuxraw;
extern const input_driver_t input_ios;
extern const input_driver_t input_null;
#include "driver_funcs.h"

141
gfx/context/ioseagl_ctx.c Normal file
View File

@ -0,0 +1,141 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "../../driver.h"
#include "../gfx_common.h"
#include "../gl_common.h"
#include "../image.h"
#include "../fonts/gl_font.h"
#include <stdint.h>
#ifdef HAVE_GLSL
#include "../shader_glsl.h"
#endif
// C interface
static void gfx_ctx_set_swap_interval(unsigned interval)
{
extern void ios_set_game_view_sync(bool on);
ios_set_game_view_sync(interval ? true : false);
}
static void gfx_ctx_destroy(void)
{
extern void ios_destroy_game_view();
ios_destroy_game_view();
}
static void gfx_ctx_get_video_size(unsigned *width, unsigned *height)
{
extern void ios_get_game_view_size(unsigned *, unsigned *);
ios_get_game_view_size(width, height);
}
static bool gfx_ctx_init(void)
{
extern bool ios_init_game_view();
return ios_init_game_view();
}
static void gfx_ctx_swap_buffers(void)
{
extern void ios_flip_game_view();
ios_flip_game_view();
}
static void gfx_ctx_check_window(bool *quit,
bool *resize, unsigned *width, unsigned *height, unsigned frame_count)
{
(void)frame_count;
*quit = false;
unsigned new_width, new_height;
gfx_ctx_get_video_size(&new_width, &new_height);
if (new_width != *width || new_height != *height)
{
*width = new_width;
*height = new_height;
*resize = true;
}
}
static void gfx_ctx_set_resize(unsigned width, unsigned height)
{
(void)width;
(void)height;
}
static void gfx_ctx_update_window_title(bool reset)
{
}
static bool gfx_ctx_set_video_mode(
unsigned width, unsigned height,
bool fullscreen)
{
(void)width;
(void)height;
(void)fullscreen;
return true;
}
static void gfx_ctx_input_driver(const input_driver_t **input, void **input_data)
{
*input = NULL;
*input_data = NULL;
}
static bool gfx_ctx_bind_api(enum gfx_ctx_api api)
{
return api == GFX_CTX_OPENGL_ES_API;
}
static bool gfx_ctx_has_focus(void)
{
return true;
}
static bool gfx_ctx_init_egl_image_buffer(const video_info_t *video)
{
return false;
}
static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, unsigned index, void **image_handle)
{
return false;
}
const gfx_ctx_driver_t gfx_ctx_ios = {
gfx_ctx_init,
gfx_ctx_destroy,
gfx_ctx_bind_api,
gfx_ctx_set_swap_interval,
gfx_ctx_set_video_mode,
gfx_ctx_get_video_size,
NULL,
gfx_ctx_update_window_title,
gfx_ctx_check_window,
gfx_ctx_set_resize,
gfx_ctx_has_focus,
gfx_ctx_swap_buffers,
gfx_ctx_input_driver,
NULL,
gfx_ctx_init_egl_image_buffer,
gfx_ctx_write_egl_image,
NULL,
"ios",
};

View File

@ -51,6 +51,9 @@ static const gfx_ctx_driver_t *gfx_ctx_drivers[] = {
#if defined(__BLACKBERRY_QNX__)
&gfx_ctx_bbqnx,
#endif
#if defined(IOS)
&gfx_ctx_ios,
#endif
};
const gfx_ctx_driver_t *gfx_ctx_find_driver(const char *ident)

View File

@ -140,6 +140,7 @@ extern const gfx_ctx_driver_t gfx_ctx_xdk;
extern const gfx_ctx_driver_t gfx_ctx_wgl;
extern const gfx_ctx_driver_t gfx_ctx_videocore;
extern const gfx_ctx_driver_t gfx_ctx_bbqnx;
extern const gfx_ctx_driver_t gfx_ctx_ios;
const gfx_ctx_driver_t *gfx_ctx_find_driver(const char *ident); // Finds driver with ident. Does not initialize.
const gfx_ctx_driver_t *gfx_ctx_init_first(enum gfx_ctx_api api); // Finds first suitable driver and initializes.

View File

@ -760,7 +760,7 @@ void gl_set_viewport(void *data, unsigned width, unsigned height, bool force_ful
gl->vp.height = height;
}
#ifdef ANDROID
#if defined(ANDROID) || defined(IOS)
// In portrait mode, we want viewport to gravitate to top of screen.
if (device_aspect < 1.0f)
gl->vp.y *= 2;
@ -901,7 +901,13 @@ static void gl_frame_fbo(void *data, const struct gl_tex_info *tex_info)
set_texture_coords(fbo_tex_coords, xamt, yamt);
// Render our FBO texture to back buffer.
#ifdef IOS
// There is no default frame buffer on IOS.
extern void ios_bind_game_view_fbo();
ios_bind_game_view_fbo();
#else
pglBindFramebuffer(GL_FRAMEBUFFER, 0);
#endif
gl_shader_use_func(gl, gl->fbo_pass + 1);
glBindTexture(GL_TEXTURE_2D, gl->fbo_texture[gl->fbo_pass - 1]);
@ -1329,6 +1335,10 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei
RARCH_PERFORMANCE_START(copy_frame);
gl_copy_frame(gl, frame, width, height, pitch);
RARCH_PERFORMANCE_STOP(copy_frame);
#ifdef IOS // Apparently the viewport is lost each frame, thanks apple.
gl_set_viewport(gl, gl->win_width, gl->win_height, false, true);
#endif
}
else
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);

View File

@ -34,7 +34,10 @@
#include <EGL/eglext.h>
#endif
#if defined(__APPLE__)
#if defined(IOS)
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#elif defined(__APPLE__)
#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
#elif defined(HAVE_PSGL)
@ -330,7 +333,11 @@ extern PFNGLACTIVETEXTUREPROC pglActiveTexture;
#ifndef GL_BGRA_EXT
#define GL_BGRA_EXT 0x80E1
#endif
#ifdef IOS
#define RARCH_GL_INTERNAL_FORMAT32 GL_RGBA // Stupid Apple
#else
#define RARCH_GL_INTERNAL_FORMAT32 GL_BGRA_EXT
#endif
#define RARCH_GL_INTERNAL_FORMAT16 GL_RGB
#define RARCH_GL_TEXTURE_TYPE32 GL_BGRA_EXT
#define RARCH_GL_TEXTURE_TYPE16 GL_RGB

View File

@ -27,7 +27,10 @@
#include "../config.h"
#endif
#if defined(__APPLE__) // Because they like to be "oh, so, special".
#if defined(IOS)
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#elif defined(__APPLE__) // Because they like to be "oh, so, special".
#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
#elif defined(HAVE_PSGL)

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
ios/Assets/Default.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
ios/Assets/Default@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
ios/Assets/Icon-72.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
ios/Assets/Icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,261 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1552</int>
<string key="IBDocument.SystemVersion">12C60</string>
<string key="IBDocument.InterfaceBuilderVersion">3084</string>
<string key="IBDocument.AppKitVersion">1187.34</string>
<string key="IBDocument.HIToolboxVersion">625.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">2083</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBNSLayoutConstraint</string>
<string>IBProxyObject</string>
<string>IBUIButton</string>
<string>IBUIView</string>
</array>
<array key="IBDocument.PluginDependencies">
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</array>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<object class="IBProxyObject" id="372490531">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
<object class="IBProxyObject" id="975951072">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
<object class="IBUIView" id="191373211">
<reference key="NSNextResponder"/>
<int key="NSvFlags">274</int>
<array class="NSMutableArray" key="NSSubviews">
<object class="IBUIButton" id="1072319895">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">292</int>
<string key="NSFrameSize">{768, 1005}</string>
<reference key="NSSuperview" ref="191373211"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<object class="NSColor" key="IBUIBackgroundColor" id="107526919">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MCAwAA</bytes>
</object>
<bool key="IBUIOpaque">NO</bool>
<int key="IBUIContentMode">5</int>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<int key="IBUIContentHorizontalAlignment">0</int>
<int key="IBUIContentVerticalAlignment">0</int>
<object class="NSColor" key="IBUIHighlightedTitleColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
<object class="NSColor" key="IBUINormalTitleColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes>
</object>
<reference key="IBUINormalTitleShadowColor" ref="107526919"/>
<object class="NSCustomResource" key="IBUINormalBackgroundImage">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">ic_pause.png</string>
</object>
<object class="IBUIFontDescription" key="IBUIFontDescription">
<int key="type">2</int>
<double key="pointSize">15</double>
</object>
<object class="NSFont" key="IBUIFont">
<string key="NSName">Helvetica-Bold</string>
<double key="NSSize">15</double>
<int key="NSfFlags">16</int>
</object>
</object>
</array>
<string key="NSFrame">{{0, 20}, {768, 1004}}</string>
<reference key="NSSuperview"/>
<reference key="NSNextKeyView" ref="1072319895"/>
<string key="NSHuggingPriority">{250, 250}</string>
<reference key="IBUIBackgroundColor" ref="107526919"/>
<bool key="IBUIOpaque">NO</bool>
<float key="IBUIAlpha">0.0</float>
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics">
<int key="IBUIStatusBarStyle">2</int>
</object>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
</array>
<object class="IBObjectContainer" key="IBDocument.Objects">
<array class="NSMutableArray" key="connectionRecords">
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">showPauseMenu:</string>
<reference key="source" ref="1072319895"/>
<reference key="destination" ref="372490531"/>
<int key="IBEventType">7</int>
</object>
<int key="connectionID">71</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
<object class="IBObjectRecord">
<int key="objectID">0</int>
<array key="object" id="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">1</int>
<reference key="object" ref="191373211"/>
<array class="NSMutableArray" key="children">
<object class="IBNSLayoutConstraint" id="483291458">
<reference key="firstItem" ref="1072319895"/>
<int key="firstAttribute">10</int>
<int key="relation">0</int>
<reference key="secondItem" ref="191373211"/>
<int key="secondAttribute">10</int>
<float key="multiplier">1</float>
<object class="IBLayoutConstant" key="constant">
<double key="value">0.0</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">5</int>
<float key="scoringTypeFloat">22</float>
<int key="contentType">2</int>
</object>
<object class="IBNSLayoutConstraint" id="446468022">
<reference key="firstItem" ref="1072319895"/>
<int key="firstAttribute">3</int>
<int key="relation">0</int>
<reference key="secondItem" ref="191373211"/>
<int key="secondAttribute">3</int>
<float key="multiplier">1</float>
<object class="IBLayoutConstant" key="constant">
<double key="value">0.0</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="925773848">
<reference key="firstItem" ref="1072319895"/>
<int key="firstAttribute">5</int>
<int key="relation">0</int>
<reference key="secondItem" ref="191373211"/>
<int key="secondAttribute">5</int>
<float key="multiplier">1</float>
<object class="IBLayoutConstant" key="constant">
<double key="value">0.0</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="474429653">
<reference key="firstItem" ref="1072319895"/>
<int key="firstAttribute">6</int>
<int key="relation">0</int>
<reference key="secondItem" ref="191373211"/>
<int key="secondAttribute">6</int>
<float key="multiplier">1</float>
<object class="IBLayoutConstant" key="constant">
<double key="value">0.0</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<reference ref="1072319895"/>
</array>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="372490531"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="975951072"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">54</int>
<reference key="object" ref="1072319895"/>
<array class="NSMutableArray" key="children"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">61</int>
<reference key="object" ref="474429653"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">63</int>
<reference key="object" ref="925773848"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">69</int>
<reference key="object" ref="446468022"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">72</int>
<reference key="object" ref="483291458"/>
<reference key="parent" ref="191373211"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
<string key="-1.CustomClassName">RetroArch_iOS</string>
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="-2.CustomClassName">UIResponder</string>
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<array class="NSMutableArray" key="1.IBViewMetadataConstraints">
<reference ref="474429653"/>
<reference ref="925773848"/>
<reference ref="446468022"/>
<reference ref="483291458"/>
</array>
<string key="54.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<real value="0.0" key="54.IBUIButtonInspectorSelectedEdgeInsetMetadataKey"/>
<real value="0.0" key="54.IBUIButtonInspectorSelectedStateConfigurationMetadataKey"/>
<boolean value="NO" key="54.IBViewMetadataTranslatesAutoresizingMaskIntoConstraints"/>
<string key="61.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="63.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="69.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="72.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">72</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes"/>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBIPadFramework</string>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
<string key="NS.key.0">ic_pause.png</string>
<string key="NS.object.0">{100, 40}</string>
</object>
<bool key="IBDocument.UseAutolayout">YES</bool>
<string key="IBCocoaTouchPluginVersion">2083</string>
</data>
</archive>

950
ios/Assets/PauseView.xib Normal file
View File

@ -0,0 +1,950 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1552</int>
<string key="IBDocument.SystemVersion">12C60</string>
<string key="IBDocument.InterfaceBuilderVersion">3084</string>
<string key="IBDocument.AppKitVersion">1187.34</string>
<string key="IBDocument.HIToolboxVersion">625.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">2083</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBNSLayoutConstraint</string>
<string>IBProxyObject</string>
<string>IBUIButton</string>
<string>IBUISegmentedControl</string>
<string>IBUIView</string>
</array>
<array key="IBDocument.PluginDependencies">
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</array>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<object class="IBProxyObject" id="372490531">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
<object class="IBProxyObject" id="975951072">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
<object class="IBUIView" id="191373211">
<reference key="NSNextResponder"/>
<int key="NSvFlags">274</int>
<array class="NSMutableArray" key="NSSubviews">
<object class="IBUIButton" id="61755535">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{160, 237}, {120, 44}}</string>
<reference key="NSSuperview" ref="191373211"/>
<reference key="NSNextKeyView"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<bool key="IBUIOpaque">NO</bool>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<int key="IBUIContentHorizontalAlignment">0</int>
<int key="IBUIContentVerticalAlignment">0</int>
<int key="IBUIButtonType">1</int>
<string key="IBUINormalTitle">Exit</string>
<object class="NSColor" key="IBUIHighlightedTitleColor" id="428922964">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
<object class="NSColor" key="IBUINormalTitleColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes>
</object>
<object class="NSColor" key="IBUINormalTitleShadowColor" id="899103122">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MC41AA</bytes>
</object>
<object class="IBUIFontDescription" key="IBUIFontDescription" id="230061939">
<int key="type">2</int>
<double key="pointSize">15</double>
</object>
<object class="NSFont" key="IBUIFont" id="495303882">
<string key="NSName">Helvetica-Bold</string>
<double key="NSSize">15</double>
<int key="NSfFlags">16</int>
</object>
</object>
<object class="IBUIButton" id="38801877">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 20}, {260, 44}}</string>
<reference key="NSSuperview" ref="191373211"/>
<reference key="NSNextKeyView" ref="1033465661"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<bool key="IBUIOpaque">NO</bool>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<int key="IBUIContentHorizontalAlignment">0</int>
<int key="IBUIContentVerticalAlignment">0</int>
<int key="IBUIButtonType">1</int>
<string key="IBUINormalTitle">Resume Game</string>
<reference key="IBUIHighlightedTitleColor" ref="428922964"/>
<object class="NSColor" key="IBUINormalTitleColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes>
</object>
<reference key="IBUINormalTitleShadowColor" ref="899103122"/>
<reference key="IBUIFontDescription" ref="230061939"/>
<reference key="IBUIFont" ref="495303882"/>
</object>
<object class="IBUIButton" id="721040644">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 237}, {120, 44}}</string>
<reference key="NSSuperview" ref="191373211"/>
<reference key="NSNextKeyView" ref="61755535"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<bool key="IBUIOpaque">NO</bool>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<int key="IBUIContentHorizontalAlignment">0</int>
<int key="IBUIContentVerticalAlignment">0</int>
<int key="IBUIButtonType">1</int>
<string key="IBUINormalTitle">Reset</string>
<reference key="IBUIHighlightedTitleColor" ref="428922964"/>
<object class="NSColor" key="IBUINormalTitleColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes>
</object>
<reference key="IBUINormalTitleShadowColor" ref="899103122"/>
<reference key="IBUIFontDescription" ref="230061939"/>
<reference key="IBUIFont" ref="495303882"/>
</object>
<object class="IBUIButton" id="694263183">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 186}, {120, 44}}</string>
<reference key="NSSuperview" ref="191373211"/>
<reference key="NSNextKeyView" ref="58186559"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<bool key="IBUIOpaque">NO</bool>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<int key="IBUIContentHorizontalAlignment">0</int>
<int key="IBUIContentVerticalAlignment">0</int>
<int key="IBUIButtonType">1</int>
<string key="IBUINormalTitle">Save State</string>
<reference key="IBUIHighlightedTitleColor" ref="428922964"/>
<object class="NSColor" key="IBUINormalTitleColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes>
</object>
<reference key="IBUINormalTitleShadowColor" ref="899103122"/>
<reference key="IBUIFontDescription" ref="230061939"/>
<reference key="IBUIFont" ref="495303882"/>
</object>
<object class="IBUIButton" id="58186559">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{160, 186}, {120, 44}}</string>
<reference key="NSSuperview" ref="191373211"/>
<reference key="NSNextKeyView" ref="721040644"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<bool key="IBUIOpaque">NO</bool>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<int key="IBUIContentHorizontalAlignment">0</int>
<int key="IBUIContentVerticalAlignment">0</int>
<int key="IBUIButtonType">1</int>
<string key="IBUINormalTitle">Load State</string>
<reference key="IBUIHighlightedTitleColor" ref="428922964"/>
<object class="NSColor" key="IBUINormalTitleColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes>
</object>
<reference key="IBUINormalTitleShadowColor" ref="899103122"/>
<reference key="IBUIFontDescription" ref="230061939"/>
<reference key="IBUIFont" ref="495303882"/>
</object>
<object class="IBUISegmentedControl" id="562513162">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 135}, {260, 44}}</string>
<reference key="NSSuperview" ref="191373211"/>
<reference key="NSNextKeyView" ref="694263183"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<bool key="IBUIOpaque">NO</bool>
<int key="IBUITag">1</int>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<int key="IBNumberOfSegments">10</int>
<int key="IBSelectedSegmentIndex">0</int>
<array key="IBSegmentTitles">
<string>1</string>
<string>2</string>
<string>3</string>
<string>4</string>
<string>5</string>
<string>6</string>
<string>7</string>
<string>8</string>
<string>9</string>
<string>10</string>
</array>
<array class="NSMutableArray" key="IBSegmentWidths">
<real value="0.0"/>
<real value="0.0"/>
<real value="0.0"/>
<real value="0.0"/>
<real value="0.0"/>
<real value="0.0"/>
<real value="0.0"/>
<real value="0.0"/>
<real value="0.0"/>
<real value="0.0"/>
</array>
<array class="NSMutableArray" key="IBSegmentEnabledStates">
<boolean value="YES"/>
<boolean value="YES"/>
<boolean value="YES"/>
<boolean value="YES"/>
<boolean value="YES"/>
<boolean value="YES"/>
<boolean value="YES"/>
<boolean value="YES"/>
<boolean value="YES"/>
<boolean value="YES"/>
</array>
<array class="NSMutableArray" key="IBSegmentContentOffsets">
<string>{0, 0}</string>
<string>{0, 0}</string>
<string>{0, 0}</string>
<string>{0, 0}</string>
<string>{0, 0}</string>
<string>{0, 0}</string>
<string>{0, 0}</string>
<string>{0, 0}</string>
<string>{0, 0}</string>
<string>{0, 0}</string>
</array>
<array class="NSMutableArray" key="IBSegmentImages">
<object class="NSNull" id="4"/>
<reference ref="4"/>
<reference ref="4"/>
<reference ref="4"/>
<reference ref="4"/>
<reference ref="4"/>
<reference ref="4"/>
<reference ref="4"/>
<reference ref="4"/>
<reference ref="4"/>
</array>
</object>
<object class="IBUIButton" id="1033465661">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 71}, {260, 44}}</string>
<reference key="NSSuperview" ref="191373211"/>
<reference key="NSNextKeyView" ref="562513162"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<bool key="IBUIOpaque">NO</bool>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<int key="IBUIContentHorizontalAlignment">0</int>
<int key="IBUIContentVerticalAlignment">0</int>
<int key="IBUIButtonType">1</int>
<string key="IBUINormalTitle">Settings</string>
<reference key="IBUIHighlightedTitleColor" ref="428922964"/>
<object class="NSColor" key="IBUINormalTitleColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes>
</object>
<reference key="IBUINormalTitleShadowColor" ref="899103122"/>
<reference key="IBUIFontDescription" ref="230061939"/>
<reference key="IBUIFont" ref="495303882"/>
</object>
</array>
<string key="NSFrame">{{0, 20}, {300, 300}}</string>
<reference key="NSSuperview"/>
<reference key="NSNextKeyView" ref="38801877"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
<object class="NSColorSpace" key="NSCustomColorSpace">
<int key="NSID">2</int>
</object>
</object>
<bool key="IBUIOpaque">NO</bool>
<float key="IBUIAlpha">0.0</float>
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics">
<int key="IBUIStatusBarStyle">2</int>
</object>
<object class="IBUISimulatedSizeMetrics" key="IBUISimulatedDestinationMetrics">
<string key="IBUISimulatedSizeMetricsClass">IBUISimulatedFreeformSizeMetricsSentinel</string>
<string key="IBUIDisplayName">Freeform</string>
</object>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
</array>
<object class="IBObjectContainer" key="IBDocument.Objects">
<array class="NSMutableArray" key="connectionRecords">
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">closeGamePressed:</string>
<reference key="source" ref="61755535"/>
<reference key="destination" ref="372490531"/>
<int key="IBEventType">7</int>
</object>
<int key="connectionID">23</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">closePauseMenu:</string>
<reference key="source" ref="38801877"/>
<reference key="destination" ref="372490531"/>
<int key="IBEventType">7</int>
</object>
<int key="connectionID">25</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">resetGame:</string>
<reference key="source" ref="721040644"/>
<reference key="destination" ref="372490531"/>
<int key="IBEventType">7</int>
</object>
<int key="connectionID">138</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">saveState:</string>
<reference key="source" ref="694263183"/>
<reference key="destination" ref="372490531"/>
<int key="IBEventType">7</int>
</object>
<int key="connectionID">137</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">loadState:</string>
<reference key="source" ref="58186559"/>
<reference key="destination" ref="372490531"/>
<int key="IBEventType">7</int>
</object>
<int key="connectionID">136</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">chooseState:</string>
<reference key="source" ref="562513162"/>
<reference key="destination" ref="372490531"/>
<int key="IBEventType">13</int>
</object>
<int key="connectionID">139</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">showSettings</string>
<reference key="source" ref="1033465661"/>
<reference key="destination" ref="372490531"/>
<int key="IBEventType">7</int>
</object>
<int key="connectionID">148</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
<object class="IBObjectRecord">
<int key="objectID">0</int>
<array key="object" id="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">1</int>
<reference key="object" ref="191373211"/>
<array class="NSMutableArray" key="children">
<object class="IBNSLayoutConstraint" id="331948221">
<reference key="firstItem" ref="61755535"/>
<int key="firstAttribute">5</int>
<int key="relation">0</int>
<reference key="secondItem" ref="58186559"/>
<int key="secondAttribute">5</int>
<float key="multiplier">1</float>
<object class="IBLayoutConstant" key="constant">
<double key="value">0.0</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">6</int>
<float key="scoringTypeFloat">24</float>
<int key="contentType">2</int>
</object>
<object class="IBNSLayoutConstraint" id="214855274">
<reference key="firstItem" ref="191373211"/>
<int key="firstAttribute">4</int>
<int key="relation">0</int>
<reference key="secondItem" ref="61755535"/>
<int key="secondAttribute">4</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">20</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="963730365">
<reference key="firstItem" ref="191373211"/>
<int key="firstAttribute">6</int>
<int key="relation">0</int>
<reference key="secondItem" ref="61755535"/>
<int key="secondAttribute">6</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">20</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="700933706">
<reference key="firstItem" ref="191373211"/>
<int key="firstAttribute">6</int>
<int key="relation">0</int>
<reference key="secondItem" ref="58186559"/>
<int key="secondAttribute">6</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">20</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="911823456">
<reference key="firstItem" ref="58186559"/>
<int key="firstAttribute">11</int>
<int key="relation">0</int>
<reference key="secondItem" ref="694263183"/>
<int key="secondAttribute">11</int>
<float key="multiplier">1</float>
<object class="IBLayoutConstant" key="constant">
<double key="value">0.0</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">6</int>
<float key="scoringTypeFloat">24</float>
<int key="contentType">2</int>
</object>
<object class="IBNSLayoutConstraint" id="120087908">
<reference key="firstItem" ref="191373211"/>
<int key="firstAttribute">4</int>
<int key="relation">0</int>
<reference key="secondItem" ref="721040644"/>
<int key="secondAttribute">4</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">20</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="217059225">
<reference key="firstItem" ref="721040644"/>
<int key="firstAttribute">3</int>
<int key="relation">0</int>
<reference key="secondItem" ref="694263183"/>
<int key="secondAttribute">4</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">8</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">6</int>
<float key="scoringTypeFloat">24</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="770441708">
<reference key="firstItem" ref="721040644"/>
<int key="firstAttribute">5</int>
<int key="relation">0</int>
<reference key="secondItem" ref="191373211"/>
<int key="secondAttribute">5</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">20</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="837542760">
<reference key="firstItem" ref="694263183"/>
<int key="firstAttribute">3</int>
<int key="relation">0</int>
<reference key="secondItem" ref="562513162"/>
<int key="secondAttribute">4</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">8</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">6</int>
<float key="scoringTypeFloat">24</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="1015088714">
<reference key="firstItem" ref="694263183"/>
<int key="firstAttribute">6</int>
<int key="relation">0</int>
<reference key="secondItem" ref="721040644"/>
<int key="secondAttribute">6</int>
<float key="multiplier">1</float>
<object class="IBLayoutConstant" key="constant">
<double key="value">0.0</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">6</int>
<float key="scoringTypeFloat">24</float>
<int key="contentType">2</int>
</object>
<object class="IBNSLayoutConstraint" id="85952235">
<reference key="firstItem" ref="694263183"/>
<int key="firstAttribute">5</int>
<int key="relation">0</int>
<reference key="secondItem" ref="191373211"/>
<int key="secondAttribute">5</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">20</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="201787259">
<reference key="firstItem" ref="191373211"/>
<int key="firstAttribute">6</int>
<int key="relation">0</int>
<reference key="secondItem" ref="562513162"/>
<int key="secondAttribute">6</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">20</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="804573855">
<reference key="firstItem" ref="562513162"/>
<int key="firstAttribute">5</int>
<int key="relation">0</int>
<reference key="secondItem" ref="191373211"/>
<int key="secondAttribute">5</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">20</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="65927411">
<reference key="firstItem" ref="1033465661"/>
<int key="firstAttribute">5</int>
<int key="relation">0</int>
<reference key="secondItem" ref="191373211"/>
<int key="secondAttribute">5</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">20</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="402510427">
<reference key="firstItem" ref="191373211"/>
<int key="firstAttribute">6</int>
<int key="relation">0</int>
<reference key="secondItem" ref="1033465661"/>
<int key="secondAttribute">6</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">20</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="828923997">
<reference key="firstItem" ref="1033465661"/>
<int key="firstAttribute">3</int>
<int key="relation">0</int>
<reference key="secondItem" ref="38801877"/>
<int key="secondAttribute">4</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">8</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">6</int>
<float key="scoringTypeFloat">24</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="38515434">
<reference key="firstItem" ref="191373211"/>
<int key="firstAttribute">6</int>
<int key="relation">0</int>
<reference key="secondItem" ref="38801877"/>
<int key="secondAttribute">6</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">20</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="509206074">
<reference key="firstItem" ref="38801877"/>
<int key="firstAttribute">5</int>
<int key="relation">0</int>
<reference key="secondItem" ref="191373211"/>
<int key="secondAttribute">5</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">20</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="89828506">
<reference key="firstItem" ref="38801877"/>
<int key="firstAttribute">3</int>
<int key="relation">0</int>
<reference key="secondItem" ref="191373211"/>
<int key="secondAttribute">3</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">20</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="191373211"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<reference ref="694263183"/>
<reference ref="38801877"/>
<reference ref="721040644"/>
<reference ref="58186559"/>
<reference ref="562513162"/>
<reference ref="61755535"/>
<reference ref="1033465661"/>
</array>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="372490531"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="975951072"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">12</int>
<reference key="object" ref="61755535"/>
<array class="NSMutableArray" key="children"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">17</int>
<reference key="object" ref="38801877"/>
<array class="NSMutableArray" key="children"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">30</int>
<reference key="object" ref="721040644"/>
<array class="NSMutableArray" key="children"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">86</int>
<reference key="object" ref="694263183"/>
<array class="NSMutableArray" key="children">
<object class="IBNSLayoutConstraint" id="229625608">
<reference key="firstItem" ref="694263183"/>
<int key="firstAttribute">7</int>
<int key="relation">0</int>
<nil key="secondItem"/>
<int key="secondAttribute">0</int>
<float key="multiplier">1</float>
<object class="IBLayoutConstant" key="constant">
<double key="value">120</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="694263183"/>
<int key="scoringType">3</int>
<float key="scoringTypeFloat">9</float>
<int key="contentType">1</int>
</object>
</array>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">88</int>
<reference key="object" ref="85952235"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">89</int>
<reference key="object" ref="89828506"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">94</int>
<reference key="object" ref="509206074"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">97</int>
<reference key="object" ref="38515434"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">98</int>
<reference key="object" ref="963730365"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">113</int>
<reference key="object" ref="770441708"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">115</int>
<reference key="object" ref="217059225"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">116</int>
<reference key="object" ref="120087908"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">118</int>
<reference key="object" ref="214855274"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">119</int>
<reference key="object" ref="229625608"/>
<reference key="parent" ref="694263183"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">121</int>
<reference key="object" ref="1015088714"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">122</int>
<reference key="object" ref="58186559"/>
<array class="NSMutableArray" key="children">
<object class="IBNSLayoutConstraint" id="685350466">
<reference key="firstItem" ref="58186559"/>
<int key="firstAttribute">7</int>
<int key="relation">0</int>
<nil key="secondItem"/>
<int key="secondAttribute">0</int>
<float key="multiplier">1</float>
<object class="IBLayoutConstant" key="constant">
<double key="value">120</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="58186559"/>
<int key="scoringType">3</int>
<float key="scoringTypeFloat">9</float>
<int key="contentType">1</int>
</object>
</array>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">125</int>
<reference key="object" ref="911823456"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">127</int>
<reference key="object" ref="331948221"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">129</int>
<reference key="object" ref="700933706"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">130</int>
<reference key="object" ref="685350466"/>
<reference key="parent" ref="58186559"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">131</int>
<reference key="object" ref="562513162"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">132</int>
<reference key="object" ref="804573855"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">133</int>
<reference key="object" ref="837542760"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">135</int>
<reference key="object" ref="201787259"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">140</int>
<reference key="object" ref="1033465661"/>
<array class="NSMutableArray" key="children"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">142</int>
<reference key="object" ref="828923997"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">143</int>
<reference key="object" ref="402510427"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">146</int>
<reference key="object" ref="65927411"/>
<reference key="parent" ref="191373211"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
<string key="-1.CustomClassName">RetroArch_iOS</string>
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="-2.CustomClassName">UIResponder</string>
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<array class="NSMutableArray" key="1.IBViewMetadataConstraints">
<reference ref="89828506"/>
<reference ref="509206074"/>
<reference ref="38515434"/>
<reference ref="828923997"/>
<reference ref="402510427"/>
<reference ref="65927411"/>
<reference ref="804573855"/>
<reference ref="201787259"/>
<reference ref="85952235"/>
<reference ref="1015088714"/>
<reference ref="837542760"/>
<reference ref="770441708"/>
<reference ref="217059225"/>
<reference ref="120087908"/>
<reference ref="911823456"/>
<reference ref="700933706"/>
<reference ref="963730365"/>
<reference ref="214855274"/>
<reference ref="331948221"/>
</array>
<string key="113.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="115.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="116.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="118.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="119.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="12.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<boolean value="NO" key="12.IBViewMetadataTranslatesAutoresizingMaskIntoConstraints"/>
<string key="121.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="122.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<array class="NSMutableArray" key="122.IBViewMetadataConstraints">
<reference ref="685350466"/>
</array>
<boolean value="NO" key="122.IBViewMetadataTranslatesAutoresizingMaskIntoConstraints"/>
<string key="125.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="127.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="129.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="130.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="131.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<boolean value="NO" key="131.IBViewMetadataTranslatesAutoresizingMaskIntoConstraints"/>
<integer value="9" key="131.IUISegmentedControlInspectorSelectedSegmentMetadataKey"/>
<string key="132.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="133.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="135.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="140.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<boolean value="NO" key="140.IBViewMetadataTranslatesAutoresizingMaskIntoConstraints"/>
<string key="142.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="143.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="146.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="17.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<boolean value="NO" key="17.IBViewMetadataTranslatesAutoresizingMaskIntoConstraints"/>
<string key="30.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<boolean value="NO" key="30.IBViewMetadataTranslatesAutoresizingMaskIntoConstraints"/>
<string key="86.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<array key="86.IBViewMetadataConstraints">
<reference ref="229625608"/>
</array>
<boolean value="NO" key="86.IBViewMetadataTranslatesAutoresizingMaskIntoConstraints"/>
<string key="88.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="89.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="94.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="97.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="98.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">148</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes"/>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBIPadFramework</string>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<bool key="IBDocument.UseAutolayout">YES</bool>
<string key="IBCocoaTouchPluginVersion">2083</string>
</data>
</archive>

BIN
ios/Assets/ic_pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

55
ios/BUILDING Normal file
View File

@ -0,0 +1,55 @@
The following instructions will only work if building from OS X, using latest XCode with fake signing enabled.
To build RetroArch:
Open $(root)/ios/RetroArch.xcodeproj, change build type to iOSDevice then choose Product->Build For->Archiving.
After compilation finishes goto Window->Organizer->Projects->RetroArch then click the small arrow beside
the Derived Data field, this will open a finder window with the projects build files directory highlighted.
The app bundle can be found at Build/Products/* in that folder. Use scp to copy the .app bundle to the appropriate place
on your device.
To build libretro cores:
In general, to build for iOS devices, copy the configuration for OSX, then at the end add the lines:
sysroot = -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/
CC = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gcc -arch armv7 $(sysroot)
CXX = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/g++ -arch armv7 $(sysroot)
Here is how it would look in Makefile.libretro for vba-next:
...
else ifeq ($(platform), osx)
TARGET := vba_next_libretro.dylib
fpic := -fPIC
SHARED := -dynamiclib
ENDIANNESS_DEFINES = -DLSB_FIRST
else ifeq ($(platform), ios)
TARGET := vba_next_libretro.dylib
fpic := -fPIC
SHARED := -dynamiclib
ENDIANNESS_DEFINES = -DLSB_FIRST
sysroot = -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/
CC = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gcc -arch armv7 $(sysroot)
CXX = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/g++ -arch armv7 $(sysroot)
else ifeq ($(platform), ps3)
...
Other arm specific flags can be added if needed.
Finally issue the make command for the module adding 'platform=ios' at the of the command.
(e.g. 'make -f Makefile.libretro platform=ios'). Then copy the resulting .dylib file into a sub-directory of the app
bundle named modules. (e.g. RetroArch.app/modules/vba_next_libretro.dylib).
Paths:
/var/mobile/Library/RetroArch: Will be created to store both saved games and the retroarch.cfg.
/var/mobile/RetroArchGames: If this exists and is a directory it will be used as the root for game browsing.
/var/mobile: If RetroArchGames does not exist this will be used as the root for game browsing,
if another browser root is desired make RetroArchGames a symlink to the preferred path.
RetroArch.app/shaders: Contains .shader format shaders, sub-directories are supported.
RetroArch.app/overlays: Contains input overlay definitions, sub-directories are supported.
RetroArch.app/modules: Contains .dylib files for the libretro modules. The only constraint on the file name
is that it must end in .dylib, sub-directories are not supported.
Simulator:
To test in iOS Simulator you will need to create the '/var/mobile' directory on you computer, and give yourself full
access to it.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:RetroArch.xcodeproj">
</FileRef>
</Workspace>

28
ios/RetroArch/RAConfig.h Normal file
View File

@ -0,0 +1,28 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
@interface RAConfig : NSObject
- (id)initWithPath:(NSString*)path;
- (void)writeToFile:(NSString*)path;
- (bool)getBoolNamed:(NSString*)name withDefault:(bool)def;
- (int)getIntNamed:(NSString*)name withDefault:(int)def;
- (unsigned)getUintNamed:(NSString*)name withDefault:(unsigned)def;
- (double)getDoubleNamed:(NSString*)name withDefault:(double)def;
- (NSString*)getStringNamed:(NSString*)name withDefault:(NSString*)def;
- (void)putIntNamed:(NSString*)name value:(int)value;
- (void)putStringNamed:(NSString*)name value:(NSString*)value;
@end

91
ios/RetroArch/RAConfig.m Normal file
View File

@ -0,0 +1,91 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#import "RAConfig.h"
#include "conf/config_file.h"
@implementation RAConfig
{
config_file_t* _config;
}
- (id)initWithPath:(NSString*)path
{
_config = config_file_new([path UTF8String]);
_config = _config ? _config : config_file_new(0);
return self;
}
- (void)dealloc
{
config_file_free(_config);
}
- (void)writeToFile:(NSString*)path
{
config_file_write(_config, [path UTF8String]);
}
- (bool)getBoolNamed:(NSString*)name withDefault:(bool)def
{
bool result = def;
config_get_bool(_config, [name UTF8String], &result);
return result;
}
- (int)getIntNamed:(NSString*)name withDefault:(int)def
{
int result = def;
config_get_int(_config, [name UTF8String], &result);
return result;
}
- (unsigned)getUintNamed:(NSString*)name withDefault:(unsigned)def
{
unsigned result = def;
config_get_uint(_config, [name UTF8String], &result);
return result;
}
- (double)getDoubleNamed:(NSString*)name withDefault:(double)def
{
double result = def;
config_get_double(_config, [name UTF8String], &result);
return result;
}
- (NSString*)getStringNamed:(NSString*)name withDefault:(NSString*)def
{
NSString* result = def;
char* data = 0;
if (config_get_string(_config, [name UTF8String], &data))
result = [NSString stringWithUTF8String:data];
free(data);
return result;
}
- (void)putIntNamed:(NSString*)name value:(int)value
{
config_set_int(_config, [name UTF8String], value);
}
- (void)putStringNamed:(NSString*)name value:(NSString*)value
{
config_set_string(_config, [name UTF8String], [value UTF8String]);
}
@end

172
ios/RetroArch/RAGameView.m Normal file
View File

@ -0,0 +1,172 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "general.h"
#include "rarch_wrapper.h"
static const float ALMOST_INVISIBLE = .021f;
static float g_screen_scale;
static int g_frame_skips = 4;
static bool g_is_syncing = true;
static RAGameView* g_instance;
@implementation RAGameView
{
EAGLContext* _glContext;
UIView* _pauseView;
UIView* _pauseIndicatorView;
}
+ (RAGameView*)get
{
if (!g_instance)
g_instance = [RAGameView new];
return g_instance;
}
- (id)init
{
self = [super init];
UINib* xib = [UINib nibWithNibName:@"PauseView" bundle:nil];
_pauseView = [[xib instantiateWithOwner:[RetroArch_iOS get] options:nil] lastObject];
xib = [UINib nibWithNibName:@"PauseIndicatorView" bundle:nil];
_pauseIndicatorView = [[xib instantiateWithOwner:[RetroArch_iOS get] options:nil] lastObject];
self.view = [GLKView new];
self.view.multipleTouchEnabled = YES;
[self.view addSubview:_pauseView];
[self.view addSubview:_pauseIndicatorView];
return self;
}
// Driver
- (void)driverInit
{
g_screen_scale = [[UIScreen mainScreen] scale];
_glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:_glContext];
((GLKView*)self.view).context = _glContext;
// Show pause button for a few seconds, so people know it's there
_pauseIndicatorView.alpha = 1.0f;
[self performSelector:@selector(hidePauseButton) withObject:self afterDelay:3.0f];
}
- (void)driverQuit
{
glFinish();
((GLKView*)self.view).context = nil;
[EAGLContext setCurrentContext:nil];
_glContext = nil;
}
- (void)flip
{
if (--g_frame_skips < 0)
{
[self.view setNeedsDisplay];
[(GLKView*)self.view bindDrawable];
g_frame_skips = g_is_syncing ? 0 : 3;
}
}
// Pause Menus
- (void)viewWillLayoutSubviews
{
UIInterfaceOrientation orientation = self.interfaceOrientation;
CGRect screenSize = [[UIScreen mainScreen] bounds];
const float width = ((int)orientation < 3) ? CGRectGetWidth(screenSize) : CGRectGetHeight(screenSize);
const float height = ((int)orientation < 3) ? CGRectGetHeight(screenSize) : CGRectGetWidth(screenSize);
float tenpctw = width / 10.0f;
float tenpcth = height / 10.0f;
_pauseView.frame = CGRectMake(width / 2.0f - 150.0f, height / 2.0f - 150.0f, 300.0f, 300.0f);
_pauseIndicatorView.frame = CGRectMake(tenpctw * 4.0f, 0.0f, tenpctw * 2.0f, tenpcth);
}
- (void)openPauseMenu
{
// Setup save state selector
UISegmentedControl* stateSelect = (UISegmentedControl*)[_pauseView viewWithTag:1];
stateSelect.selectedSegmentIndex = (g_extern.state_slot < 10) ? g_extern.state_slot : -1;
//
[UIView animateWithDuration:0.2
animations:^ { _pauseView.alpha = 1.0f; }
completion:^(BOOL finished){}];
}
- (void)closePauseMenu
{
[UIView animateWithDuration:0.2
animations:^ { _pauseView.alpha = 0.0f; }
completion:^(BOOL finished) { }
];
}
- (void)hidePauseButton
{
[UIView animateWithDuration:0.2
animations:^ { _pauseIndicatorView.alpha = ALMOST_INVISIBLE; }
completion:^(BOOL finished) { }
];
}
@end
bool ios_init_game_view()
{
[RAGameView.get driverInit];
return true;
}
void ios_destroy_game_view()
{
[RAGameView.get driverQuit];
}
void ios_flip_game_view()
{
[RAGameView.get flip];
}
void ios_set_game_view_sync(bool on)
{
g_is_syncing = on;
g_frame_skips = on ? 0 : 3;
}
void ios_get_game_view_size(unsigned *width, unsigned *height)
{
*width = RAGameView.get.view.bounds.size.width * g_screen_scale;
*width = *width ? *width : 640;
*height = RAGameView.get.view.bounds.size.height * g_screen_scale;
*height = *height ? *height : 480;
}
void ios_bind_game_view_fbo()
{
[(GLKView*)RAGameView.get.view bindDrawable];
}

View File

@ -0,0 +1,87 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
@implementation RAModuleInfo
+ (RAModuleInfo*)moduleWithPath:(NSString*)thePath data:(RAConfig*)theData
{
RAModuleInfo* new = [RAModuleInfo new];
new.displayName = [theData getStringNamed:@"display_name" withDefault:[[thePath lastPathComponent] stringByDeletingPathExtension]];
new.path = thePath;
new.configPath = [NSString stringWithFormat:@"%@/%@.cfg", [RetroArch_iOS get].system_directory, [[thePath lastPathComponent] stringByDeletingPathExtension]];
new.data = theData;
new.supportedExtensions = [[theData getStringNamed:@"supported_extensions" withDefault:@""] componentsSeparatedByString:@"|"];
return new;
}
- (bool)supportsFileAtPath:(NSString*)path
{
return [self.supportedExtensions containsObject:[[path pathExtension] lowercaseString]];
}
@end
static NSString* const labels[3] = {@"Emulator Name", @"Manufacturer", @"Name"};
static NSString* const keys[3] = {@"emuname", @"manufacturer", @"systemname"};
static NSString* const sectionNames[2] = {@"Emulator", @"Hardware"};
static const uint32_t sectionSizes[2] = {1, 2};
@implementation RAModuleInfoList
{
RAModuleInfo* _data;
}
- (id)initWithModuleInfo:(RAModuleInfo*)info
{
self = [super initWithStyle:UITableViewStyleGrouped];
_data = info;
return self;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView
{
return sizeof(sectionSizes) / sizeof(sectionSizes[0]);
}
- (NSString*)tableView:(UITableView*)tableView titleForHeaderInSection:(NSInteger)section
{
return sectionNames[section];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return sectionSizes[section];
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:@"datacell"];
cell = (cell != nil) ? cell : [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"datacell"];
uint32_t sectionBase = 0;
for (int i = 0; i != indexPath.section; i ++)
{
sectionBase += sectionSizes[i];
}
cell.textLabel.text = labels[sectionBase + indexPath.row];
cell.detailTextLabel.text = [_data.data getStringNamed:keys[sectionBase + indexPath.row] withDefault:@"Unspecified"];
return cell;
}
@end

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>libretro.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationExitsOnSuspend</key>
<false/>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIStatusBarHidden</key>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,31 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
//
// Prefix header for all source files of the 'RetroArch' target in the 'RetroArch' project
//
#import <Availability.h>
#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "views.h"
#import "RetroArch_iOS.h"
#endif

View File

@ -0,0 +1,32 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#import <UIKit/UIKit.h>
@interface RetroArch_iOS : UINavigationController<UIApplicationDelegate, UINavigationControllerDelegate>
+ (void)displayErrorMessage:(NSString*)message;
+ (RetroArch_iOS*)get;
- (void)runGame:(NSString*)path;
- (void)refreshConfig;
- (IBAction)showSettings;
@property (strong, nonatomic) RAModuleInfo* moduleInfo;
@property (strong, nonatomic) NSString* system_directory;
@end

View File

@ -0,0 +1,287 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/stat.h>
#include "rarch_wrapper.h"
#include "general.h"
#import "browser/browser.h"
#import "settings/settings.h"
#ifdef WIIMOTE
#import "input/BTStack/WiiMoteHelper.h"
#endif
@implementation RetroArch_iOS
{
UIWindow* _window;
NSTimer* _gameTimer;
bool _isGameTop;
bool _isPaused;
bool _isRunning;
}
+ (void)displayErrorMessage:(NSString*)message
{
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"RetroArch"
message:message
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}
+ (RetroArch_iOS*)get
{
return (RetroArch_iOS*)[[UIApplication sharedApplication] delegate];
}
// UIApplicationDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
// TODO: Relocate this!
self.system_directory = @"/var/mobile/Library/RetroArch/";
mkdir([self.system_directory UTF8String], 0755);
// Setup window
self.delegate = self;
[self pushViewController:[RADirectoryList directoryListOrGridWithPath:nil] animated:YES];
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
_window.rootViewController = self;
[_window makeKeyAndVisible];
}
- (void)applicationDidBecomeActive:(UIApplication*)application
{
[self startTimer];
}
- (void)applicationWillResignActive:(UIApplication*)application
{
[self stopTimer];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
if (_isRunning)
init_drivers();
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
if (_isRunning)
uninit_drivers();
}
// UINavigationControllerDelegate
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
_isGameTop = [viewController isKindOfClass:[RAGameView class]];
[[UIApplication sharedApplication] setStatusBarHidden:_isGameTop withAnimation:UIStatusBarAnimationNone];
self.navigationBarHidden = _isGameTop;
if (_isGameTop)
[self startTimer];
self.topViewController.navigationItem.rightBarButtonItem = [self createBluetoothButton];
}
// UINavigationController: Never animate when pushing onto, or popping, an RAGameView
- (void)pushViewController:(UIViewController*)theView animated:(BOOL)animated
{
[super pushViewController:theView animated:animated && !_isGameTop];
}
- (UIViewController*)popViewControllerAnimated:(BOOL)animated
{
return [super popViewControllerAnimated:animated && !_isGameTop];
}
#pragma mark EMULATION
- (void)runGame:(NSString*)path
{
assert(self.moduleInfo);
[RASettingsList refreshConfigFile];
const char* const sd = [[RetroArch_iOS get].system_directory UTF8String];
const char* const cf = (ra_ios_is_file(self.moduleInfo.configPath)) ? [self.moduleInfo.configPath UTF8String] : 0;
const char* const libretro = [self.moduleInfo.path UTF8String];
struct rarch_main_wrap main_wrapper = {[path UTF8String], sd, sd, cf, libretro};
if (rarch_main_init_wrap(&main_wrapper) == 0)
{
rarch_init_msg_queue();
// Read load time settings
RAConfig* conf = [[RAConfig alloc] initWithPath:self.moduleInfo.configPath];
if ([conf getBoolNamed:@"ios_auto_bluetooth" withDefault:false])
[self startBluetooth];
//
[self pushViewController:RAGameView.get animated:NO];
_isRunning = true;
}
else
{
_isRunning = false;
[RetroArch_iOS displayErrorMessage:@"Failed to load game."];
}
}
- (void)closeGame
{
if (_isRunning)
{
_isRunning = false;
rarch_main_deinit();
rarch_deinit_msg_queue();
rarch_main_clear_state();
// Stop bluetooth (might be annoying but forgetting could eat battery of device AND wiimote)
[self stopBluetooth];
//
[self popToViewController:[RAGameView get] animated:NO];
[self popViewControllerAnimated:NO];
}
}
- (void)refreshConfig
{
// Need to clear these otherwise stale versions may be used!
memset(g_settings.input.overlay, 0, sizeof(g_settings.input.overlay));
memset(g_settings.video.bsnes_shader_path, 0, sizeof(g_settings.video.bsnes_shader_path));
if (_isRunning)
{
uninit_drivers();
config_load();
init_drivers();
}
}
- (void)iterate
{
if (_isPaused || !_isRunning || !_isGameTop)
[self stopTimer];
else if (_isRunning && !rarch_main_iterate())
[self closeGame];
}
- (void)startTimer
{
if (!_gameTimer)
_gameTimer = [NSTimer scheduledTimerWithTimeInterval:0.001f target:self selector:@selector(iterate) userInfo:nil repeats:YES];
}
- (void)stopTimer
{
if (_gameTimer)
[_gameTimer invalidate];
_gameTimer = nil;
}
#pragma mark PAUSE MENU
- (IBAction)showPauseMenu:(id)sender
{
if (_isRunning && !_isPaused && _isGameTop)
{
_isPaused = true;
[[RAGameView get] openPauseMenu];
}
}
- (IBAction)resetGame:(id)sender
{
if (_isRunning) rarch_game_reset();
[self closePauseMenu:sender];
}
- (IBAction)loadState:(id)sender
{
if (_isRunning) rarch_load_state();
[self closePauseMenu:sender];
}
- (IBAction)saveState:(id)sender
{
if (_isRunning) rarch_save_state();
[self closePauseMenu:sender];
}
- (IBAction)chooseState:(id)sender
{
g_extern.state_slot = ((UISegmentedControl*)sender).selectedSegmentIndex;
}
- (IBAction)closePauseMenu:(id)sender
{
[[RAGameView get] closePauseMenu];
if (_isPaused)
{
_isPaused = false;
[self startTimer];
}
}
- (IBAction)closeGamePressed:(id)sender
{
[self closePauseMenu:sender];
[self closeGame];
}
- (IBAction)showSettings
{
[self pushViewController:[RASettingsList new] animated:YES];
}
#pragma mark Bluetooth Helpers
- (UIBarButtonItem*)createBluetoothButton
{
#ifdef WIIMOTE
const bool isBTOn = [WiiMoteHelper isBluetoothRunning];
return [[UIBarButtonItem alloc]
initWithTitle:isBTOn ? @"Stop Bluetooth" : @"Start Bluetooth"
style:UIBarButtonItemStyleBordered
target:[RetroArch_iOS get]
action:isBTOn ? @selector(stopBluetooth) : @selector(startBluetooth)];
#else
return nil;
#endif
}
- (IBAction)startBluetooth
{
#ifdef WIIMOTE
[WiiMoteHelper startBluetooth];
[self.topViewController.navigationItem setRightBarButtonItem:[self createBluetoothButton] animated:YES];
#endif
}
- (IBAction)stopBluetooth
{
#ifdef WIIMOTE
[WiiMoteHelper stopBluetooth];
[self.topViewController.navigationItem setRightBarButtonItem:[self createBluetoothButton] animated:YES];
#endif
}
@end

View File

@ -0,0 +1,110 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#import "RAConfig.h"
#import "browser.h"
@implementation RADirectoryGrid
{
NSString* _path;
NSArray* _list;
RAConfig* _config;
}
- (id)initWithPath:(NSString*)path config:(RAConfig*)config
{
_path = path;
_config = config;
_list = ra_ios_list_directory(_path);
[self setTitle: [_path lastPathComponent]];
// Init collection view
UICollectionViewFlowLayout* layout = [UICollectionViewFlowLayout new];
layout.itemSize = CGSizeMake([config getUintNamed:@"cover_width" withDefault:100], [config getUintNamed:@"cover_height" withDefault:100]);
self = [super initWithCollectionViewLayout:layout];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"dircell"];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"textcell"];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"imagecell"];
return self;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [_list count];
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
RADirectoryItem* path = [_list objectAtIndex: indexPath.row];
if(path.isDirectory)
[[RetroArch_iOS get] pushViewController:[RADirectoryList directoryListOrGridWithPath:path.path] animated:YES];
else
[[RetroArch_iOS get] pushViewController:[[RAModuleList alloc] initWithGame:path.path] animated:YES];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
RADirectoryItem* path = [_list objectAtIndex: indexPath.row];
UICollectionViewCell* cell = nil;
if (path.isDirectory)
{
cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:@"dircell" forIndexPath:indexPath];
if (!cell.backgroundView)
{
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"ic_dir"]];
((UIImageView*)cell.backgroundView).contentMode = UIViewContentModeScaleAspectFit;
}
}
else if (path.coverPath)
{
cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:@"imagecell" forIndexPath:indexPath];
if (!cell.backgroundView)
{
cell.backgroundView = [UIImageView new];
((UIImageView*)cell.backgroundView).contentMode = UIViewContentModeScaleAspectFit;
}
((UIImageView*)cell.backgroundView).image = [UIImage imageWithContentsOfFile:path.coverPath];
}
else
{
cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:@"textcell" forIndexPath:indexPath];
if (!cell.backgroundView)
{
cell.backgroundView = [UILabel new];
((UILabel*)cell.backgroundView).numberOfLines = 0;
((UILabel*)cell.backgroundView).textAlignment = NSTextAlignmentCenter;
}
((UILabel*)cell.backgroundView).text = [path.path lastPathComponent];
}
return cell;
}
@end

View File

@ -0,0 +1,80 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#import "browser.h"
@implementation RADirectoryList
{
NSString* _path;
NSArray* _list;
RAConfig* _config;
}
+ (id)directoryListOrGridWithPath:(NSString*)path
{
path = ra_ios_check_path(path);
RAConfig* config = [[RAConfig alloc] initWithPath:[path stringByAppendingPathComponent:@".raconfig"]];
if ([UICollectionViewController instancesRespondToSelector:@selector(initWithCollectionViewLayout:)])
{
NSString* coverDir = [path stringByAppendingPathComponent:@".coverart"];
if (ra_ios_is_directory(coverDir))
return [[RADirectoryGrid alloc] initWithPath:path config:config];
}
return [[RADirectoryList alloc] initWithPath:path config:config];
}
- (id)initWithPath:(NSString*)path config:(RAConfig*)config
{
self = [super initWithStyle:UITableViewStylePlain];
_path = path;
_config = config;
_list = ra_ios_list_directory(_path);
[self setTitle: [_path lastPathComponent]];
return self;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
RADirectoryItem* path = [_list objectAtIndex: indexPath.row];
if(path.isDirectory)
[[RetroArch_iOS get] pushViewController:[RADirectoryList directoryListOrGridWithPath:path.path] animated:YES];
else
[[RetroArch_iOS get] pushViewController:[[RAModuleList alloc] initWithGame:path.path] animated:YES];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_list count];
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
RADirectoryItem* path = [_list objectAtIndex: indexPath.row];
UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:@"path"];
cell = (cell != nil) ? cell : [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"path"];
cell.textLabel.text = [path.path lastPathComponent];
cell.accessoryType = (path.isDirectory) ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
cell.imageView.image = [UIImage imageNamed:(path.isDirectory) ? @"ic_dir" : @"ic_file"];
return cell;
}
@end

View File

@ -0,0 +1,128 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#import "browser.h"
@implementation RAModuleList
{
NSMutableArray* _supported;
NSMutableArray* _other;
NSString* _game;
}
- (id)initWithGame:(NSString*)path
{
self = [super initWithStyle:UITableViewStyleGrouped];
_game = path;
// Get the contents of the modules directory of the bundle.
NSString* module_dir = [NSString stringWithFormat:@"%@/modules", [[NSBundle mainBundle] bundlePath]];
NSArray* moduleList = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:module_dir error:nil];
if (moduleList != nil)
{
moduleList = [module_dir stringsByAppendingPaths:moduleList];
moduleList = [moduleList pathsMatchingExtensions:[NSArray arrayWithObject:@"dylib"]];
}
if (moduleList == nil || [moduleList count] == 0)
{
[RetroArch_iOS displayErrorMessage:@"No libretro cores were found."];
}
// Load the modules with their data
_supported = [NSMutableArray array];
_other = [NSMutableArray array];
for (int i = 0; i != [moduleList count]; i ++)
{
NSString* modulePath = [moduleList objectAtIndex:i];
NSString* baseName = [[modulePath stringByDeletingPathExtension] stringByAppendingPathExtension:@"info"];
RAModuleInfo* module = [RAModuleInfo moduleWithPath:modulePath data:[[RAConfig alloc] initWithPath:baseName]];
if ([module supportsFileAtPath:_game])
[_supported addObject:module];
else
[_other addObject:module];
}
// Sort
[_supported sortUsingComparator:^(RAModuleInfo* left, RAModuleInfo* right)
{
return [left.displayName caseInsensitiveCompare:right.displayName];
}];
[_other sortUsingComparator:^(RAModuleInfo* left, RAModuleInfo* right)
{
return [left.displayName caseInsensitiveCompare:right.displayName];
}];
[self setTitle:[_game lastPathComponent]];
return self;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView
{
return _supported.count ? 2 : 1;
}
- (NSString*)tableView:(UITableView*)tableView titleForHeaderInSection:(NSInteger)section
{
if (_supported.count)
return (section == 0) ? @"Suggested Emulators" : @"Other Emulators";
return @"All Emulators";
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSMutableArray* sectionData = (_supported.count && section == 0) ? _supported : _other;
return sectionData.count;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray* sectionData = (_supported.count && indexPath.section == 0) ? _supported : _other;
[RetroArch_iOS get].moduleInfo = (RAModuleInfo*)sectionData[indexPath.row];
[[RetroArch_iOS get] runGame:_game];
}
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray* sectionData = (_supported.count && indexPath.section == 0) ? _supported : _other;
[RetroArch_iOS get].moduleInfo = (RAModuleInfo*)sectionData[indexPath.row];
[[RetroArch_iOS get] showSettings];
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:@"module"];
cell = (cell != nil) ? cell : [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"module"];
NSMutableArray* sectionData = (_supported.count && indexPath.section == 0) ? _supported : _other;
RAModuleInfo* info = (RAModuleInfo*)sectionData[indexPath.row];
cell.textLabel.text = info.displayName;
cell.accessoryType = (info.data) ? UITableViewCellAccessoryDetailDisclosureButton : UITableViewCellAccessoryNone;
return cell;
}
@end

View File

@ -0,0 +1,38 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
extern BOOL ra_ios_is_directory(NSString* path);
extern BOOL ra_ios_is_file(NSString* path);
extern NSArray* ra_ios_list_directory(NSString* path);
extern NSString* ra_ios_check_path(NSString* path);
@interface RADirectoryItem : NSObject
@property (strong) NSString* path;
@property (strong) NSString* coverPath;
@property bool isDirectory;
@end
@interface RADirectoryGrid : UICollectionViewController
- (id)initWithPath:(NSString*)path config:(RAConfig*)config;
@end
@interface RADirectoryList : UITableViewController
+ (id)directoryListOrGridWithPath:(NSString*)path;
- (id)initWithPath:(NSString*)path config:(RAConfig*)config;
@end
@interface RAModuleList : UITableViewController
- (id)initWithGame:(NSString*)path;
@end

View File

@ -0,0 +1,104 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <dirent.h>
#include <sys/stat.h>
#import "browser.h"
@implementation RADirectoryItem
+ (RADirectoryItem*)directoryItemFromPath:(const char*)thePath checkForCovers:(BOOL)checkCovers
{
RADirectoryItem* result = [RADirectoryItem new];
result.path = [NSString stringWithUTF8String:thePath];
struct stat statbuf;
if (stat(thePath, &statbuf) == 0)
result.isDirectory = S_ISDIR(statbuf.st_mode);
if (checkCovers && !result.isDirectory)
{
result.coverPath = [NSString stringWithFormat:@"%@/.coverart/%@.png", [result.path stringByDeletingLastPathComponent], [[result.path lastPathComponent] stringByDeletingPathExtension]];
if (!ra_ios_is_file(result.coverPath))
result.coverPath = nil;
}
return result;
}
@end
BOOL ra_ios_is_file(NSString* path)
{
return [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:nil];
}
BOOL ra_ios_is_directory(NSString* path)
{
BOOL result = NO;
[[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&result];
return result;
}
NSArray* ra_ios_list_directory(NSString* path)
{
NSMutableArray* result = [NSMutableArray array];
// Build list
char* cpath = malloc([path length] + sizeof(struct dirent));
sprintf(cpath, "%s/", [path UTF8String]);
size_t cpath_end = strlen(cpath);
DIR* dir = opendir(cpath);
if (!dir)
return result;
for(struct dirent* item = readdir(dir); item; item = readdir(dir))
{
if (strcmp(item->d_name, ".") == 0 || strcmp(item->d_name, "..") == 0)
continue;
cpath[cpath_end] = 0;
strcat(cpath, item->d_name);
[result addObject:[RADirectoryItem directoryItemFromPath:cpath checkForCovers:YES]];
}
closedir(dir);
free(cpath);
// Sort
[result sortUsingComparator:^(RADirectoryItem* left, RADirectoryItem* right)
{
return (left.isDirectory != right.isDirectory) ?
(left.isDirectory ? -1 : 1) :
([left.path caseInsensitiveCompare:right.path]);
}];
return result;
}
NSString* ra_ios_check_path(NSString* path)
{
if (path && ra_ios_is_directory(path))
return path;
if (path)
[RetroArch_iOS displayErrorMessage:@"Browsed path is not a directory."];
if (ra_ios_is_directory(@"/var/mobile/RetroArchGames")) return @"/var/mobile/RetroArchGames";
else if (ra_ios_is_directory(@"/var/mobile")) return @"/var/mobile";
else return @"/";
}

View File

@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */

View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2009 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
//
// BTDevice.h
//
// Created by Matthias Ringwald on 3/30/09.
//
#import <Foundation/Foundation.h>
#include "btstack/utils.h"
#define kCODHID 0x2540
#define kCODZeeMote 0x584
#define kCODInvalid 0xffff
typedef enum {
kBluetoothDeviceTypeGeneric = 0,
kBluetoothDeviceTypeHID,
kBluetoothDeviceTypeMobilePhone,
kBluetoothDeviceTypeSmartPhone,
kBluetoothDeviceTypeZeeMote,
} BluetoothDeviceType;
typedef enum {
kBluetoothConnectionNotConnected = 0,
kBluetoothConnectionRemoteName,
kBluetoothConnectionConnecting,
kBluetoothConnectionConnected
} BluetoothConnectionState;
@interface BTDevice : NSObject {
bd_addr_t _address;
NSString * name;
uint32_t classOfDevice;
uint8_t pageScanRepetitionMode;
uint16_t clockOffset;
uint8_t rssi;
// deprecated
BluetoothConnectionState connectionState;
}
- (void) setAddress:(bd_addr_t*)addr;
- (BOOL) setAddressFromString:(NSString *) addressString;
- (bd_addr_t*) address;
- (NSString *) toString;
- (NSString *) addressString;
+ (NSString *) stringForAddress:(bd_addr_t*) address;
+ (BOOL) address:(bd_addr_t *) address fromString:(NSString *) addressString;
@property (readonly) BluetoothDeviceType deviceType;
@property (readonly) NSString * nameOrAddress;
@property (nonatomic, copy) NSString * name;
@property (nonatomic, assign) uint32_t classOfDevice;
@property (nonatomic, assign) uint16_t clockOffset;
@property (nonatomic, assign) uint8_t pageScanRepetitionMode;
@property (nonatomic, assign) uint8_t rssi;
@property (nonatomic, assign) BluetoothConnectionState connectionState;
@end

View File

@ -0,0 +1,129 @@
/*
* Copyright (C) 2009 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
//
// BTDevice.m
//
// Created by Matthias Ringwald on 3/30/09.
//
#import "BTDevice.h"
@implementation BTDevice
@synthesize name;
@synthesize classOfDevice;
@synthesize connectionState;
@synthesize pageScanRepetitionMode;
@synthesize clockOffset;
@synthesize rssi;
- (BTDevice *)init {
name = NULL;
memset(&_address, 0, 6);
classOfDevice = kCODInvalid;
connectionState = kBluetoothConnectionNotConnected;
return self;
}
- (void) setAddress:(bd_addr_t*)newAddr{
BD_ADDR_COPY( &_address, newAddr);
}
- (BOOL) setAddressFromString:(NSString *) addressString{
return [BTDevice address:&_address fromString:addressString];
}
- (bd_addr_t*) address{
return &_address;
}
+ (NSString *) stringForAddress:(bd_addr_t*) address {
uint8_t *addr = (uint8_t*) *address;
return [NSString stringWithFormat:@"%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2],
addr[3], addr[4], addr[5]];
}
+ (BOOL) address:(bd_addr_t *)address fromString:(NSString *) addressString{
// support both : and - or NOTHING as separator
addressString = [addressString stringByReplacingOccurrencesOfString:@":" withString:@""];
addressString = [addressString stringByReplacingOccurrencesOfString:@"-" withString:@""];
if ([addressString length] != 12) return NO;
unsigned int bd_addr_buffer[BD_ADDR_LEN]; //for sscanf, integer needed
// reset result buffer
int i;
for (i = 0; i < BD_ADDR_LEN; i++) {
bd_addr_buffer[i] = 0;
}
// parse
int result = sscanf([addressString UTF8String], "%2x%2x%2x%2x%2x%2x", &bd_addr_buffer[0], &bd_addr_buffer[1], &bd_addr_buffer[2],
&bd_addr_buffer[3], &bd_addr_buffer[4], &bd_addr_buffer[5]);
// store
if (result == 6){
for (i = 0; i < BD_ADDR_LEN; i++) {
(*address)[i] = (uint8_t) bd_addr_buffer[i];
}
return YES;
}
return NO;
}
- (NSString *) nameOrAddress{
if (name) return name;
return [self addressString];
}
- (NSString *) addressString{
return [BTDevice stringForAddress:&_address];
}
- (BluetoothDeviceType) deviceType{
switch (classOfDevice) {
case kCODHID:
return kBluetoothDeviceTypeHID;
case kCODZeeMote:
return kBluetoothDeviceTypeZeeMote;
default:
return kBluetoothDeviceTypeGeneric;
}
}
- (NSString *) toString{
return [NSString stringWithFormat:@"Device addr %@ name %@ COD %x", [BTDevice stringForAddress:&_address], name, classOfDevice];
}
- (void)dealloc {
[name release];
[super dealloc];
}
@end

View File

@ -0,0 +1,188 @@
/*
* Copyright (C) 2009 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#pragma once
#import <Foundation/Foundation.h>
#import <stdint.h>
#import "btstack/btstack.h"
#define PREFS_REMOTE_NAME @"RemoteName"
#define PREFS_LINK_KEY @"LinkKey"
#define BTstackManagerID @"ch.ringwald.btstack"
@class BTDevice;
/*
* Information on devices is stored in a system-wide plist
* it is maintained by BTstackManager
* this includes the link keys
*/
// TODO enumerate BTstackError type
typedef int BTstackError;
typedef enum {
kDeactivated = 1,
kW4SysBTState,
kW4SysBTDisabled,
kW4Activated,
kActivated,
kW4Deactivated,
kSleeping,
#if 0
kW4DisoveryStopped,
kW4AuthenticationEnableCommand
#endif
} ManagerState;
typedef enum {
kInactive = 1,
kW4InquiryMode,
kInquiry,
kRemoteName,
// stopping
kW4InquiryModeBeforeStop,
kW4InquiryStop,
kW4RemoteNameBeforeStop,
} DiscoveryState;
@protocol BTstackManagerDelegate;
@protocol BTstackManagerListener;
@interface BTstackManager : NSObject {
@private
NSObject<BTstackManagerDelegate>* _delegate;
NSMutableArray *discoveredDevices;
NSMutableSet *listeners;
BOOL connectedToDaemon;
ManagerState state;
DiscoveryState discoveryState;
int discoveryDeviceIndex;
#if 0
// current connection - kind a ugly
uint8_t connType; // 0 = L2CAP, 1 = RFCOMM
bd_addr_t connAddr;
uint16_t connPSM;
uint16_t connChan;
uint8_t connAuth;
#endif
}
// shared instance
+(BTstackManager *) sharedInstance;
// listeners
-(void) addListener:(id<BTstackManagerListener>)listener;
-(void) removeListener:(id<BTstackManagerListener>)listener;
// Activation
-(BTstackError) activate;
-(BTstackError) deactivate;
-(BOOL) isActivating;
-(BOOL) isActive;
// Discovery
-(BTstackError) startDiscovery;
-(BTstackError) stopDiscovery;
-(int) numberOfDevicesFound;
-(BTDevice*) deviceAtIndex:(int)index;
-(BOOL) isDiscoveryActive;
// Link Key Management
-(void) dropLinkKeyForAddress:(bd_addr_t*) address;
// Connections
-(BTstackError) createL2CAPChannelAtAddress:(bd_addr_t*) address withPSM:(uint16_t)psm authenticated:(BOOL)authentication;
-(BTstackError) closeL2CAPChannelWithID:(uint16_t) channelID;
-(BTstackError) sendL2CAPPacketForChannelID:(uint16_t)channelID;
-(BTstackError) createRFCOMMConnectionAtAddress:(bd_addr_t*) address withChannel:(uint16_t)channel authenticated:(BOOL)authentication;
-(BTstackError) closeRFCOMMConnectionWithID:(uint16_t) connectionID;
-(BTstackError) sendRFCOMMPacketForChannelID:(uint16_t)connectionID;
// TODO add l2cap and rfcomm incoming commands
@property (nonatomic, assign) NSObject<BTstackManagerDelegate>* delegate;
@property (nonatomic, retain) NSMutableArray *discoveredDevices;
@property (nonatomic, retain) NSMutableSet *listeners;
@end
@protocol BTstackManagerDelegate
@optional
// Activation callbacks
-(BOOL) disableSystemBluetoothBTstackManager:(BTstackManager*) manager; // default: YES
// Connection events
-(NSString*) btstackManager:(BTstackManager*) manager pinForAddress:(bd_addr_t)addr; // default: "0000"
// direct access
-(void) btstackManager:(BTstackManager*) manager
handlePacketWithType:(uint8_t) packet_type
forChannel:(uint16_t) channel
andData:(uint8_t *)packet
withLen:(uint16_t) size;
@end
@protocol BTstackManagerListener
@optional
// Activation events
-(void) activatedBTstackManager:(BTstackManager*) manager;
-(void) btstackManager:(BTstackManager*)manager activationFailed:(BTstackError)error;
-(void) deactivatedBTstackManager:(BTstackManager*) manager;
// Power management events
-(void) sleepModeEnterBTstackManager:(BTstackManager*) manager;
-(void) sleepModeExtitBTstackManager:(BTstackManager*) manager;
// Discovery events: general
-(void) btstackManager:(BTstackManager*)manager deviceInfo:(BTDevice*)device;
-(void) btstackManager:(BTstackManager*)manager discoveryQueryRemoteName:(int)deviceIndex;
-(void) discoveryStoppedBTstackManager:(BTstackManager*) manager;
-(void) discoveryInquiryBTstackManager:(BTstackManager*) manager;
// Connection
-(void) l2capChannelCreatedAtAddress:(bd_addr_t)addr withPSM:(uint16_t)psm asID:(uint16_t)channelID;
-(void) l2capChannelCreateFailedAtAddress:(bd_addr_t)addr withPSM:(uint16_t)psm error:(BTstackError)error;
-(void) l2capChannelClosedForChannelID:(uint16_t)channelID;
-(void) l2capDataReceivedForChannelID:(uint16_t)channelID withData:(uint8_t *)packet ofLen:(uint16_t)size;
-(void) rfcommConnectionCreatedAtAddress:(bd_addr_t)addr forChannel:(uint16_t)channel asID:(uint16_t)connectionID;
-(void) rfcommConnectionCreateFailedAtAddress:(bd_addr_t)addr forChannel:(uint16_t)channel error:(BTstackError)error;
-(void) rfcommConnectionClosedForConnectionID:(uint16_t)connectionID;
-(void) rfcommDataReceivedForConnectionID:(uint16_t)connectionID withData:(uint8_t *)packet ofLen:(uint16_t)size;
// TODO add l2cap and rfcomm incoming events
@end

View File

@ -0,0 +1,590 @@
/*
* Copyright (C) 2009 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#import "BTstackManager.h"
#import "btstack/btstack.h"
#import "BTDevice.h"
#define INQUIRY_INTERVAL 3
static BTstackManager * btstackManager = nil;
@interface BTstackManager (privat)
- (void)handlePacketWithType:(uint8_t) packet_type forChannel:(uint16_t) channel andData:(uint8_t *)packet withLen:(uint16_t) size;
@end
// needed for libBTstack
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
[btstackManager handlePacketWithType:packet_type forChannel:channel andData:packet withLen:size];
}
@implementation BTstackManager
@synthesize delegate = _delegate;
@synthesize listeners;
@synthesize discoveredDevices;
-(BTstackManager *) init {
self = [super init];
if (!self) return self;
state = kDeactivated;
discoveryState = kInactive;
connectedToDaemon = NO;
// device discovery
[self setDiscoveredDevices: [[NSMutableArray alloc] init]];
// delegate and listener
_delegate = nil;
[self setListeners:[[NSMutableSet alloc] init]];
// Use Cocoa run loop
run_loop_init(RUN_LOOP_COCOA);
// our packet handler
bt_register_packet_handler(packet_handler);
return self;
}
+(BTstackManager *) sharedInstance {
if (!btstackManager) {
btstackManager = [[BTstackManager alloc] init];
}
return btstackManager;
}
// listeners
-(void) addListener:(id<BTstackManagerListener>)listener{
[listeners addObject:listener];
}
-(void) removeListener:(id<BTstackManagerListener>)listener{
[listeners removeObject:listener];
}
// send events
-(void) sendActivated {
for (NSObject<BTstackManagerListener>* listener in listeners) {
if ([listener respondsToSelector:@selector(activatedBTstackManager:)]){
[listener activatedBTstackManager:self];
}
}
}
-(void) sendActivationFailed:(BTstackError)error {
for (NSObject<BTstackManagerListener>* listener in listeners) {
if ([listener respondsToSelector:@selector(btstackManager:activationFailed:)]){
[listener btstackManager:self activationFailed:error];
}
}
}
-(void) sendDeactivated {
for (NSObject<BTstackManagerListener>* listener in listeners) {
if ([listener respondsToSelector:@selector(deactivatedBTstackManager:)]){
[listener deactivatedBTstackManager:self];
}
}
}
-(void) sendSleepEnter {
for (NSObject<BTstackManagerListener>* listener in listeners) {
if ([listener respondsToSelector:@selector(sleepModeEnterBTstackManager:)]){
[listener sleepModeEnterBTstackManager:self];
}
}
}
-(void) sendSleepExit {
for (NSObject<BTstackManagerListener>* listener in listeners) {
if ([listener respondsToSelector:@selector(sleepModeExtitBTstackManager:)]){
[listener sleepModeExtitBTstackManager:self];
}
}
}
-(void) sendDiscoveryStoppedEvent {
for (NSObject<BTstackManagerListener>* listener in listeners) {
if ([listener respondsToSelector:@selector(discoveryStoppedBTstackManager:)]){
[listener discoveryStoppedBTstackManager:self];
}
}
}
-(void) sendDiscoveryInquiry{
for (NSObject<BTstackManagerListener>* listener in listeners) {
if ([listener respondsToSelector:@selector(discoveryInquiryBTstackManager:)]){
[listener discoveryInquiryBTstackManager:self];
}
}
}
-(void) sendDiscoveryQueryRemoteName:(int)index {
for (NSObject<BTstackManagerListener>* listener in listeners) {
if ([listener respondsToSelector:@selector(btstackManager:discoveryQueryRemoteName:)]){
[listener btstackManager:self discoveryQueryRemoteName:index];
}
}
}
-(void) sendDeviceInfo:(BTDevice*) device{
for (NSObject<BTstackManagerListener>* listener in listeners) {
if ([listener respondsToSelector:@selector(btstackManager:deviceInfo:)]){
[listener btstackManager:self deviceInfo:device];
}
}
}
// Activation
-(BTstackError) activate {
BTstackError err = 0;
if (!connectedToDaemon) {
err = bt_open();
if (err) return BTSTACK_CONNECTION_TO_BTDAEMON_FAILED;
}
connectedToDaemon = YES;
// check system BT
state = kW4SysBTState;
bt_send_cmd(&btstack_get_system_bluetooth_enabled);
return err;
}
-(BTstackError) deactivate {
if (!connectedToDaemon) return BTSTACK_CONNECTION_TO_BTDAEMON_FAILED;
state = kW4Deactivated;
bt_send_cmd(&btstack_set_power_mode, HCI_POWER_OFF);
return 0;
}
-(BOOL) isActive {
return state == kActivated;
}
-(BOOL) isActivating {
switch (state){
case kW4SysBTState:
case kW4SysBTDisabled:
case kW4Activated:
return YES;
default:
return NO;
}
}
-(BOOL) isDiscoveryActive {
return state == kActivated && (discoveryState != kInactive);
}
// Discovery
-(BTstackError) startDiscovery {
if (state < kActivated) return BTSTACK_NOT_ACTIVATED;
discoveryState = kW4InquiryMode;
bt_send_cmd(&hci_write_inquiry_mode, 0x01); // with RSSI
return 0;
};
-(BTstackError) stopDiscovery{
if (state < kActivated) return BTSTACK_NOT_ACTIVATED;
switch (discoveryState){
case kInactive:
[self sendDiscoveryStoppedEvent];
break;
case kW4InquiryMode:
discoveryState = kW4InquiryModeBeforeStop;
break;
case kInquiry:
discoveryState = kW4InquiryStop;
bt_send_cmd(&hci_inquiry_cancel);
break;
case kRemoteName: {
discoveryState = kW4RemoteNameBeforeStop;
BTDevice *device = [discoveredDevices objectAtIndex:discoveryDeviceIndex];
bt_send_cmd(&hci_remote_name_request_cancel, [device address]);
break;
}
default:
NSLog(@"[BTstackManager stopDiscovery] invalid discoveryState %u", discoveryState);
[self sendDiscoveryStoppedEvent];
break;
}
return 0;
};
-(int) numberOfDevicesFound{
return [discoveredDevices count];
};
-(BTDevice*) deviceAtIndex:(int)index{
return (BTDevice*) [discoveredDevices objectAtIndex:index];
};
-(BTDevice*) deviceForAddress:(bd_addr_t*) address{
for (BTDevice *device in discoveredDevices){
// NSLog(@"compare %@ to %@", [BTDevice stringForAddress:address], [device addressString]);
if ( BD_ADDR_CMP(address, [device address]) == 0){
return device;
}
}
return nil;
}
- (void) activationHandleEvent:(uint8_t *)packet withLen:(uint16_t) size {
switch (state) {
case kW4SysBTState:
case kW4SysBTDisabled:
// BTSTACK_EVENT_SYSTEM_BLUETOOTH_ENABLED
if ( packet[0] == BTSTACK_EVENT_SYSTEM_BLUETOOTH_ENABLED){
if (packet[2]){
// system bt on - first time try to disable it
if ( state == kW4SysBTState) {
if (_delegate == nil
|| ![_delegate respondsToSelector:@selector(disableSystemBluetoothBTstackManager:)]
|| [_delegate disableSystemBluetoothBTstackManager:self]){
state = kW4SysBTDisabled;
bt_send_cmd(&btstack_set_system_bluetooth_enabled, 0);
} else {
state = kDeactivated;
[self sendActivationFailed:BTSTACK_ACTIVATION_FAILED_SYSTEM_BLUETOOTH];
}
} else {
state = kDeactivated;
[self sendActivationFailed:BTSTACK_ACTIVATION_FAILED_UNKNOWN];
}
} else {
state = kW4Activated;
bt_send_cmd(&btstack_set_power_mode, HCI_POWER_ON);
}
}
break;
case kW4Activated:
switch (packet[0]){
case BTSTACK_EVENT_STATE:
if (packet[2] == HCI_STATE_WORKING) {
state = kActivated;
[self sendActivated];
}
break;
case BTSTACK_EVENT_POWERON_FAILED:
state = kDeactivated;
[self sendActivationFailed:BTSTACK_ACTIVATION_POWERON_FAILED];
break;
default:
break;
}
break;
case kW4Deactivated:
if (packet[0] != BTSTACK_EVENT_STATE && packet[2] == HCI_STATE_OFF){
state = kDeactivated;
[self sendDeactivated];
}
break;
case kActivated:
if (packet[0] != BTSTACK_EVENT_STATE && packet[2] == HCI_STATE_FALLING_ASLEEP){
state = kSleeping;
[self sendSleepEnter];
}
break;
case kSleeping:
if (packet[0] != BTSTACK_EVENT_STATE && packet[2] == HCI_STATE_WORKING){
state = kActivated;
[self sendSleepExit];
}
break;
default:
break;
}
}
-(void) discoveryRemoteName{
BOOL found = NO;
while ( discoveryDeviceIndex < [discoveredDevices count]){
BTDevice *device = [discoveredDevices objectAtIndex:discoveryDeviceIndex];
if (device.name) {
discoveryDeviceIndex ++;
continue;
}
bt_send_cmd(&hci_remote_name_request, [device address], device.pageScanRepetitionMode,
0, device.clockOffset | 0x8000);
[self sendDiscoveryQueryRemoteName:discoveryDeviceIndex];
found = YES;
break;
}
if (!found) {
// printf("Queried all devices, restart.\n");
discoveryState = kInquiry;
bt_send_cmd(&hci_inquiry, HCI_INQUIRY_LAP, INQUIRY_INTERVAL, 0);
[self sendDiscoveryInquiry];
}
}
- (NSString *) createRemoteNameFromRemoteNameEvent:(uint8_t *) packet {
// get lenght: first null byte or max 248 chars
int nameLen = 0;
while (nameLen < 248 && packet[9+nameLen]) nameLen++;
// Bluetooth specification mandates UTF-8 encoding...
NSString *name = [[NSString alloc] initWithBytes:&packet[9] length:nameLen encoding:NSUTF8StringEncoding];
// but fallback to latin-1 for non-standard products like old Microsoft Wireless Presenter
if (!name){
name = [[NSString alloc] initWithBytes:&packet[9] length:nameLen encoding:NSISOLatin1StringEncoding];
}
return name;
}
- (void) handleRemoteNameCached: (uint8_t *) packet {
bd_addr_t addr;
bt_flip_addr(addr, &packet[3]);
// NSLog(@"Get remote name done for %@", [BTDevice stringForAddress:&addr]);
BTDevice* device = [self deviceForAddress:&addr];
if (!device) return;
[device setName:[self createRemoteNameFromRemoteNameEvent:packet]];
[self sendDeviceInfo:device];
}
- (void) handleRemoteName: (uint8_t *) packet {
bd_addr_t addr;
bt_flip_addr(addr, &packet[3]);
// NSLog(@"Get remote name done for %@", [BTDevice stringForAddress:&addr]);
BTDevice* device = [self deviceForAddress:&addr];
if (!device) return;
[device setName:[self createRemoteNameFromRemoteNameEvent:packet]];
[self sendDeviceInfo:device];
discoveryDeviceIndex++;
[self discoveryRemoteName];
}
-(void) discoveryHandleEvent:(uint8_t *)packet withLen:(uint16_t) size {
bd_addr_t addr;
int i;
int numResponses;
switch (discoveryState) {
case kInactive:
break;
case kW4InquiryMode:
if (packet[0] == HCI_EVENT_COMMAND_COMPLETE && COMMAND_COMPLETE_EVENT(packet, hci_write_inquiry_mode) ) {
discoveryState = kInquiry;
bt_send_cmd(&hci_inquiry, HCI_INQUIRY_LAP, INQUIRY_INTERVAL, 0);
[self sendDiscoveryInquiry];
}
break;
case kInquiry:
switch (packet[0]){
case HCI_EVENT_INQUIRY_RESULT:
numResponses = packet[2];
for (i=0; i<numResponses ; i++){
bt_flip_addr(addr, &packet[3+i*6]);
// NSLog(@"found %@", [BTDevice stringForAddress:&addr]);
BTDevice* device = [self deviceForAddress:&addr];
if (!device) {
device = [[BTDevice alloc] init];
[discoveredDevices addObject:device];
[device setAddress:&addr];
}
// update
device.pageScanRepetitionMode = packet [3 + numResponses*(6) + i*1];
device.classOfDevice = READ_BT_24(packet, 3 + numResponses*(6+1+1+1) + i*3);
device.clockOffset = READ_BT_16(packet, 3 + numResponses*(6+1+1+1+3) + i*2) & 0x7fff;
device.rssi = 0;
[self sendDeviceInfo:device];
}
break;
case HCI_EVENT_INQUIRY_RESULT_WITH_RSSI:
numResponses = packet[2];
for (i=0; i<numResponses ;i++){
bt_flip_addr(addr, &packet[3+i*6]);
// NSLog(@"found %@", [BTDevice stringForAddress:&addr]);
BTDevice* device = [self deviceForAddress:&addr];
if (!device) {
device = [[BTDevice alloc] init];
[discoveredDevices addObject:device];
[device setAddress:&addr];
}
device.pageScanRepetitionMode = packet [3 + numResponses*(6) + i*1];
device.classOfDevice = READ_BT_24(packet, 3 + numResponses*(6+1+1) + i*3);
device.clockOffset = READ_BT_16(packet, 3 + numResponses*(6+1+1+3) + i*2) & 0x7fff;
device.rssi = packet [3 + numResponses*(6+1+1+3+2) + i*1];
[self sendDeviceInfo:device];
}
break;
case BTSTACK_EVENT_REMOTE_NAME_CACHED:
[self handleRemoteNameCached:packet];
break;
case HCI_EVENT_INQUIRY_COMPLETE:
// printf("Inquiry scan done.\n");
discoveryState = kRemoteName;
discoveryDeviceIndex = 0;
[self discoveryRemoteName];
break;
}
break;
case kRemoteName:
if (packet[0] == HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE){
[self handleRemoteName:packet];
}
break;
case kW4InquiryModeBeforeStop:
if (packet[0] == HCI_EVENT_COMMAND_COMPLETE && COMMAND_COMPLETE_EVENT(packet, hci_write_inquiry_mode) ) {
discoveryState = kInactive;
[self sendDiscoveryStoppedEvent];
}
break;
case kW4InquiryStop:
if (packet[0] == HCI_EVENT_INQUIRY_COMPLETE
|| COMMAND_COMPLETE_EVENT(packet, hci_inquiry_cancel)) {
discoveryState = kInactive;
[self sendDiscoveryStoppedEvent];
}
break;
case kW4RemoteNameBeforeStop:
if (packet[0] == HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE
|| COMMAND_COMPLETE_EVENT(packet, hci_remote_name_request_cancel)){
discoveryState = kInactive;
[self sendDiscoveryStoppedEvent];
}
break;
default:
break;
}
}
-(void) dropLinkKeyForAddress:(bd_addr_t*) address {
bt_send_cmd(&hci_delete_stored_link_key, address, 0);
// NSLog(@"Removing link key for %@", devAddress);
}
-(void) handlePacketWithType:(uint8_t)packet_type forChannel:(uint16_t)channel andData:(uint8_t *)packet withLen:(uint16_t) size {
switch (state) {
case kDeactivated:
break;
// Activation
case kW4SysBTState:
case kW4SysBTDisabled:
case kW4Activated:
case kW4Deactivated:
case kSleeping:
if (packet_type != HCI_EVENT_PACKET) break;
[self activationHandleEvent:packet withLen:size];
break;
// Pairing + Discovery
case kActivated:
if (packet_type != HCI_EVENT_PACKET) break;
switch (packet[0]){
case BTSTACK_EVENT_STATE:
[self activationHandleEvent:packet withLen:size];
break;
default:
break;
}
[self discoveryHandleEvent:packet withLen:size];
break;
default:
break;
}
if ([_delegate respondsToSelector:@selector(btstackManager:handlePacketWithType:forChannel:andData:withLen:)]){
[_delegate btstackManager:self handlePacketWithType:packet_type forChannel:channel andData:packet withLen:size];
}
}
// Connections
-(BTstackError) createL2CAPChannelAtAddress:(bd_addr_t*) address withPSM:(uint16_t)psm authenticated:(BOOL)authentication {
if (state < kActivated) return BTSTACK_NOT_ACTIVATED;
if (state != kActivated) return BTSTACK_BUSY;
#if 0
// ...f (state
// store params
connType = 0;
BD_ADDR_COPY(&connAddr, address);
connPSM = psm;
connAuth = authentication;
// send write authentication enabled
bt_send_cmd(&hci_write_authentication_enable, authentication);
state = kW4AuthenticationEnableCommand;
#endif
return 0;
};
-(BTstackError) sendL2CAPPacketForChannelID:(uint16_t)channelID {
if (state < kActivated) return BTSTACK_NOT_ACTIVATED;
return 0;
};
-(BTstackError) closeL2CAPChannelWithID:(uint16_t) channelID {
if (state < kActivated) return BTSTACK_NOT_ACTIVATED;
return 0;
};
-(BTstackError) createRFCOMMConnectionAtAddress:(bd_addr_t*) address withChannel:(uint16_t)channel authenticated:(BOOL)authentication {
if (state < kActivated) return BTSTACK_NOT_ACTIVATED;
if (state != kActivated) return BTSTACK_BUSY;
#if 0
// store params
connType = 1;
BD_ADDR_COPY(&connAddr, address);
connChan = channel;
connAuth = authentication;
#endif
return 0;
};
-(BTstackError) sendRFCOMMPacketForChannelID:(uint16_t)connectionID {
if (state < kActivated) return BTSTACK_NOT_ACTIVATED;
return 0;
};
-(BTstackError) closeRFCOMMConnectionWithID:(uint16_t) connectionID {
if (state <kActivated) return BTSTACK_NOT_ACTIVATED;
return 0;
};
@end

View File

@ -0,0 +1,41 @@
/*
* This file is part of MAME4iOS.
*
* Copyright (C) 2012 David Valdeita (Seleuco)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* In addition, as a special exception, Seleuco
* gives permission to link the code of this program with
* the MAME library (or with modified versions of MAME that use the
* same license as MAME), and distribute linked combinations including
* the two. You must obey the GNU General Public License in all
* respects for all of the code used other than MAME. If you modify
* this file, you may extend this exception to your version of the
* file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*/
#import <UIKit/UIKit.h>
#import "BTstackManager.h"
@interface WiiMoteHelper : NSObject<BTstackManagerDelegate, BTstackManagerListener>
+ (void)startBluetooth;
+ (BOOL)isBluetoothRunning;
+ (void)stopBluetooth;
@end

View File

@ -0,0 +1,267 @@
/*
* This file is part of MAME4iOS.
*
* Copyright (C) 2012 David Valdeita (Seleuco)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* In addition, as a special exception, Seleuco
* gives permission to link the code of this program with
* the MAME library (or with modified versions of MAME that use the
* same license as MAME), and distribute linked combinations including
* the two. You must obey the GNU General Public License in all
* respects for all of the code used other than MAME. If you modify
* this file, you may extend this exception to your version of the
* file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*/
#include <sys/types.h>
#include <sys/sysctl.h>
#include <stdio.h>
#include "wiimote.h"
#import "WiiMoteHelper.h"
#import "BTDevice.h"
#import "BTstackManager.h"
static WiiMoteHelper* instance;
static BTDevice *device;
static bool btOK;
@implementation WiiMoteHelper
+ (void)startBluetooth
{
instance = instance ? instance : [WiiMoteHelper new];
if (!btOK)
{
BTstackManager* bt = [BTstackManager sharedInstance];
[bt setDelegate:instance];
[bt addListener:instance];
btOK = [bt activate] == 0;
}
}
+ (BOOL)isBluetoothRunning
{
return btOK;
}
+ (void)stopBluetooth
{
myosd_num_of_joys = 0;
if (btOK)
{
BTstackManager* bt = [BTstackManager sharedInstance];
[bt deactivate];
[bt setDelegate:nil];
[bt removeListener:instance];
btOK = false;
}
instance = nil;
}
// BTStackManagerListener
-(void) activatedBTstackManager:(BTstackManager*) manager
{
[[BTstackManager sharedInstance] startDiscovery];
}
-(void) btstackManager:(BTstackManager*)manager deviceInfo:(BTDevice*)newDevice
{
if ([newDevice name] && [[newDevice name] hasPrefix:@"Nintendo RVL-CNT-01"])
{
device = newDevice;
[[BTstackManager sharedInstance] stopDiscovery];
}
}
-(void) discoveryStoppedBTstackManager:(BTstackManager*) manager
{
bt_send_cmd(&hci_write_authentication_enable, 0);
}
// BTStackManagerDelegate
-(void) btstackManager:(BTstackManager*) manager
handlePacketWithType:(uint8_t)packet_type
forChannel:(uint16_t)channel
andData:(uint8_t*)packet
withLen:(uint16_t)size
{
bd_addr_t event_addr;
switch (packet_type)
{
case L2CAP_DATA_PACKET://0x06
{
struct wiimote_t *wm = wiimote_get_by_source_cid(channel);
if (wm != NULL)
{
byte* msg = packet + 2;
byte event = packet[1];
switch (event)
{
case WM_RPT_BTN:
{
wiimote_pressed_buttons(wm, msg);
break;
}
case WM_RPT_READ:
{
/* data read */
wiimote_pressed_buttons(wm, msg);
byte len = ((msg[2] & 0xF0) >> 4) + 1;
byte *data = (msg + 5);
if(wiimote_handshake(wm, WM_RPT_READ, data, len))
{
if (device != nil)
{
[device setConnectionState:kBluetoothConnectionConnected];
device = nil;
}
}
return;
}
case WM_RPT_CTRL_STATUS:
{
wiimote_pressed_buttons(wm, msg);
//handshake stuff!
if(wiimote_handshake(wm,WM_RPT_CTRL_STATUS,msg,-1))
{
[device setConnectionState:kBluetoothConnectionConnected];
if (device != nil)
{
[device setConnectionState:kBluetoothConnectionConnected];
device = nil;
}
}
return;
}
case WM_RPT_BTN_EXP:
{
/* button - expansion */
wiimote_pressed_buttons(wm, msg);
wiimote_handle_expansion(wm, msg+2);
break;
}
case WM_RPT_WRITE:
{
/* write feedback - safe to skip */
break;
}
default:
{
printf("Unknown event, can not handle it [Code 0x%x].", event);
return;
}
}
}
break;
}
case HCI_EVENT_PACKET://0x04
{
switch (packet[0])
{
case HCI_EVENT_COMMAND_COMPLETE:
{
if (COMMAND_COMPLETE_EVENT(packet, hci_write_authentication_enable))
bt_send_cmd(&l2cap_create_channel, [device address], PSM_HID_INTERRUPT);
break;
}
case HCI_EVENT_PIN_CODE_REQUEST:
{
bt_flip_addr(event_addr, &packet[2]);
if (BD_ADDR_CMP([device address], event_addr)) break;
// inform about pin code request
NSLog(@"HCI_EVENT_PIN_CODE_REQUEST\n");
bt_send_cmd(&hci_pin_code_request_reply, event_addr, 6, &packet[2]); // use inverse bd_addr as PIN
break;
}
case L2CAP_EVENT_CHANNEL_OPENED:
{
// data: event (8), len(8), status (8), address(48), handle (16), psm (16), local_cid(16), remote_cid (16)
if (packet[2] == 0)
{
// inform about new l2cap connection
bt_flip_addr(event_addr, &packet[3]);
uint16_t psm = READ_BT_16(packet, 11);
uint16_t source_cid = READ_BT_16(packet, 13);
uint16_t wiiMoteConHandle = READ_BT_16(packet, 9);
if (psm == 0x13)
{
// interupt channel openedn succesfully, now open control channel, too.
bt_send_cmd(&l2cap_create_channel, event_addr, 0x11);
struct wiimote_t *wm = &joys[myosd_num_of_joys];
memset(wm, 0, sizeof(struct wiimote_t));
wm->unid = myosd_num_of_joys;
wm->i_source_cid = source_cid;
memcpy(&wm->addr,&event_addr,BD_ADDR_LEN);
wm->exp.type = EXP_NONE;
}
else
{
//inicializamos el wiimote!
struct wiimote_t *wm = &joys[myosd_num_of_joys];
wm->wiiMoteConHandle = wiiMoteConHandle;
wm->c_source_cid = source_cid;
wm->state = WIIMOTE_STATE_CONNECTED;
myosd_num_of_joys++;
wiimote_handshake(wm,-1,NULL,-1);
}
}
break;
}
case L2CAP_EVENT_CHANNEL_CLOSED:
{
// data: event (8), len(8), channel (16)
uint16_t source_cid = READ_BT_16(packet, 2);
bd_addr_t addr;
wiimote_remove(source_cid,&addr);
break;
}
}
}
}
}
@end

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2009 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/*
* btstack.h
*
* Created by Matthias Ringwald on 7/1/09.
*
* BTstack client API
*
*/
#pragma once
#include "hci_cmds.h"
#include "run_loop.h"
#include "utils.h"
#include <stdint.h>
#if defined __cplusplus
extern "C" {
#endif
// Default TCP port for BTstack daemon
#define BTSTACK_PORT 13333
// UNIX domain socket for BTstack */
#define BTSTACK_UNIX "/tmp/BTstack"
// packet handler
typedef void (*btstack_packet_handler_t) (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
// optional: if called before bt_open, TCP socket is used instead of local unix socket
// note: address is not copied and must be valid during bt_open
void bt_use_tcp(const char * address, uint16_t port);
// init BTstack library
int bt_open(void);
// stop using BTstack library
int bt_close(void);
// send hci cmd packet
int bt_send_cmd(const hci_cmd_t *cmd, ...);
// register packet handler -- channel only valid for l2cap and rfcomm packets
// @returns old packet handler
btstack_packet_handler_t bt_register_packet_handler(btstack_packet_handler_t handler);
void bt_send_acl(uint8_t * data, uint16_t len);
void bt_send_l2cap(uint16_t local_cid, uint8_t *data, uint16_t len);
void bt_send_rfcomm(uint16_t rfcom_cid, uint8_t *data, uint16_t len);
#if defined __cplusplus
}
#endif

View File

@ -0,0 +1,380 @@
/*
* Copyright (C) 2009 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/*
* hci_cmds.h
*
* Created by Matthias Ringwald on 7/23/09.
*/
#pragma once
#include <stdint.h>
#if defined __cplusplus
extern "C" {
#endif
/**
* packet types - used in BTstack and over the H4 UART interface
*/
#define HCI_COMMAND_DATA_PACKET 0x01
#define HCI_ACL_DATA_PACKET 0x02
#define HCI_SCO_DATA_PACKET 0x03
#define HCI_EVENT_PACKET 0x04
// extension for client/server communication
#define DAEMON_EVENT_PACKET 0x05
// L2CAP data
#define L2CAP_DATA_PACKET 0x06
// RFCOMM data
#define RFCOMM_DATA_PACKET 0x07
// Attribute protocol data
#define ATT_DATA_PACKET 0x08
// Security Manager protocol data
#define SM_DATA_PACKET 0x09
// debug log messages
#define LOG_MESSAGE_PACKET 0xfc
// Fixed PSM numbers
#define PSM_SDP 0x01
#define PSM_RFCOMM 0x03
#define PSM_HID_CONTROL 0x11
#define PSM_HID_INTERRUPT 0x13
// Events from host controller to host
#define HCI_EVENT_INQUIRY_COMPLETE 0x01
#define HCI_EVENT_INQUIRY_RESULT 0x02
#define HCI_EVENT_CONNECTION_COMPLETE 0x03
#define HCI_EVENT_CONNECTION_REQUEST 0x04
#define HCI_EVENT_DISCONNECTION_COMPLETE 0x05
#define HCI_EVENT_AUTHENTICATION_COMPLETE_EVENT 0x06
#define HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE 0x07
#define HCI_EVENT_ENCRYPTION_CHANGE 0x08
#define HCI_EVENT_CHANGE_CONNECTION_LINK_KEY_COMPLETE 0x09
#define HCI_EVENT_MASTER_LINK_KEY_COMPLETE 0x0A
#define HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE 0x0B
#define HCI_EVENT_READ_REMOTE_VERSION_INFORMATION_COMPLETE 0x0C
#define HCI_EVENT_QOS_SETUP_COMPLETE 0x0D
#define HCI_EVENT_COMMAND_COMPLETE 0x0E
#define HCI_EVENT_COMMAND_STATUS 0x0F
#define HCI_EVENT_HARDWARE_ERROR 0x10
#define HCI_EVENT_FLUSH_OCCURED 0x11
#define HCI_EVENT_ROLE_CHANGE 0x12
#define HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS 0x13
#define HCI_EVENT_MODE_CHANGE_EVENT 0x14
#define HCI_EVENT_RETURN_LINK_KEYS 0x15
#define HCI_EVENT_PIN_CODE_REQUEST 0x16
#define HCI_EVENT_LINK_KEY_REQUEST 0x17
#define HCI_EVENT_LINK_KEY_NOTIFICATION 0x18
#define HCI_EVENT_DATA_BUFFER_OVERFLOW 0x1A
#define HCI_EVENT_MAX_SLOTS_CHANGED 0x1B
#define HCI_EVENT_READ_CLOCK_OFFSET_COMPLETE 0x1C
#define HCI_EVENT_PACKET_TYPE_CHANGED 0x1D
#define HCI_EVENT_INQUIRY_RESULT_WITH_RSSI 0x22
#define HCI_EVENT_EXTENDED_INQUIRY_RESPONSE 0x2F
#define HCI_EVENT_LE_META 0x3E
#define HCI_EVENT_VENDOR_SPECIFIC 0xFF
#define HCI_SUBEVENT_LE_CONNECTION_COMPLETE 0x01
#define HCI_SUBEVENT_LE_ADVERTISING_REPORT 0x02
#define HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE 0x03
#define HCI_SUBEVENT_LE_READ_REMOTE_USED_FEATURES_COMPLETE 0x04
#define HCI_SUBEVENT_LE_LONG_TERM_KEY_REQUEST 0x05
// last used HCI_EVENT in 2.1 is 0x3d
// events 0x50-0x5f are used internally
// BTSTACK DAEMON EVENTS
// events from BTstack for application/client lib
#define BTSTACK_EVENT_STATE 0x60
// data: event(8), len(8), nr hci connections
#define BTSTACK_EVENT_NR_CONNECTIONS_CHANGED 0x61
// data: none
#define BTSTACK_EVENT_POWERON_FAILED 0x62
// data: majot (8), minor (8), revision(16)
#define BTSTACK_EVENT_VERSION 0x63
// data: system bluetooth on/off (bool)
#define BTSTACK_EVENT_SYSTEM_BLUETOOTH_ENABLED 0x64
// data: event (8), len(8), status (8) == 0, address (48), name (1984 bits = 248 bytes)
#define BTSTACK_EVENT_REMOTE_NAME_CACHED 0x65
// data: discoverable enabled (bool)
#define BTSTACK_EVENT_DISCOVERABLE_ENABLED 0x66
// L2CAP EVENTS
// data: event (8), len(8), status (8), address(48), handle (16), psm (16), local_cid(16), remote_cid (16), local_mtu(16), remote_mtu(16)
#define L2CAP_EVENT_CHANNEL_OPENED 0x70
// data: event (8), len(8), channel (16)
#define L2CAP_EVENT_CHANNEL_CLOSED 0x71
// data: event (8), len(8), address(48), handle (16), psm (16), local_cid(16), remote_cid (16)
#define L2CAP_EVENT_INCOMING_CONNECTION 0x72
// data: event(8), len(8), handle(16)
#define L2CAP_EVENT_TIMEOUT_CHECK 0x73
// data: event(8), len(8), local_cid(16), credits(8)
#define L2CAP_EVENT_CREDITS 0x74
// data: event(8), len(8), status (8), psm (16)
#define L2CAP_EVENT_SERVICE_REGISTERED 0x75
// RFCOMM EVENTS
// data: event(8), len(8), status (8), address (48), handle (16), server channel(8), rfcomm_cid(16), max frame size(16)
#define RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE 0x80
// data: event(8), len(8), rfcomm_cid(16)
#define RFCOMM_EVENT_CHANNEL_CLOSED 0x81
// data: event (8), len(8), address(48), channel (8), rfcomm_cid (16)
#define RFCOMM_EVENT_INCOMING_CONNECTION 0x82
// data: event (8), len(8), rfcommid (16), ...
#define RFCOMM_EVENT_REMOTE_LINE_STATUS 0x83
// data: event(8), len(8), rfcomm_cid(16), credits(8)
#define RFCOMM_EVENT_CREDITS 0x84
// data: event(8), len(8), status (8), rfcomm server channel id (8)
#define RFCOMM_EVENT_SERVICE_REGISTERED 0x85
// data: event(8), len(8), status (8), rfcomm server channel id (8)
#define RFCOMM_EVENT_PERSISTENT_CHANNEL 0x86
// data: event(8), len(8), status(8), service_record_handle(32)
#define SDP_SERVICE_REGISTERED 0x90
// last error code in 2.1 is 0x38 - we start with 0x50 for BTstack errors
#define BTSTACK_CONNECTION_TO_BTDAEMON_FAILED 0x50
#define BTSTACK_ACTIVATION_FAILED_SYSTEM_BLUETOOTH 0x51
#define BTSTACK_ACTIVATION_POWERON_FAILED 0x52
#define BTSTACK_ACTIVATION_FAILED_UNKNOWN 0x53
#define BTSTACK_NOT_ACTIVATED 0x54
#define BTSTACK_BUSY 0x55
#define BTSTACK_MEMORY_ALLOC_FAILED 0x56
#define BTSTACK_ACL_BUFFERS_FULL 0x57
// l2cap errors - enumeration by the command that created them
#define L2CAP_COMMAND_REJECT_REASON_COMMAND_NOT_UNDERSTOOD 0x60
#define L2CAP_COMMAND_REJECT_REASON_SIGNALING_MTU_EXCEEDED 0x61
#define L2CAP_COMMAND_REJECT_REASON_INVALID_CID_IN_REQUEST 0x62
#define L2CAP_CONNECTION_RESPONSE_RESULT_SUCCESSFUL 0x63
#define L2CAP_CONNECTION_RESPONSE_RESULT_PENDING 0x64
#define L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_PSM 0x65
#define L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_SECURITY 0x66
#define L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_RESOURCES 0x65
#define L2CAP_CONFIG_RESPONSE_RESULT_SUCCESSFUL 0x66
#define L2CAP_CONFIG_RESPONSE_RESULT_UNACCEPTABLE_PARAMS 0x67
#define L2CAP_CONFIG_RESPONSE_RESULT_REJECTED 0x68
#define L2CAP_CONFIG_RESPONSE_RESULT_UNKNOWN_OPTIONS 0x69
#define L2CAP_SERVICE_ALREADY_REGISTERED 0x6a
#define RFCOMM_MULTIPLEXER_STOPPED 0x70
#define RFCOMM_CHANNEL_ALREADY_REGISTERED 0x71
#define RFCOMM_NO_OUTGOING_CREDITS 0x72
#define SDP_HANDLE_ALREADY_REGISTERED 0x80
/**
* Default INQ Mode
*/
#define HCI_INQUIRY_LAP 0x9E8B33L // 0x9E8B33: General/Unlimited Inquiry Access Code (GIAC)
/**
* Hardware state of Bluetooth controller
*/
typedef enum {
HCI_POWER_OFF = 0,
HCI_POWER_ON,
HCI_POWER_SLEEP
} HCI_POWER_MODE;
/**
* State of BTstack
*/
typedef enum {
HCI_STATE_OFF = 0,
HCI_STATE_INITIALIZING,
HCI_STATE_WORKING,
HCI_STATE_HALTING,
HCI_STATE_SLEEPING,
HCI_STATE_FALLING_ASLEEP
} HCI_STATE;
/**
* compact HCI Command packet description
*/
typedef struct {
uint16_t opcode;
const char *format;
} hci_cmd_t;
// HCI Commands - see hci_cmds.c for info on parameters
extern const hci_cmd_t btstack_get_state;
extern const hci_cmd_t btstack_set_power_mode;
extern const hci_cmd_t btstack_set_acl_capture_mode;
extern const hci_cmd_t btstack_get_version;
extern const hci_cmd_t btstack_get_system_bluetooth_enabled;
extern const hci_cmd_t btstack_set_system_bluetooth_enabled;
extern const hci_cmd_t btstack_set_discoverable;
extern const hci_cmd_t btstack_set_bluetooth_enabled; // only used by btstack config
extern const hci_cmd_t hci_accept_connection_request;
extern const hci_cmd_t hci_authentication_requested;
extern const hci_cmd_t hci_change_connection_link_key;
extern const hci_cmd_t hci_create_connection;
extern const hci_cmd_t hci_create_connection_cancel;
extern const hci_cmd_t hci_delete_stored_link_key;
extern const hci_cmd_t hci_disconnect;
extern const hci_cmd_t hci_host_buffer_size;
extern const hci_cmd_t hci_inquiry;
extern const hci_cmd_t hci_inquiry_cancel;
extern const hci_cmd_t hci_link_key_request_negative_reply;
extern const hci_cmd_t hci_link_key_request_reply;
extern const hci_cmd_t hci_pin_code_request_reply;
extern const hci_cmd_t hci_pin_code_request_negative_reply;
extern const hci_cmd_t hci_qos_setup;
extern const hci_cmd_t hci_read_bd_addr;
extern const hci_cmd_t hci_read_buffer_size;
extern const hci_cmd_t hci_read_le_host_supported;
extern const hci_cmd_t hci_read_link_policy_settings;
extern const hci_cmd_t hci_read_link_supervision_timeout;
extern const hci_cmd_t hci_read_local_supported_features;
extern const hci_cmd_t hci_read_num_broadcast_retransmissions;
extern const hci_cmd_t hci_reject_connection_request;
extern const hci_cmd_t hci_remote_name_request;
extern const hci_cmd_t hci_remote_name_request_cancel;
extern const hci_cmd_t hci_reset;
extern const hci_cmd_t hci_role_discovery;
extern const hci_cmd_t hci_set_event_mask;
extern const hci_cmd_t hci_set_connection_encryption;
extern const hci_cmd_t hci_sniff_mode;
extern const hci_cmd_t hci_switch_role_command;
extern const hci_cmd_t hci_write_authentication_enable;
extern const hci_cmd_t hci_write_class_of_device;
extern const hci_cmd_t hci_write_extended_inquiry_response;
extern const hci_cmd_t hci_write_inquiry_mode;
extern const hci_cmd_t hci_write_le_host_supported;
extern const hci_cmd_t hci_write_link_policy_settings;
extern const hci_cmd_t hci_write_link_supervision_timeout;
extern const hci_cmd_t hci_write_local_name;
extern const hci_cmd_t hci_write_num_broadcast_retransmissions;
extern const hci_cmd_t hci_write_page_timeout;
extern const hci_cmd_t hci_write_scan_enable;
extern const hci_cmd_t hci_write_simple_pairing_mode;
extern const hci_cmd_t hci_le_add_device_to_whitelist;
extern const hci_cmd_t hci_le_clear_white_list;
extern const hci_cmd_t hci_le_connection_update;
extern const hci_cmd_t hci_le_create_connection;
extern const hci_cmd_t hci_le_create_connection_cancel;
extern const hci_cmd_t hci_le_encrypt;
extern const hci_cmd_t hci_le_long_term_key_negative_reply;
extern const hci_cmd_t hci_le_long_term_key_request_reply;
extern const hci_cmd_t hci_le_rand;
extern const hci_cmd_t hci_le_read_advertising_channel_tx_power;
extern const hci_cmd_t hci_le_read_buffer_size ;
extern const hci_cmd_t hci_le_read_channel_map;
extern const hci_cmd_t hci_le_read_remote_used_features;
extern const hci_cmd_t hci_le_read_supported_features;
extern const hci_cmd_t hci_le_read_supported_states;
extern const hci_cmd_t hci_le_read_white_list_size;
extern const hci_cmd_t hci_le_receiver_test;
extern const hci_cmd_t hci_le_remove_device_from_whitelist;
extern const hci_cmd_t hci_le_set_advertise_enable;
extern const hci_cmd_t hci_le_set_advertising_data;
extern const hci_cmd_t hci_le_set_advertising_parameters;
extern const hci_cmd_t hci_le_set_event_mask;
extern const hci_cmd_t hci_le_set_host_channel_classification;
extern const hci_cmd_t hci_le_set_random_address;
extern const hci_cmd_t hci_le_set_scan_enable;
extern const hci_cmd_t hci_le_set_scan_parameters;
extern const hci_cmd_t hci_le_set_scan_response_data;
extern const hci_cmd_t hci_le_start_encryption;
extern const hci_cmd_t hci_le_test_end;
extern const hci_cmd_t hci_le_transmitter_test;
extern const hci_cmd_t l2cap_accept_connection;
extern const hci_cmd_t l2cap_create_channel;
extern const hci_cmd_t l2cap_create_channel_mtu;
extern const hci_cmd_t l2cap_decline_connection;
extern const hci_cmd_t l2cap_disconnect;
extern const hci_cmd_t l2cap_register_service;
extern const hci_cmd_t l2cap_unregister_service;
extern const hci_cmd_t sdp_register_service_record;
extern const hci_cmd_t sdp_unregister_service_record;
// accept connection @param bd_addr(48), rfcomm_cid (16)
extern const hci_cmd_t rfcomm_accept_connection;
// create rfcomm channel: @param bd_addr(48), channel (8)
extern const hci_cmd_t rfcomm_create_channel;
// create rfcomm channel: @param bd_addr(48), channel (8), mtu (16), credits (8)
extern const hci_cmd_t rfcomm_create_channel_with_initial_credits;
// decline rfcomm disconnect,@param bd_addr(48), rfcomm cid (16), reason(8)
extern const hci_cmd_t rfcomm_decline_connection;
// disconnect rfcomm disconnect, @param rfcomm_cid(8), reason(8)
extern const hci_cmd_t rfcomm_disconnect;
// register rfcomm service: @param channel(8), mtu (16)
extern const hci_cmd_t rfcomm_register_service;
// register rfcomm service: @param channel(8), mtu (16), initial credits (8)
extern const hci_cmd_t rfcomm_register_service_with_initial_credits;
// unregister rfcomm service, @param service_channel(16)
extern const hci_cmd_t rfcomm_unregister_service;
// request persisten rfcomm channel for service name: serive name (char*)
extern const hci_cmd_t rfcomm_persistent_channel_for_service;
#if defined __cplusplus
}
#endif

View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2009 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/*
* linked_list.h
*
* Created by Matthias Ringwald on 7/13/09.
*/
#pragma once
#if defined __cplusplus
extern "C" {
#endif
typedef struct linked_item {
struct linked_item *next; // <-- next element in list, or NULL
void *user_data; // <-- pointer to struct base
} linked_item_t;
typedef linked_item_t * linked_list_t;
void linked_item_set_user(linked_item_t *item, void *user_data); // <-- set user data
void * linked_item_get_user(linked_item_t *item); // <-- get user data
int linked_list_empty(linked_list_t * list);
void linked_list_add(linked_list_t * list, linked_item_t *item); // <-- add item to list as first element
void linked_list_add_tail(linked_list_t * list, linked_item_t *item); // <-- add item to list as last element
int linked_list_remove(linked_list_t * list, linked_item_t *item); // <-- remove item from list
linked_item_t * linked_list_get_last_item(linked_list_t * list); // <-- find the last item in the list
void test_linked_list(void);
#if defined __cplusplus
}
#endif

View File

@ -0,0 +1,122 @@
/*
* Copyright (C) 2009 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/*
* run_loop.h
*
* Created by Matthias Ringwald on 6/6/09.
*/
#pragma once
//#include "config.h"
#define HAVE_TIME
#include "linked_list.h"
#include <stdint.h>
#ifdef HAVE_TIME
#include <sys/time.h>
#endif
#if defined __cplusplus
extern "C" {
#endif
typedef enum {
RUN_LOOP_POSIX = 1,
RUN_LOOP_COCOA,
RUN_LOOP_EMBEDDED
} RUN_LOOP_TYPE;
typedef struct data_source {
linked_item_t item;
int fd; // <-- file descriptor to watch or 0
int (*process)(struct data_source *ds); // <-- do processing
} data_source_t;
typedef struct timer {
linked_item_t item;
#ifdef HAVE_TIME
struct timeval timeout; // <-- next timeout
#endif
#ifdef HAVE_TICK
uint32_t timeout; // timeout in system ticks
#endif
void (*process)(struct timer *ts); // <-- do processing
} timer_source_t;
// Set timer based on current time in milliseconds.
void run_loop_set_timer(timer_source_t *a, uint32_t timeout_in_ms);
// Set callback that will be executed when timer expires.
void run_loop_set_timer_handler(timer_source_t *ts, void (*process)(timer_source_t *_ts));
// Add/Remove timer source.
void run_loop_add_timer(timer_source_t *timer);
int run_loop_remove_timer(timer_source_t *timer);
// Init must be called before any other run_loop call.
// Use RUN_LOOP_EMBEDDED for embedded devices.
void run_loop_init(RUN_LOOP_TYPE type);
// Set data source callback.
void run_loop_set_data_source_handler(data_source_t *ds, int (*process)(data_source_t *_ds));
// Add/Remove data source.
void run_loop_add_data_source(data_source_t *dataSource);
int run_loop_remove_data_source(data_source_t *dataSource);
// Execute configured run loop. This function does not return.
void run_loop_execute(void);
// hack to fix HCI timer handling
#ifdef HAVE_TICK
// Sets how many miliseconds has one tick.
uint32_t embedded_ticks_for_ms(uint32_t time_in_ms);
// Queries the current time in ticks.
uint32_t embedded_get_ticks(void);
#endif
#ifdef EMBEDDED
// Sets an internal flag that is checked in the critical section
// just before entering sleep mode. Has to be called by the interupt
// handler of a data source to signal the run loop that a new data
// is available.
void embedded_trigger(void);
#endif
#if defined __cplusplus
}
#endif

View File

@ -0,0 +1,138 @@
/*
* Copyright (C) 2010 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/*
* sdp_util.h
*/
#pragma once
#include <stdint.h>
#if defined __cplusplus
extern "C" {
#endif
typedef enum {
DE_NIL = 0,
DE_UINT,
DE_INT,
DE_UUID,
DE_STRING,
DE_BOOL,
DE_DES,
DE_DEA,
DE_URL
} de_type_t;
typedef enum {
DE_SIZE_8 = 0,
DE_SIZE_16,
DE_SIZE_32,
DE_SIZE_64,
DE_SIZE_128,
DE_SIZE_VAR_8,
DE_SIZE_VAR_16,
DE_SIZE_VAR_32
} de_size_t;
// UNIVERSAL ATTRIBUTE DEFINITIONS
#define SDP_ServiceRecordHandle 0x0000
#define SDP_ServiceClassIDList 0x0001
#define SDP_ServiceRecordState 0x0002
#define SDP_ServiceID 0x0003
#define SDP_ProtocolDescriptorList 0x0004
#define SDP_BrowseGroupList 0x0005
#define SDP_LanguageBaseAttributeIDList 0x0006
#define SDP_ServiceInfoTimeToLive 0x0007
#define SDP_ServiceAvailability 0x0008
#define SDP_BluetoothProfileDescriptorList 0x0009
#define SDP_DocumentationURL 0x000a
#define SDP_ClientExecutableURL 0x000b
#define SDP_IconURL 0x000c
#define SDP_AdditionalProtocolDescriptorList 0x000d
#define SDP_SupportedFormatsList 0x0303
// SERVICE CLASSES
#define SDP_OBEXObjectPush 0x1105
#define SDP_OBEXFileTransfer 0x1106
#define SDP_PublicBrowseGroup 0x1002
// PROTOCOLS
#define SDP_SDPProtocol 0x0001
#define SDP_UDPProtocol 0x0002
#define SDP_RFCOMMProtocol 0x0003
#define SDP_OBEXProtocol 0x0008
#define SDP_L2CAPProtocol 0x0100
// OFFSETS FOR LOCALIZED ATTRIBUTES - SDP_LanguageBaseAttributeIDList
#define SDP_Offest_ServiceName 0x0000
#define SDP_Offest_ServiceDescription 0x0001
#define SDP_Offest_ProviderName 0x0002
// OBEX
#define SDP_vCard_2_1 0x01
#define SDP_vCard_3_0 0x02
#define SDP_vCal_1_0 0x03
#define SDP_iCal_2_0 0x04
#define SDP_vNote 0x05
#define SDP_vMessage 0x06
#define SDP_OBEXFileTypeAny 0xFF
// MARK: DateElement
void de_dump_data_element(uint8_t * record);
int de_get_len(uint8_t *header);
de_size_t de_get_size_type(uint8_t *header);
de_type_t de_get_element_type(uint8_t *header);
int de_get_header_size(uint8_t * header);
void de_create_sequence(uint8_t *header);
void de_store_descriptor_with_len(uint8_t * header, de_type_t type, de_size_t size, uint32_t len);
uint8_t * de_push_sequence(uint8_t *header);
void de_pop_sequence(uint8_t * parent, uint8_t * child);
void de_add_number(uint8_t *seq, de_type_t type, de_size_t size, uint32_t value);
void de_add_data( uint8_t *seq, de_type_t type, uint16_t size, uint8_t *data);
int de_get_data_size(uint8_t * header);
void de_add_uuid128(uint8_t * seq, uint8_t * uuid);
// MARK: SDP
uint16_t sdp_append_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint8_t *buffer);
uint8_t * sdp_get_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID);
uint8_t sdp_set_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID, uint32_t value);
int sdp_record_matches_service_search_pattern(uint8_t *record, uint8_t *serviceSearchPattern);
int spd_get_filtered_size(uint8_t *record, uint8_t *attributeIDList);
int sdp_filter_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint16_t *usedBytes, uint8_t *buffer);
void sdp_create_spp_service(uint8_t *service, int service_id, const char *name);
#if defined __cplusplus
}
#endif

View File

@ -0,0 +1,127 @@
/*
* Copyright (C) 2009 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/*
* utils.h
*
* General utility functions
*
* Created by Matthias Ringwald on 7/23/09.
*/
#pragma once
#if defined __cplusplus
extern "C" {
#endif
#include <stdint.h>
/**
* @brief hci connection handle type
*/
typedef uint16_t hci_con_handle_t;
/**
* @brief Length of a bluetooth device address.
*/
#define BD_ADDR_LEN 6
typedef uint8_t bd_addr_t[BD_ADDR_LEN];
/**
* @brief The link key type
*/
#define LINK_KEY_LEN 16
typedef uint8_t link_key_t[LINK_KEY_LEN];
/**
* @brief The device name type
*/
#define DEVICE_NAME_LEN 248
typedef uint8_t device_name_t[DEVICE_NAME_LEN+1];
// helper for BT little endian format
#define READ_BT_16( buffer, pos) ( ((uint16_t) buffer[pos]) | (((uint16_t)buffer[pos+1]) << 8))
#define READ_BT_24( buffer, pos) ( ((uint32_t) buffer[pos]) | (((uint32_t)buffer[pos+1]) << 8) | (((uint32_t)buffer[pos+2]) << 16))
#define READ_BT_32( buffer, pos) ( ((uint32_t) buffer[pos]) | (((uint32_t)buffer[pos+1]) << 8) | (((uint32_t)buffer[pos+2]) << 16) | (((uint32_t) buffer[pos+3])) << 24)
// helper for SDP big endian format
#define READ_NET_16( buffer, pos) ( ((uint16_t) buffer[pos+1]) | (((uint16_t)buffer[pos ]) << 8))
#define READ_NET_32( buffer, pos) ( ((uint32_t) buffer[pos+3]) | (((uint32_t)buffer[pos+2]) << 8) | (((uint32_t)buffer[pos+1]) << 16) | (((uint32_t) buffer[pos])) << 24)
// HCI CMD OGF/OCF
#define READ_CMD_OGF(buffer) (buffer[1] >> 2)
#define READ_CMD_OCF(buffer) ((buffer[1] & 0x03) << 8 | buffer[0])
// check if command complete event for given command
#define COMMAND_COMPLETE_EVENT(event,cmd) ( event[0] == HCI_EVENT_COMMAND_COMPLETE && READ_BT_16(event,3) == cmd.opcode)
#define COMMAND_STATUS_EVENT(event,cmd) ( event[0] == HCI_EVENT_COMMAND_STATUS && READ_BT_16(event,4) == cmd.opcode)
// Code+Len=2, Pkts+Opcode=3; total=5
#define OFFSET_OF_DATA_IN_COMMAND_COMPLETE 5
// ACL Packet
#define READ_ACL_CONNECTION_HANDLE( buffer ) ( READ_BT_16(buffer,0) & 0x0fff)
#define READ_ACL_FLAGS( buffer ) ( buffer[1] >> 4 )
#define READ_ACL_LENGTH( buffer ) (READ_BT_16(buffer, 2))
// L2CAP Packet
#define READ_L2CAP_LENGTH(buffer) ( READ_BT_16(buffer, 4))
#define READ_L2CAP_CHANNEL_ID(buffer) ( READ_BT_16(buffer, 6))
void bt_store_16(uint8_t *buffer, uint16_t pos, uint16_t value);
void bt_store_32(uint8_t *buffer, uint16_t pos, uint32_t value);
void bt_flip_addr(bd_addr_t dest, bd_addr_t src);
void net_store_16(uint8_t *buffer, uint16_t pos, uint16_t value);
void net_store_32(uint8_t *buffer, uint16_t pos, uint32_t value);
void hexdump(void *data, int size);
void printUUID(uint8_t *uuid);
// @deprecated please use more convenient bd_addr_to_str
void print_bd_addr( bd_addr_t addr);
char * bd_addr_to_str(bd_addr_t addr);
int sscan_bd_addr(uint8_t * addr_string, bd_addr_t addr);
uint8_t crc8_check(uint8_t *data, uint16_t len, uint8_t check_sum);
uint8_t crc8_calc(uint8_t *data, uint16_t len);
#define BD_ADDR_CMP(a,b) memcmp(a,b, BD_ADDR_LEN)
#define BD_ADDR_COPY(dest,src) memcpy(dest,src,BD_ADDR_LEN)
#if defined __cplusplus
}
#endif

Binary file not shown.

View File

@ -0,0 +1,692 @@
/*
* This file is part of iMAME4all.
*
* Copyright (C) 2010 David Valdeita (Seleuco)
*
* based on:
*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* In addition, as a special exception, Seleuco
* gives permission to link the code of this program with
* the MAME library (or with modified versions of MAME that use the
* same license as MAME), and distribute linked combinations including
* the two. You must obey the GNU General Public License in all
* respects for all of the code used other than MAME. If you modify
* this file, you may extend this exception to your version of the
* file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include "btstack/btstack.h"
#include "wiimote.h"
int myosd_num_of_joys = 0;
struct wiimote_t joys[4];
extern int g_pref_wii_DZ_value;
#define STICK4WAY (myosd_waysStick == 4 && myosd_inGame)
#define STICK2WAY (myosd_waysStick == 2 && myosd_inGame)
int wiimote_send(struct wiimote_t* wm, byte report_type, byte* msg, int len);
int wiimote_read_data(struct wiimote_t* wm, unsigned int addr, unsigned short len);
int wiimote_write_data(struct wiimote_t* wm, unsigned int addr, byte* data, byte len);
void wiimote_set_leds(struct wiimote_t* wm, int leds);
int classic_ctrl_handshake(struct wiimote_t* wm, struct classic_ctrl_t* cc, byte* data, unsigned short len);
void classic_ctrl_event(struct classic_ctrl_t* cc, byte* msg);
int wiimote_remove(uint16_t source_cid, bd_addr_t *addr){
int i = 0;
int unid = -1;
int found = 0;
for(;i<myosd_num_of_joys;i++)
{
if(joys[i].c_source_cid==source_cid && !found)
{
found=1;
struct wiimote_t *wm = NULL;
wm = &joys[i];
if(WIIMOTE_DBG)printf("%02x:%02x:%02x:%02x:%02x:%02x\n",wm->addr[0], wm->addr[1], wm->addr[2],wm->addr[3], wm->addr[4], wm->addr[5]);
memcpy(addr,&(wm->addr),BD_ADDR_LEN);
unid = wm->unid;
continue;
}
if(found)
{
memcpy(&joys[i-1],&joys[i],sizeof(struct wiimote_t ));
joys[i-1].unid = i-1;
struct wiimote_t *wm = NULL;
wm = &joys[i-1];
if(wm->unid==0)
wiimote_set_leds(wm, WIIMOTE_LED_1);
else if(wm->unid==1)
wiimote_set_leds(wm, WIIMOTE_LED_2);
else if(wm->unid==2)
wiimote_set_leds(wm, WIIMOTE_LED_3);
else if(wm->unid==3)
wiimote_set_leds(wm, WIIMOTE_LED_4);
}
}
if(found)
{
myosd_num_of_joys--;
if(WIIMOTE_DBG)printf("NUM JOYS %d\n",myosd_num_of_joys);
return unid;
}
return unid;
}
/**
* @brief Find a wiimote_t structure by its source_cid.
*
* @param wm Pointer to a wiimote_t structure.
* @param wiimotes The number of wiimote_t structures in \a wm.
* @param unid The unique identifier to search for.
*
* @return Pointer to a wiimote_t structure, or NULL if not found.
*/
struct wiimote_t* wiimote_get_by_source_cid(uint16_t source_cid){
int i = 0;
for (; i < myosd_num_of_joys; ++i) {
if(WIIMOTE_DBG)printf("0x%02x 0x%02x\n",joys[i].i_source_cid,source_cid);
if (joys[i].i_source_cid == source_cid)
return &joys[i];
}
return NULL;
}
/**
* @brief Request the wiimote controller status.
*
* @param wm Pointer to a wiimote_t structure.
*
* Controller status includes: battery level, LED status, expansions
*/
void wiimote_status(struct wiimote_t* wm) {
byte buf = 0;
if (!wm || !WIIMOTE_IS_CONNECTED(wm))
return;
if(WIIMOTE_DBG)printf("Requested wiimote status.\n");
wiimote_send(wm, WM_CMD_CTRL_STATUS, &buf, 1);
}
void wiimote_data_report(struct wiimote_t* wm, byte type) {
byte buf[2] = {0x0,0x0};
if (!wm || !WIIMOTE_IS_CONNECTED(wm))
return;
buf[1] = type;
//CUIDADO es un &buf?
wiimote_send(wm, WM_CMD_REPORT_TYPE, buf, 2);
}
/**
* @brief Set the enabled LEDs.
*
* @param wm Pointer to a wiimote_t structure.
* @param leds What LEDs to enable.
*
* \a leds is a bitwise or of WIIMOTE_LED_1, WIIMOTE_LED_2, WIIMOTE_LED_3, or WIIMOTE_LED_4.
*/
void wiimote_set_leds(struct wiimote_t* wm, int leds) {
byte buf;
if (!wm || !WIIMOTE_IS_CONNECTED(wm))
return;
/* remove the lower 4 bits because they control rumble */
wm->leds = (leds & 0xF0);
buf = wm->leds;
wiimote_send(wm, WM_CMD_LED, &buf, 1);
}
/**
* @brief Find what buttons are pressed.
*
* @param wm Pointer to a wiimote_t structure.
* @param msg The message specified in the event packet.
*/
void wiimote_pressed_buttons(struct wiimote_t* wm, byte* msg) {
short now;
/* convert to big endian */
now = BIG_ENDIAN_SHORT(*(short*)msg) & WIIMOTE_BUTTON_ALL;
/* buttons pressed now */
wm->btns = now;
}
/**
* @brief Handle data from the expansion.
*
* @param wm A pointer to a wiimote_t structure.
* @param msg The message specified in the event packet for the expansion.
*/
void wiimote_handle_expansion(struct wiimote_t* wm, byte* msg) {
switch (wm->exp.type) {
case EXP_CLASSIC:
classic_ctrl_event(&wm->exp.classic, msg);
break;
default:
break;
}
}
/**
* @brief Get initialization data from the wiimote.
*
* @param wm Pointer to a wiimote_t structure.
* @param data unused
* @param len unused
*
* When first called for a wiimote_t structure, a request
* is sent to the wiimote for initialization information.
* This includes factory set accelerometer data.
* The handshake will be concluded when the wiimote responds
* with this data.
*/
int wiimote_handshake(struct wiimote_t* wm, byte event, byte* data, unsigned short len) {
if (!wm) return 0;
while(1)
{
if(WIIMOTE_DBG)printf("Handshake %d\n",wm->handshake_state);
switch (wm->handshake_state) {
case 0://no ha habido nunca handshake, debemos forzar un mensaje de staus para ver que pasa.
{
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
wiimote_set_leds(wm, WIIMOTE_LED_NONE);
/* request the status of the wiimote to see if there is an expansion */
wiimote_status(wm);
wm->handshake_state=1;
return 0;
}
case 1://estamos haciendo handshake o bien se necesita iniciar un nuevo handshake ya que se inserta(quita una expansion.
{
int attachment = 0;
if(event != WM_RPT_CTRL_STATUS)
return 0;
/* is an attachment connected to the expansion port? */
if ((data[2] & WM_CTRL_STATUS_BYTE1_ATTACHMENT) == WM_CTRL_STATUS_BYTE1_ATTACHMENT)
{
attachment = 1;
}
if(WIIMOTE_DBG)printf("attachment %d %d\n",attachment,WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP));
/* expansion port */
if (attachment && !WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP)) {
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_EXP);
/* send the initialization code for the attachment */
if(WIIMOTE_DBG)printf("haciendo el handshake de la expansion\n");
if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_HANDSHAKE_COMPLETE))
{
if(WIIMOTE_DBG)printf("rehandshake\n");
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE_COMPLETE);
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);//forzamos un handshake por si venimos de un hanshake completo
}
byte buf;
//Old way. initialize the extension was by writing the single encryption byte 0x00 to 0x(4)A40040
//buf = 0x00;
//wiimote_write_data(wm, WM_EXP_MEM_ENABLE, &buf, 1);
//NEW WAY 0x55 to 0x(4)A400F0, then writing 0x00 to 0x(4)A400FB. (support clones)
buf = 0x55;
wiimote_write_data(wm, 0x04A400F0, &buf, 1);
usleep(100000);
buf = 0x00;
wiimote_write_data(wm, 0x04A400FB, &buf, 1);
//check extension type!
usleep(100000);
wiimote_read_data(wm, WM_EXP_MEM_CALIBR+220, 4);
//wiimote_read_data(wm, WM_EXP_MEM_CALIBR, EXP_HANDSHAKE_LEN);
wm->handshake_state = 4;
return 0;
} else if (!attachment && WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP)) {
/* attachment removed */
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP);
wm->exp.type = EXP_NONE;
if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_HANDSHAKE_COMPLETE))
{
if(WIIMOTE_DBG)printf("rehandshake\n");
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE_COMPLETE);
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);//forzamos un handshake por si venimos de un hanshake completo
}
}
if(!attachment && WIIMOTE_IS_SET(wm,WIIMOTE_STATE_HANDSHAKE))
{
wm->handshake_state = 2;
continue;
}
return 0;
}
case 2://find handshake no expansion
{
if(WIIMOTE_DBG)printf("Finalizado HANDSHAKE SIN EXPANSION\n");
wiimote_data_report(wm,WM_RPT_BTN);
wm->handshake_state = 6;
continue;
}
case 3://find handshake expansion
{
if(WIIMOTE_DBG)printf("Finalizado HANDSHAKE CON EXPANSION\n");
wiimote_data_report(wm,WM_RPT_BTN_EXP);
wm->handshake_state = 6;
continue;
}
case 4:
{
if(event != WM_RPT_READ)
return 0;
int id = BIG_ENDIAN_LONG(*(int*)(data));
if(WIIMOTE_DBG)printf("Expansion id=0x%04x\n",id);
if(id!=/*EXP_ID_CODE_CLASSIC_CONTROLLER*/0xa4200101)
{
wm->handshake_state = 2;
//WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP);
continue;
}
else
{
usleep(100000);
wiimote_read_data(wm, WM_EXP_MEM_CALIBR, 16);//pedimos datos de calibracion del JOY!
wm->handshake_state = 5;
}
return 0;
}
case 5:
{
if(event != WM_RPT_READ)
return 0;
classic_ctrl_handshake(wm, &wm->exp.classic, data,len);
wm->handshake_state = 3;
continue;
}
case 6:
{
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE_COMPLETE);
wm->handshake_state = 1;
if(wm->unid==0)
wiimote_set_leds(wm, WIIMOTE_LED_1);
else if(wm->unid==1)
wiimote_set_leds(wm, WIIMOTE_LED_2);
else if(wm->unid==2)
wiimote_set_leds(wm, WIIMOTE_LED_3);
else if(wm->unid==3)
wiimote_set_leds(wm, WIIMOTE_LED_4);
return 1;
}
default:
{
break;
}
}
}
}
/**
* @brief Send a packet to the wiimote.
*
* @param wm Pointer to a wiimote_t structure.
* @param report_type The report type to send (WIIMOTE_CMD_LED, WIIMOTE_CMD_RUMBLE, etc). Found in wiimote.h
* @param msg The payload.
* @param len Length of the payload in bytes.
*
* This function should replace any write()s directly to the wiimote device.
*/
int wiimote_send(struct wiimote_t* wm, byte report_type, byte* msg, int len) {
byte buf[32];
buf[0] = WM_SET_REPORT | WM_BT_OUTPUT;
buf[1] = report_type;
memcpy(buf+2, msg, len);
if(WIIMOTE_DBG)
{
int x = 2;
printf("[DEBUG] (id %i) SEND: (%x) %.2x ", wm->unid, buf[0], buf[1]);
for (; x < len+2; ++x)
printf("%.2x ", buf[x]);
printf("\n");
}
bt_send_l2cap( wm->c_source_cid, buf, len+2);
return 1;
}
/**
* @brief Read data from the wiimote (event version).
*
* @param wm Pointer to a wiimote_t structure.
* @param addr The address of wiimote memory to read from.
* @param len The length of the block to be read.
*
* The library can only handle one data read request at a time
* because it must keep track of the buffer and other
* events that are specific to that request. So if a request
* has already been made, subsequent requests will be added
* to a pending list and be sent out when the previous
* finishes.
*/
int wiimote_read_data(struct wiimote_t* wm, unsigned int addr, unsigned short len) {
//No puden ser mas de 16 lo leido o vendra en trozos!
if (!wm || !WIIMOTE_IS_CONNECTED(wm))
return 0;
if (!len /*|| len > 16*/)
return 0;
byte buf[6];
/* the offset is in big endian */
*(int*)(buf) = BIG_ENDIAN_LONG(addr);
/* the length is in big endian */
*(short*)(buf + 4) = BIG_ENDIAN_SHORT(len);
if(WIIMOTE_DBG)printf("Request read at address: 0x%x length: %i", addr, len);
wiimote_send(wm, WM_CMD_READ_DATA, buf, 6);
return 1;
}
/**
* @brief Write data to the wiimote.
*
* @param wm Pointer to a wiimote_t structure.
* @param addr The address to write to.
* @param data The data to be written to the memory location.
* @param len The length of the block to be written.
*/
int wiimote_write_data(struct wiimote_t* wm, unsigned int addr, byte* data, byte len) {
byte buf[21] = {0}; /* the payload is always 23 */
if (!wm || !WIIMOTE_IS_CONNECTED(wm))
return 0;
if (!data || !len)
return 0;
if(WIIMOTE_DBG)printf("Writing %i bytes to memory location 0x%x...\n", len, addr);
if(WIIMOTE_DBG)
{
int i = 0;
printf("Write data is: ");
for (; i < len; ++i)
printf("%x ", data[i]);
printf("\n");
}
/* the offset is in big endian */
*(int*)(buf) = BIG_ENDIAN_LONG(addr);
/* length */
*(byte*)(buf + 4) = len;
/* data */
memcpy(buf + 5, data, len);
wiimote_send(wm, WM_CMD_WRITE_DATA, buf, 21);
return 1;
}
/////////////////////// CLASSIC /////////////////
static void classic_ctrl_pressed_buttons(struct classic_ctrl_t* cc, short now);
void calc_joystick_state(struct joystick_t* js, float x, float y);
/**
* @brief Handle the handshake data from the classic controller.
*
* @param cc A pointer to a classic_ctrl_t structure.
* @param data The data read in from the device.
* @param len The length of the data block, in bytes.
*
* @return Returns 1 if handshake was successful, 0 if not.
*/
int classic_ctrl_handshake(struct wiimote_t* wm, struct classic_ctrl_t* cc, byte* data, unsigned short len) {
int offset = 0;
cc->btns = 0;
cc->r_shoulder = 0;
cc->l_shoulder = 0;
/* decrypt data */
/*
for (i = 0; i < len; ++i)
data[i] = (data[i] ^ 0x17) + 0x17;
*/
if(WIIMOTE_DBG)
{
int x = 0;
printf("[DECRIPTED]");
for (; x < len; x++)
printf("%.2x ", data[x]);
printf("\n");
}
/*
if (data[offset] == 0xFF)
{
return 0;//ERROR!
}
*/
/* joystick stuff */
if (data[offset] != 0xFF && data[offset] != 0x00)
{
cc->ljs.max.x = data[0 + offset] / 4;
cc->ljs.min.x = data[1 + offset] / 4;
cc->ljs.center.x = data[2 + offset] / 4;
cc->ljs.max.y = data[3 + offset] / 4;
cc->ljs.min.y = data[4 + offset] / 4;
cc->ljs.center.y = data[5 + offset] / 4;
cc->rjs.max.x = data[6 + offset] / 8;
cc->rjs.min.x = data[7 + offset] / 8;
cc->rjs.center.x = data[8 + offset] / 8;
cc->rjs.max.y = data[9 + offset] / 8;
cc->rjs.min.y = data[10 + offset] / 8;
cc->rjs.center.y = data[11 + offset] / 8;
}
else
{
cc->ljs.max.x = 55;
cc->ljs.min.x = 5;
cc->ljs.center.x = 30;
cc->ljs.max.y = 55;
cc->ljs.min.y = 5;
cc->ljs.center.y = 30;
cc->rjs.max.x = 30;
cc->rjs.min.x = 0;
cc->rjs.center.x = 15;
cc->rjs.max.y = 30;
cc->rjs.min.y = 0;
cc->rjs.center.y = 15;
}
/* handshake done */
wm->exp.type = EXP_CLASSIC;
return 1;
}
/**
* @brief Handle classic controller event.
*
* @param cc A pointer to a classic_ctrl_t structure.
* @param msg The message specified in the event packet.
*/
void classic_ctrl_event(struct classic_ctrl_t* cc, byte* msg) {
int lx, ly, rx, ry;
byte l, r;
/* decrypt data */
/*
for (i = 0; i < 6; ++i)
msg[i] = (msg[i] ^ 0x17) + 0x17;
*/
classic_ctrl_pressed_buttons(cc, BIG_ENDIAN_SHORT(*(short*)(msg + 4)));
/* left/right buttons */
l = (((msg[2] & 0x60) >> 2) | ((msg[3] & 0xE0) >> 5));
r = (msg[3] & 0x1F);
/*
* TODO - LR range hardcoded from 0x00 to 0x1F.
* This is probably in the calibration somewhere.
*/
cc->r_shoulder = ((float)r / 0x1F);
cc->l_shoulder = ((float)l / 0x1F);
/* calculate joystick orientation */
lx = (msg[0] & 0x3F);
ly = (msg[1] & 0x3F);
rx = ((msg[0] & 0xC0) >> 3) | ((msg[1] & 0xC0) >> 5) | ((msg[2] & 0x80) >> 7);
ry = (msg[2] & 0x1F);
if(WIIMOTE_DBG)
printf("lx ly rx ry %d %d %d %d\n",lx,ly,rx,ry);
calc_joystick_state(&cc->ljs, lx, ly);
calc_joystick_state(&cc->rjs, rx, ry);
/*
printf("classic L button pressed: %f\n", cc->l_shoulder);
printf("classic R button pressed: %f\n", cc->r_shoulder);
printf("classic left joystick angle: %f\n", cc->ljs.ang);
printf("classic left joystick magnitude: %f\n", cc->ljs.mag);
printf("classic right joystick angle: %f\n", cc->rjs.ang);
printf("classic right joystick magnitude: %f\n", cc->rjs.mag);
*/
}
/**
* @brief Find what buttons are pressed.
*
* @param cc A pointer to a classic_ctrl_t structure.
* @param msg The message byte specified in the event packet.
*/
static void classic_ctrl_pressed_buttons(struct classic_ctrl_t* cc, short now) {
/* message is inverted (0 is active, 1 is inactive) */
now = ~now & CLASSIC_CTRL_BUTTON_ALL;
/* buttons pressed now */
cc->btns = now;
}
/**
* @brief Calculate the angle and magnitude of a joystick.
*
* @param js [out] Pointer to a joystick_t structure.
* @param x The raw x-axis value.
* @param y The raw y-axis value.
*/
void calc_joystick_state(struct joystick_t* js, float x, float y) {
float rx, ry, ang;
/*
* Since the joystick center may not be exactly:
* (min + max) / 2
* Then the range from the min to the center and the center to the max
* may be different.
* Because of this, depending on if the current x or y value is greater
* or less than the assoicated axis center value, it needs to be interpolated
* between the center and the minimum or maxmimum rather than between
* the minimum and maximum.
*
* So we have something like this:
* (x min) [-1] ---------*------ [0] (x center) [0] -------- [1] (x max)
* Where the * is the current x value.
* The range is therefore -1 to 1, 0 being the exact center rather than
* the middle of min and max.
*/
if (x == js->center.x)
rx = 0;
else if (x >= js->center.x)
rx = ((float)(x - js->center.x) / (float)(js->max.x - js->center.x));
else
rx = ((float)(x - js->min.x) / (float)(js->center.x - js->min.x)) - 1.0f;
if (y == js->center.y)
ry = 0;
else if (y >= js->center.y)
ry = ((float)(y - js->center.y) / (float)(js->max.y - js->center.y));
else
ry = ((float)(y - js->min.y) / (float)(js->center.y - js->min.y)) - 1.0f;
/* calculate the joystick angle and magnitude */
ang = RAD_TO_DEGREE(atanf(ry / rx));
ang -= 90.0f;
if (rx < 0.0f)
ang -= 180.0f;
js->ang = absf(ang);
js->mag = (float) sqrt((rx * rx) + (ry * ry));
js->rx = rx;
js->ry = ry;
}

View File

@ -0,0 +1,292 @@
/*
* This file is part of iMAME4all.
*
* Copyright (C) 2010 David Valdeita (Seleuco)
*
* based on:
*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* In addition, as a special exception, Seleuco
* gives permission to link the code of this program with
* the MAME library (or with modified versions of MAME that use the
* same license as MAME), and distribute linked combinations including
* the two. You must obey the GNU General Public License in all
* respects for all of the code used other than MAME. If you modify
* this file, you may extend this exception to your version of the
* file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*/
#ifndef __WIIMOTE_H__
#define __WIIMOTE_H__
#include "btstack/utils.h"
#if defined(__cplusplus)
extern "C" {
#endif
typedef unsigned char byte;
typedef char sbyte;
#define WIIMOTE_PI 3.14159265
#define WIIMOTE_DBG 0
/* Convert between radians and degrees */
#define RAD_TO_DEGREE(r) ((r * 180.0f) / WIIMOTE_PI)
#define DEGREE_TO_RAD(d) (d * (WIIMOTE_PI / 180.0f))
/* Convert to big endian */
#define BIG_ENDIAN_LONG(i) (htonl(i))
#define BIG_ENDIAN_SHORT(i) (htons(i))
#define absf(x) ((x >= 0) ? (x) : (x * -1.0f))
#define diff_f(x, y) ((x >= y) ? (absf(x - y)) : (absf(y - x)))
/* wiimote state flags*/
#define WIIMOTE_STATE_DEV_FOUND 0x0001
#define WIIMOTE_STATE_HANDSHAKE 0x0002 /* actual connection exists but no handshake yet */
#define WIIMOTE_STATE_HANDSHAKE_COMPLETE 0x0004
#define WIIMOTE_STATE_CONNECTED 0x0008
#define WIIMOTE_STATE_EXP 0x0040
/* Communication channels */
#define WM_OUTPUT_CHANNEL 0x11
#define WM_INPUT_CHANNEL 0x13
#define WM_SET_REPORT 0x50
/* commands */
#define WM_CMD_LED 0x11
#define WM_CMD_REPORT_TYPE 0x12
#define WM_CMD_RUMBLE 0x13
#define WM_CMD_IR 0x13
#define WM_CMD_CTRL_STATUS 0x15
#define WM_CMD_WRITE_DATA 0x16
#define WM_CMD_READ_DATA 0x17
#define WM_CMD_IR_2 0x1A
/* input report ids */
#define WM_RPT_CTRL_STATUS 0x20
#define WM_RPT_READ 0x21
#define WM_RPT_WRITE 0x22
#define WM_RPT_BTN 0x30
#define WM_RPT_BTN_ACC 0x31
#define WM_RPT_BTN_ACC_IR 0x33
#define WM_RPT_BTN_EXP 0x34
#define WM_RPT_BTN_ACC_EXP 0x35
#define WM_RPT_BTN_IR_EXP 0x36
#define WM_RPT_BTN_ACC_IR_EXP 0x37
#define WM_BT_INPUT 0x01
#define WM_BT_OUTPUT 0x02
/* controller status stuff */
#define WM_MAX_BATTERY_CODE 0xC8
#define EXP_ID_CODE_CLASSIC_CONTROLLER 0x9A1EFDFD
/* offsets in wiimote memory */
#define WM_MEM_OFFSET_CALIBRATION 0x16
#define WM_EXP_MEM_BASE 0x04A40000
#define WM_EXP_MEM_ENABLE 0x04A40040
#define WM_EXP_MEM_CALIBR 0x04A40020
#define EXP_HANDSHAKE_LEN 224
/* controller status flags for the first message byte */
/* bit 1 is unknown */
#define WM_CTRL_STATUS_BYTE1_ATTACHMENT 0x02
#define WM_CTRL_STATUS_BYTE1_SPEAKER_ENABLED 0x04
#define WM_CTRL_STATUS_BYTE1_IR_ENABLED 0x08
#define WM_CTRL_STATUS_BYTE1_LED_1 0x10
#define WM_CTRL_STATUS_BYTE1_LED_2 0x20
#define WM_CTRL_STATUS_BYTE1_LED_3 0x40
#define WM_CTRL_STATUS_BYTE1_LED_4 0x80
/* led bit masks */
#define WIIMOTE_LED_NONE 0x00
#define WIIMOTE_LED_1 0x10
#define WIIMOTE_LED_2 0x20
#define WIIMOTE_LED_3 0x40
#define WIIMOTE_LED_4 0x80
/* button codes */
#define WIIMOTE_BUTTON_TWO 0x0001
#define WIIMOTE_BUTTON_ONE 0x0002
#define WIIMOTE_BUTTON_B 0x0004
#define WIIMOTE_BUTTON_A 0x0008
#define WIIMOTE_BUTTON_MINUS 0x0010
#define WIIMOTE_BUTTON_ZACCEL_BIT6 0x0020
#define WIIMOTE_BUTTON_ZACCEL_BIT7 0x0040
#define WIIMOTE_BUTTON_HOME 0x0080
#define WIIMOTE_BUTTON_LEFT 0x0100
#define WIIMOTE_BUTTON_RIGHT 0x0200
#define WIIMOTE_BUTTON_DOWN 0x0400
#define WIIMOTE_BUTTON_UP 0x0800
#define WIIMOTE_BUTTON_PLUS 0x1000
#define WIIMOTE_BUTTON_ZACCEL_BIT4 0x2000
#define WIIMOTE_BUTTON_ZACCEL_BIT5 0x4000
#define WIIMOTE_BUTTON_UNKNOWN 0x8000
#define WIIMOTE_BUTTON_ALL 0x1F9F
/* classic controller button codes */
#define CLASSIC_CTRL_BUTTON_UP 0x0001
#define CLASSIC_CTRL_BUTTON_LEFT 0x0002
#define CLASSIC_CTRL_BUTTON_ZR 0x0004
#define CLASSIC_CTRL_BUTTON_X 0x0008
#define CLASSIC_CTRL_BUTTON_A 0x0010
#define CLASSIC_CTRL_BUTTON_Y 0x0020
#define CLASSIC_CTRL_BUTTON_B 0x0040
#define CLASSIC_CTRL_BUTTON_ZL 0x0080
#define CLASSIC_CTRL_BUTTON_FULL_R 0x0200
#define CLASSIC_CTRL_BUTTON_PLUS 0x0400
#define CLASSIC_CTRL_BUTTON_HOME 0x0800
#define CLASSIC_CTRL_BUTTON_MINUS 0x1000
#define CLASSIC_CTRL_BUTTON_FULL_L 0x2000
#define CLASSIC_CTRL_BUTTON_DOWN 0x4000
#define CLASSIC_CTRL_BUTTON_RIGHT 0x8000
#define CLASSIC_CTRL_BUTTON_ALL 0xFEFF
/* expansion codes */
#define EXP_NONE 0
#define EXP_CLASSIC 2
/**
* @struct vec2b_t
* @brief Unsigned x,y byte vector.
*/
typedef struct vec2b_t {
byte x, y;
} vec2b_t;
/**
* @struct joystick_t
* @brief Joystick calibration structure.
*
* The angle \a ang is relative to the positive y-axis into quadrant I
* and ranges from 0 to 360 degrees. So if the joystick is held straight
* upwards then angle is 0 degrees. If it is held to the right it is 90,
* down is 180, and left is 270.
*
* The magnitude \a mag is the distance from the center to where the
* joystick is being held. The magnitude ranges from 0 to 1.
* If the joystick is only slightly tilted from the center the magnitude
* will be low, but if it is closer to the outter edge the value will
* be higher.
*/
typedef struct joystick_t {
struct vec2b_t max; /**< maximum joystick values */
struct vec2b_t min; /**< minimum joystick values */
struct vec2b_t center; /**< center joystick values */
float ang; /**< angle the joystick is being held */
float mag; /**< magnitude of the joystick (range 0-1) */
float rx, ry;
} joystick_t;
/**
* @struct classic_ctrl_t
* @brief Classic controller expansion device.
*/
typedef struct classic_ctrl_t {
short btns; /**< what buttons have just been pressed */
float r_shoulder; /**< right shoulder button (range 0-1) */
float l_shoulder; /**< left shoulder button (range 0-1) */
struct joystick_t ljs; /**< left joystick calibration */
struct joystick_t rjs; /**< right joystick calibration */
} classic_ctrl_t;
/**
* @struct expansion_t
* @brief Generic expansion device plugged into wiimote.
*/
typedef struct expansion_t {
int type; /**< type of expansion attached */
union {
struct classic_ctrl_t classic;
};
} expansion_t;
/**
* @struct wiimote_t
* @brief Wiimote structure.
*/
typedef struct wiimote_t {
int unid; /**< user specified id */
uint16_t wiiMoteConHandle;
uint16_t i_source_cid;
uint16_t c_source_cid;
bd_addr_t addr;
int state; /**< various state flags */
byte leds; /**< currently lit leds */
float battery_level; /**< battery level */
byte handshake_state; /**< the state of the connection handshake */
struct expansion_t exp; /**< wiimote expansion device */
unsigned short btns; /**< what buttons have just been pressed */
} wiimote;
/**
* @brief Check if a button is pressed.
* @param dev Pointer to a wiimote_t or expansion structure.
* @param button The button you are interested in.
* @return 1 if the button is pressed, 0 if not.
*/
#define IS_PRESSED(dev, button) ((dev->btns & button) == button)
/* macro to manage states */
#define WIIMOTE_IS_SET(wm, s) ((wm->state & (s)) == (s))
#define WIIMOTE_ENABLE_STATE(wm, s) (wm->state |= (s))
#define WIIMOTE_DISABLE_STATE(wm, s) (wm->state &= ~(s))
#define WIIMOTE_TOGGLE_STATE(wm, s) ((wm->state & (s)) ? WIIMOTE_DISABLE_STATE(wm, s) : WIIMOTE_ENABLE_STATE(wm, s))
#define WIIMOTE_IS_CONNECTED(wm) (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_CONNECTED))
extern struct wiimote_t joys[4];
extern int myosd_num_of_joys;
int wiimote_remove(uint16_t source_cid, bd_addr_t *addr);
struct wiimote_t* wiimote_get_by_source_cid(uint16_t source_cid);
int wiimote_handshake(struct wiimote_t* wm, byte event, byte* data, unsigned short len);
void wiimote_status(struct wiimote_t* wm);
void wiimote_data_report(struct wiimote_t* wm, byte type);
void wiimote_pressed_buttons(struct wiimote_t* wm, byte* msg);
void wiimote_handle_expansion(struct wiimote_t* wm, byte* msg);
#if defined(__cplusplus)
}
#endif
#endif

View File

@ -0,0 +1,31 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
// Input responder
#define MAX_TOUCHES 16
#define MAX_KEYS 256
typedef struct touch_data
{
int16_t screen_x, screen_y;
int16_t fixed_x, fixed_y;
int16_t full_x, full_y;
} touch_data_t;
@interface RAInputResponder : NSObject
- (void)poll;
- (bool)isKeyPressed:(unsigned)index;
- (const touch_data_t*)getTouchDataAtIndex:(unsigned)index;
@end

View File

@ -0,0 +1,97 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#import "RAInputResponder.h"
#include "input/input_common.h"
extern NSString* const GSEventKeyDownNotification;
extern NSString* const GSEventKeyUpNotification;
extern NSString* const RATouchNotification;
@implementation RAInputResponder
{
unsigned _touchCount;
touch_data_t _touches[MAX_TOUCHES];
bool _keys[MAX_KEYS];
}
-(id)init
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyPressed:) name: GSEventKeyDownNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyReleased:) name: GSEventKeyUpNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleTouches:) name: RATouchNotification object:nil];
return self;
}
-(void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)poll
{
for (int i = 0; i != _touchCount; i ++)
{
input_translate_coord_viewport(_touches[i].screen_x, _touches[i].screen_y,
&_touches[i].fixed_x, &_touches[i].fixed_y,
&_touches[i].full_x, &_touches[i].full_y);
}
}
- (bool)isKeyPressed:(unsigned)index
{
return (index < MAX_KEYS) ? _keys[index] : NO;
}
- (const touch_data_t*)getTouchDataAtIndex:(unsigned)index
{
return (index < _touchCount) ? &_touches[index] : 0;
}
// Response handlers
- (void)keyPressed:(NSNotification*)notification
{
int keycode = [[notification.userInfo objectForKey:@"keycode"] intValue];
if (keycode < MAX_KEYS) _keys[keycode] = true;
}
- (void)keyReleased:(NSNotification*)notification
{
int keycode = [[notification.userInfo objectForKey:@"keycode"] intValue];
if (keycode < MAX_KEYS) _keys[keycode] = false;
}
- (void)handleTouches:(NSNotification*)notification
{
UIEvent* event = [notification.userInfo objectForKey:@"event"];
NSArray* touches = [[event allTouches] allObjects];
const int numTouches = [touches count];
_touchCount = 0;
for(int i = 0; i != numTouches && _touchCount != MAX_TOUCHES; i ++)
{
UITouch *touch = [touches objectAtIndex:i];
CGPoint coord = [touch locationInView:touch.view];
float scale = [[UIScreen mainScreen] scale];
if (touch.phase != UITouchPhaseEnded && touch.phase != UITouchPhaseCancelled)
{
_touches[_touchCount ].screen_x = coord.x * scale;
_touches[_touchCount ++].screen_y = coord.y * scale;
}
}
}
@end

View File

@ -0,0 +1,201 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#import "RAInputResponder.h"
#include <unistd.h>
#include "input/input_common.h"
#include "general.h"
#include "driver.h"
#ifdef WIIMOTE
extern const rarch_joypad_driver_t ios_joypad;
static const rarch_joypad_driver_t* const g_joydriver = &ios_joypad;
#else
static const rarch_joypad_driver_t* const g_joydriver = 0;
#endif
static const struct rarch_key_map rarch_key_map_hidusage[];
static RAInputResponder* g_input_driver;
// Non-exported helpers
static bool ios_key_pressed(enum retro_key key)
{
if ((int)key >= 0 && key < RETROK_LAST)
{
return [g_input_driver isKeyPressed:input_translate_rk_to_keysym(key)];
}
return false;
}
static bool ios_is_pressed(unsigned port_num, const struct retro_keybind *key)
{
return ios_key_pressed(key->key) || input_joypad_pressed(g_joydriver, port_num, key);
}
// Exported input driver
static void *ios_input_init(void)
{
input_init_keyboard_lut(rarch_key_map_hidusage);
g_input_driver = [RAInputResponder new];
return (void*)-1;
}
static void ios_input_poll(void *data)
{
[g_input_driver poll];
input_joypad_poll(g_joydriver);
}
static int16_t ios_input_state(void *data, const struct retro_keybind **binds, unsigned port, unsigned device, unsigned index, unsigned id)
{
switch (device)
{
case RETRO_DEVICE_JOYPAD:
return (id < RARCH_BIND_LIST_END) ? ios_is_pressed(port, &binds[port][id]) : false;
case RARCH_DEVICE_POINTER_SCREEN:
{
const touch_data_t* touch = [g_input_driver getTouchDataAtIndex:index];
switch (id)
{
case RETRO_DEVICE_ID_POINTER_X: return touch ? touch->full_x : 0;
case RETRO_DEVICE_ID_POINTER_Y: return touch ? touch->full_y : 0;
case RETRO_DEVICE_ID_POINTER_PRESSED: return touch ? 1 : 0;
}
return 0;
}
default:
return 0;
}
}
static bool ios_bind_button_pressed(void *data, int key)
{
const struct retro_keybind *binds = g_settings.input.binds[0];
return (key >= 0 && key < RARCH_BIND_LIST_END) ? ios_is_pressed(0, &binds[key]) : false;
}
static void ios_input_free_input(void *data)
{
(void)data;
g_input_driver = nil;
}
const input_driver_t input_ios = {
ios_input_init,
ios_input_poll,
ios_input_state,
ios_bind_button_pressed,
ios_input_free_input,
"ios_input",
};
// Key table
#include "keycode.h"
static const struct rarch_key_map rarch_key_map_hidusage[] = {
{ KEY_Left, RETROK_LEFT },
{ KEY_Right, RETROK_RIGHT },
{ KEY_Up, RETROK_UP },
{ KEY_Down, RETROK_DOWN },
{ KEY_Enter, RETROK_RETURN },
{ KEY_Tab, RETROK_TAB },
{ KEY_Insert, RETROK_INSERT },
{ KEY_Delete, RETROK_DELETE },
{ KEY_RightShift, RETROK_RSHIFT },
{ KEY_LeftShift, RETROK_LSHIFT },
{ KEY_RightControl, RETROK_RCTRL },
{ KEY_LeftControl, RETROK_LCTRL },
{ KEY_End, RETROK_END },
{ KEY_Home, RETROK_HOME },
{ KEY_PageDown, RETROK_PAGEDOWN },
{ KEY_PageUp, RETROK_PAGEUP },
{ KEY_RightAlt, RETROK_RALT },
{ KEY_LeftAlt, RETROK_LALT },
{ KEY_Space, RETROK_SPACE },
{ KEY_Escape, RETROK_ESCAPE },
{ KEY_Delete, RETROK_BACKSPACE },
{ KP_Enter, RETROK_KP_ENTER },
{ KP_Add, RETROK_KP_PLUS },
{ KP_Subtract, RETROK_KP_MINUS },
{ KP_Multiply, RETROK_KP_MULTIPLY },
{ KP_Divide, RETROK_KP_DIVIDE },
{ KEY_Grave, RETROK_BACKQUOTE },
{ KEY_Pause, RETROK_PAUSE },
{ KP_0, RETROK_KP0 },
{ KP_1, RETROK_KP1 },
{ KP_2, RETROK_KP2 },
{ KP_3, RETROK_KP3 },
{ KP_4, RETROK_KP4 },
{ KP_5, RETROK_KP5 },
{ KP_6, RETROK_KP6 },
{ KP_7, RETROK_KP7 },
{ KP_8, RETROK_KP8 },
{ KP_9, RETROK_KP9 },
{ KEY_0, RETROK_0 },
{ KEY_1, RETROK_1 },
{ KEY_2, RETROK_2 },
{ KEY_3, RETROK_3 },
{ KEY_4, RETROK_4 },
{ KEY_5, RETROK_5 },
{ KEY_6, RETROK_6 },
{ KEY_7, RETROK_7 },
{ KEY_8, RETROK_8 },
{ KEY_9, RETROK_9 },
{ KEY_F1, RETROK_F1 },
{ KEY_F2, RETROK_F2 },
{ KEY_F3, RETROK_F3 },
{ KEY_F4, RETROK_F4 },
{ KEY_F5, RETROK_F5 },
{ KEY_F6, RETROK_F6 },
{ KEY_F7, RETROK_F7 },
{ KEY_F8, RETROK_F8 },
{ KEY_F9, RETROK_F9 },
{ KEY_F10, RETROK_F10 },
{ KEY_F11, RETROK_F11 },
{ KEY_F12, RETROK_F12 },
{ KEY_A, RETROK_a },
{ KEY_B, RETROK_b },
{ KEY_C, RETROK_c },
{ KEY_D, RETROK_d },
{ KEY_E, RETROK_e },
{ KEY_F, RETROK_f },
{ KEY_G, RETROK_g },
{ KEY_H, RETROK_h },
{ KEY_I, RETROK_i },
{ KEY_J, RETROK_j },
{ KEY_K, RETROK_k },
{ KEY_L, RETROK_l },
{ KEY_M, RETROK_m },
{ KEY_N, RETROK_n },
{ KEY_O, RETROK_o },
{ KEY_P, RETROK_p },
{ KEY_Q, RETROK_q },
{ KEY_R, RETROK_r },
{ KEY_S, RETROK_s },
{ KEY_T, RETROK_t },
{ KEY_U, RETROK_u },
{ KEY_V, RETROK_v },
{ KEY_W, RETROK_w },
{ KEY_X, RETROK_x },
{ KEY_Y, RETROK_y },
{ KEY_Z, RETROK_z },
{ 0, RETROK_UNKNOWN }
};

View File

@ -0,0 +1,75 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "input/input_common.h"
#include "BTStack/wiimote.h"
#include "general.h"
static uint32_t g_buttons[MAX_PLAYERS];
static bool ios_joypad_init(void)
{
return true;
}
static bool ios_joypad_query_pad(unsigned pad)
{
return pad < MAX_PLAYERS;
}
static void ios_joypad_destroy(void)
{
}
static bool ios_joypad_button(unsigned port, uint16_t joykey)
{
if (joykey == NO_BTN)
return false;
// Check hat.
if (GET_HAT_DIR(joykey))
return false;
else // Check the button
return (port < MAX_PLAYERS && joykey < 32) ? (g_buttons[port] & (1 << joykey)) != 0 : false;
}
static int16_t ios_joypad_axis(unsigned port, uint32_t joyaxis)
{
return 0;
}
static void ios_joypad_poll(void)
{
for (int i = 0; i != MAX_PLAYERS; i ++)
{
g_buttons[i] = 0;
if (i < myosd_num_of_joys)
{
g_buttons[i] = joys[i].btns;
g_buttons[i] |= (joys[i].exp.type == EXP_CLASSIC) ? (joys[i].exp.classic.btns << 16) : 0;
}
}
}
const rarch_joypad_driver_t ios_joypad = {
ios_joypad_init,
ios_joypad_query_pad,
ios_joypad_destroy,
ios_joypad_button,
ios_joypad_axis,
ios_joypad_poll,
"ios",
};

View File

@ -0,0 +1,156 @@
/*
Taken from https://github.com/depp/keycode, distributed with the following license:
Copyright 2011-2012 Dietrich Epp <depp@zdome.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* This file is automatically generated by keycode.py. */
#ifndef KEYCODE_KEYCODE_H
#define KEYCODE_KEYCODE_H
enum {
KEY_A = 4,
KEY_B = 5,
KEY_C = 6,
KEY_D = 7,
KEY_E = 8,
KEY_F = 9,
KEY_G = 10,
KEY_H = 11,
KEY_I = 12,
KEY_J = 13,
KEY_K = 14,
KEY_L = 15,
KEY_M = 16,
KEY_N = 17,
KEY_O = 18,
KEY_P = 19,
KEY_Q = 20,
KEY_R = 21,
KEY_S = 22,
KEY_T = 23,
KEY_U = 24,
KEY_V = 25,
KEY_W = 26,
KEY_X = 27,
KEY_Y = 28,
KEY_Z = 29,
KEY_1 = 30,
KEY_2 = 31,
KEY_3 = 32,
KEY_4 = 33,
KEY_5 = 34,
KEY_6 = 35,
KEY_7 = 36,
KEY_8 = 37,
KEY_9 = 38,
KEY_0 = 39,
KEY_Enter = 40,
KEY_Escape = 41,
KEY_Delete = 42,
KEY_Tab = 43,
KEY_Space = 44,
KEY_Minus = 45,
KEY_Equals = 46,
KEY_LeftBracket = 47,
KEY_RightBracket = 48,
KEY_Backslash = 49,
KEY_Semicolon = 51,
KEY_Quote = 52,
KEY_Grave = 53,
KEY_Comma = 54,
KEY_Period = 55,
KEY_Slash = 56,
KEY_CapsLock = 57,
KEY_F1 = 58,
KEY_F2 = 59,
KEY_F3 = 60,
KEY_F4 = 61,
KEY_F5 = 62,
KEY_F6 = 63,
KEY_F7 = 64,
KEY_F8 = 65,
KEY_F9 = 66,
KEY_F10 = 67,
KEY_F11 = 68,
KEY_F12 = 69,
KEY_PrintScreen = 70,
KEY_ScrollLock = 71,
KEY_Pause = 72,
KEY_Insert = 73,
KEY_Home = 74,
KEY_PageUp = 75,
KEY_DeleteForward = 76,
KEY_End = 77,
KEY_PageDown = 78,
KEY_Right = 79,
KEY_Left = 80,
KEY_Down = 81,
KEY_Up = 82,
KP_NumLock = 83,
KP_Divide = 84,
KP_Multiply = 85,
KP_Subtract = 86,
KP_Add = 87,
KP_Enter = 88,
KP_1 = 89,
KP_2 = 90,
KP_3 = 91,
KP_4 = 92,
KP_5 = 93,
KP_6 = 94,
KP_7 = 95,
KP_8 = 96,
KP_9 = 97,
KP_0 = 98,
KP_Point = 99,
KEY_NonUSBackslash = 100,
KP_Equals = 103,
KEY_F13 = 104,
KEY_F14 = 105,
KEY_F15 = 106,
KEY_F16 = 107,
KEY_F17 = 108,
KEY_F18 = 109,
KEY_F19 = 110,
KEY_F20 = 111,
KEY_F21 = 112,
KEY_F22 = 113,
KEY_F23 = 114,
KEY_F24 = 115,
KEY_Help = 117,
KEY_Menu = 118,
KEY_LeftControl = 224,
KEY_LeftShift = 225,
KEY_LeftAlt = 226,
KEY_LeftGUI = 227,
KEY_RightControl = 228,
KEY_RightShift = 229,
KEY_RightAlt = 230,
KEY_RightGUI = 231
};
#endif

77
ios/RetroArch/main.m Normal file
View File

@ -0,0 +1,77 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#import <UIKit/UIKit.h>
#define GSEVENT_TYPE 2
#define GSEVENT_FLAGS 12
#define GSEVENTKEY_KEYCODE 15
#define GSEVENT_TYPE_KEYDOWN 10
#define GSEVENT_TYPE_KEYUP 11
NSString *const GSEventKeyDownNotification = @"GSEventKeyDownHackNotification";
NSString *const GSEventKeyUpNotification = @"GSEventKeyUpHackNotification";
NSString *const RATouchNotification = @"RATouchNotification";
@interface RApplication : UIApplication
@end
@implementation RApplication
- (void)sendEvent:(UIEvent *)event
{
[super sendEvent:event];
if ([[event allTouches] count])
{
NSDictionary* inf = [[NSDictionary alloc] initWithObjectsAndKeys:
event, @"event", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:RATouchNotification object:nil userInfo:inf];
}
// Stolen from: http://nacho4d-nacho4d.blogspot.com/2012/01/catching-keyboard-events-in-ios.html
else if ([event respondsToSelector:@selector(_gsEvent)])
{
int* eventMem = (int *)(void*)CFBridgingRetain([event performSelector:@selector(_gsEvent)]);
int eventType = eventMem ? eventMem[GSEVENT_TYPE] : 0;
if (eventMem && (eventType == GSEVENT_TYPE_KEYDOWN || eventType == GSEVENT_TYPE_KEYUP))
{
// Read keycode from GSEventKey
int tmp = eventMem[GSEVENTKEY_KEYCODE];
UniChar *keycode = (UniChar *)&tmp;
// Post notification
NSDictionary *inf = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithShort:keycode[0]], @"keycode",
nil];
[[NSNotificationCenter defaultCenter]
postNotificationName:(eventType == GSEVENT_TYPE_KEYDOWN) ? GSEventKeyDownNotification : GSEventKeyUpNotification
object:nil userInfo:inf];
}
CFBridgingRelease(eventMem);
}
}
@end
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, NSStringFromClass([RApplication class]), NSStringFromClass([RetroArch_iOS class]));
}
}

View File

@ -0,0 +1,28 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __IOS_RARCH_WRAPPER_H__
#define __IOS_RARCH_WRAPPER_H__
// These functions must only be called in gfx/context/ioseagl_ctx.c
bool ios_init_game_view();
void ios_destroy_game_view();
void ios_flip_game_view();
void ios_set_game_view_sync(bool on);
void ios_get_game_view_size(unsigned *width, unsigned *height);
void ios_bind_game_view_fbo();
#endif

View File

@ -0,0 +1,177 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#import "settings.h"
#include "../input/keycode.h"
#ifdef WIIMOTE
# include "../input/BTStack/wiimote.h"
#endif
extern NSString* const GSEventKeyUpNotification;
static const struct
{
const char* const keyname;
const uint32_t hid_id;
} ios_key_name_map[] = {
{ "left", KEY_Left }, { "right", KEY_Right },
{ "up", KEY_Up }, { "down", KEY_Down },
{ "enter", KEY_Enter }, { "kp_enter", KP_Enter },
{ "space", KEY_Space }, { "tab", KEY_Tab },
{ "shift", KEY_LeftShift }, { "rshift", KEY_RightShift },
{ "ctrl", KEY_LeftControl }, { "alt", KEY_LeftAlt },
{ "escape", KEY_Escape }, { "backspace", KEY_DeleteForward },
{ "backquote", KEY_Grave }, { "pause", KEY_Pause },
{ "f1", KEY_F1 }, { "f2", KEY_F2 },
{ "f3", KEY_F3 }, { "f4", KEY_F4 },
{ "f5", KEY_F5 }, { "f6", KEY_F6 },
{ "f7", KEY_F7 }, { "f8", KEY_F8 },
{ "f9", KEY_F9 }, { "f10", KEY_F10 },
{ "f11", KEY_F11 }, { "f12", KEY_F12 },
{ "num0", KEY_0 }, { "num1", KEY_1 },
{ "num2", KEY_2 }, { "num3", KEY_3 },
{ "num4", KEY_4 }, { "num5", KEY_5 },
{ "num6", KEY_6 }, { "num7", KEY_7 },
{ "num8", KEY_8 }, { "num9", KEY_9 },
{ "insert", KEY_Insert }, { "del", KEY_DeleteForward },
{ "home", KEY_Home }, { "end", KEY_End },
{ "pageup", KEY_PageUp }, { "pagedown", KEY_PageDown },
{ "add", KP_Add }, { "subtract", KP_Subtract },
{ "multiply", KP_Multiply }, { "divide", KP_Divide },
{ "keypad0", KP_0 }, { "keypad1", KP_1 },
{ "keypad2", KP_2 }, { "keypad3", KP_3 },
{ "keypad4", KP_4 }, { "keypad5", KP_5 },
{ "keypad6", KP_6 }, { "keypad7", KP_7 },
{ "keypad8", KP_8 }, { "keypad9", KP_9 },
{ "period", KEY_Period }, { "capslock", KEY_CapsLock },
{ "numlock", KP_NumLock }, { "print", KEY_PrintScreen },
{ "scroll_lock", KEY_ScrollLock },
{ "a", KEY_A }, { "b", KEY_B }, { "c", KEY_C }, { "d", KEY_D },
{ "e", KEY_E }, { "f", KEY_F }, { "g", KEY_G }, { "h", KEY_H },
{ "i", KEY_I }, { "j", KEY_J }, { "k", KEY_K }, { "l", KEY_L },
{ "m", KEY_M }, { "n", KEY_N }, { "o", KEY_O }, { "p", KEY_P },
{ "q", KEY_Q }, { "r", KEY_R }, { "s", KEY_S }, { "t", KEY_T },
{ "u", KEY_U }, { "v", KEY_V }, { "w", KEY_W }, { "x", KEY_X },
{ "y", KEY_Y }, { "z", KEY_Z },
{ "nul", 0x00},
};
static NSString* get_key_config_name(uint32_t hid_id)
{
for (int i = 0; ios_key_name_map[i].hid_id; i ++)
if (hid_id == ios_key_name_map[i].hid_id)
return [NSString stringWithUTF8String:ios_key_name_map[i].keyname];
return @"nul";
}
@implementation RAButtonGetter
{
RAButtonGetter* _me;
RASettingData* _value;
UIAlertView* _alert;
UITableView* _view;
bool _finished;
#ifdef WIIMOTE
NSTimer* _btTimer;
#endif
}
- (id)initWithSetting:(RASettingData*)setting fromTable:(UITableView*)table
{
self = [super init];
_value = setting;
_view = table;
_me = self;
_alert = [[UIAlertView alloc] initWithTitle:@"RetroArch"
message:_value.label
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:nil];
[_alert show];
[[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(keyReleased:) name: GSEventKeyUpNotification object: nil];
#ifdef WIIMOTE
_btTimer = [NSTimer scheduledTimerWithTimeInterval:.05f target:self selector:@selector(checkWiiMote) userInfo:nil repeats:YES];
#endif
return self;
}
- (void)finish
{
if (!_finished)
{
_finished = true;
#ifdef WIIMOTE
[_btTimer invalidate];
#endif
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_alert dismissWithClickedButtonIndex:0 animated:YES];
[_view reloadData];
_me = nil;
}
}
- (void)alertView:(UIAlertView*)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex
{
[self finish];
}
#ifdef WIIMOTE
- (void)checkWiiMote
{
for (int i = 0; i != myosd_num_of_joys; i ++)
{
uint32_t buttons = joys[i].btns;
buttons |= (joys[i].exp.type == EXP_CLASSIC) ? (joys[i].exp.classic.btns << 16) : 0;
for (int j = 0; j != sizeof(buttons) * 8; j ++)
{
if (buttons & (1 << j))
{
_value.msubValues[1] = [NSString stringWithFormat:@"%d", j];
[self finish];
return;
}
}
}
}
#endif
- (void)keyReleased:(NSNotification*) notification
{
int keycode = [[notification.userInfo objectForKey:@"keycode"] intValue];
_value.msubValues[0] = get_key_config_name(keycode);
[self finish];
}
@end

View File

@ -0,0 +1,64 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#import "settings.h"
@implementation RASettingEnumerationList
{
RASettingData* _value;
UITableView* _view;
};
- (id)initWithSetting:(RASettingData*)setting fromTable:(UITableView*)table
{
self = [super initWithStyle:UITableViewStyleGrouped];
_value = setting;
_view = table;
[self setTitle: _value.label];
return self;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView
{
return 2;
}
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
return (section == 1) ? [_value.subValues count] : 1;
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:@"option"];
cell = cell ? cell : [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"option"];
cell.textLabel.text = (indexPath.section == 1) ? [_value.subValues objectAtIndex:indexPath.row] : @"None";
return cell;
}
- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
_value.value = (indexPath.section == 1) ? [_value.subValues objectAtIndex:indexPath.row] : @"";
[_view reloadData];
[[RetroArch_iOS get] popViewControllerAnimated:YES];
}
@end

View File

@ -0,0 +1,211 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#import <objc/runtime.h>
#import "settings.h"
@implementation RASettingData
- (id)initWithType:(enum SettingTypes)aType label:(NSString*)aLabel name:(NSString*)aName
{
self.type = aType;
self.label = aLabel;
self.name = aName;
return self;
}
@end
static NSString* get_value_from_config(RAConfig* config, NSString* name, NSString* defaultValue)
{
return [config getStringNamed:name withDefault:defaultValue];
}
static RASettingData* boolean_setting(RAConfig* config, NSString* name, NSString* label, NSString* defaultValue)
{
RASettingData* result = [[RASettingData alloc] initWithType:BooleanSetting label:label name:name];
result.value = get_value_from_config(config, name, defaultValue);
return result;
}
static RASettingData* button_setting(RAConfig* config, NSString* name, NSString* label, NSString* defaultValue)
{
RASettingData* result = [[RASettingData alloc] initWithType:ButtonSetting label:label name:name];
result.msubValues = [NSMutableArray arrayWithObjects:
get_value_from_config(config, name, defaultValue),
get_value_from_config(config, [name stringByAppendingString:@"_btn"], @""),
nil];
return result;
}
static RASettingData* group_setting(NSString* label, NSArray* settings)
{
RASettingData* result = [[RASettingData alloc] initWithType:GroupSetting label:label name:nil];
result.subValues = settings;
return result;
}
static RASettingData* enumeration_setting(RAConfig* config, NSString* name, NSString* label, NSString* defaultValue, NSArray* values)
{
RASettingData* result = [[RASettingData alloc] initWithType:EnumerationSetting label:label name:name];
result.value = get_value_from_config(config, name, defaultValue);
result.subValues = values;
return result;
}
static RASettingData* subpath_setting(RAConfig* config, NSString* name, NSString* label, NSString* defaultValue, NSString* path, NSString* extension)
{
NSString* value = get_value_from_config(config, name, defaultValue);
value = [value stringByReplacingOccurrencesOfString:path withString:@""];
NSArray* values = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:path error:nil];
values = [values pathsMatchingExtensions:[NSArray arrayWithObject:extension]];
RASettingData* result = [[RASettingData alloc] initWithType:FileListSetting label:label name:name];
result.value = value;
result.subValues = values;
result.path = path;
return result;
}
static RASettingData* aspect_setting(RAConfig* config, NSString* label)
{
// Why does this need to be so difficult?
RASettingData* result = [[RASettingData alloc] initWithType:AspectSetting label:label name:@"fram"];
result.subValues = [NSArray arrayWithObjects:@"Fill Screen", @"Game Aspect", @"Pixel Aspect", @"4:3", @"16:9", nil];
bool videoForceAspect = [config getBoolNamed:@"video_force_aspect" withDefault:true];
bool videoAspectAuto = [config getBoolNamed:@"video_aspect_ratio_auto" withDefault:false];
double videoAspect = [config getDoubleNamed:@"video_aspect_ratio" withDefault:0.0];
if (!videoForceAspect)
result.value = @"Fill Screen";
else if (videoAspect < 0.0)
result.value = videoAspectAuto ? @"Game Aspect" : @"Pixel Aspect";
else
result.value = (videoAspect < 1.5) ? @"4:3" : @"16:9";
return result;
}
static RASettingData* custom_action(NSString* action)
{
return [[RASettingData alloc] initWithType:CustomAction label:action name:nil];
}
@implementation RASettingsList
+ (void)refreshConfigFile
{
(void)[[RASettingsList alloc] init];
}
- (id)init
{
RAConfig* config = [[RAConfig alloc] initWithPath:[RetroArch_iOS get].moduleInfo.configPath];
NSString* overlay_path = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/overlays/"];
NSString* shader_path = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/shaders/"];
NSArray* settings = [NSArray arrayWithObjects:
[NSArray arrayWithObjects:@"Frontend",
custom_action(@"Module Info"),
#ifdef WIIMOTE
boolean_setting(config, @"ios_auto_bluetooth", @"Auto Enable Bluetooth", @"false"),
#endif
nil],
[NSArray arrayWithObjects:@"Video",
boolean_setting(config, @"video_smooth", @"Smooth Video", @"true"),
boolean_setting(config, @"video_crop_overscan", @"Crop Overscan", @"true"),
boolean_setting(config, @"video_scale_integer", @"Integer Scaling", @"false"),
aspect_setting(config, @"Aspect Ratio"),
subpath_setting(config, @"video_bsnes_shader", @"Shader", @"", shader_path, @"shader"),
nil],
[NSArray arrayWithObjects:@"Audio",
boolean_setting(config, @"audio_enable", @"Enable Output", @"true"),
boolean_setting(config, @"audio_sync", @"Sync on Audio Stream", @"true"),
boolean_setting(config, @"audio_rate_control", @"Adjust for Better Sync", @"true"),
nil],
[NSArray arrayWithObjects:@"Input",
subpath_setting(config, @"input_overlay", @"Input Overlay", @"", overlay_path, @"cfg"),
group_setting(@"Player 1 Keys", [NSArray arrayWithObjects:
[NSArray arrayWithObjects:@"Player 1",
button_setting(config, @"input_player1_up", @"Up", @"up"),
button_setting(config, @"input_player1_down", @"Down", @"down"),
button_setting(config, @"input_player1_left", @"Left", @"left"),
button_setting(config, @"input_player1_right", @"Right", @"right"),
button_setting(config, @"input_player1_start", @"Start", @"enter"),
button_setting(config, @"input_player1_select", @"Select", @"rshift"),
button_setting(config, @"input_player1_b", @"B", @"z"),
button_setting(config, @"input_player1_a", @"A", @"x"),
button_setting(config, @"input_player1_x", @"X", @"s"),
button_setting(config, @"input_player1_y", @"Y", @"a"),
button_setting(config, @"input_player1_l", @"L", @"q"),
button_setting(config, @"input_player1_r", @"R", @"w"),
button_setting(config, @"input_player1_l2", @"L2", @""),
button_setting(config, @"input_player1_r2", @"R2", @""),
button_setting(config, @"input_player1_l3", @"L3", @""),
button_setting(config, @"input_player1_r3", @"R3", @""),
nil],
nil]),
group_setting(@"System Keys", [NSArray arrayWithObjects:
[NSArray arrayWithObjects:@"System Keys",
button_setting(config, @"input_save_state", @"Save State", @"f2"),
button_setting(config, @"input_load_state", @"Load State", @"f4"),
button_setting(config, @"input_state_slot_increase", @"Next State Slot", @"f7"),
button_setting(config, @"input_state_slot_decrease", @"Previous State Slot", @"f6"),
button_setting(config, @"input_toggle_fast_forward", @"Toggle Fast Forward", @"space"),
button_setting(config, @"input_hold_fast_forward", @"Hold Fast Forward", @"l"),
button_setting(config, @"input_rewind", @"Rewind", @"r"),
button_setting(config, @"input_slowmotion", @"Slow Motion", @"e"),
button_setting(config, @"input_reset", @"Reset", @"h"),
button_setting(config, @"input_exit_emulator", @"Close Game", @"escape"),
nil],
nil]),
nil],
[NSArray arrayWithObjects:@"Save States",
boolean_setting(config, @"rewind_enable", @"Enable Rewinding", @"false"),
boolean_setting(config, @"block_sram_overwrite", @"Disable SRAM on Load", @"false"),
boolean_setting(config, @"savestate_auto_save", @"Auto Save on Exit", @"false"),
boolean_setting(config, @"savestate_auto_load", @"Auto Load on Startup", @"true"),
nil],
nil
];
self = [super initWithSettings:settings title:@"RetroArch Settings"];
return self;
}
- (void)dealloc
{
RAConfig* config = [[RAConfig alloc] initWithPath:[RetroArch_iOS get].moduleInfo.configPath];
[config putStringNamed:@"system_directory" value:[RetroArch_iOS get].system_directory];
[self writeSettings:nil toConfig:config];
[config writeToFile:[RetroArch_iOS get].moduleInfo.configPath];
[[RetroArch_iOS get] refreshConfig];
}
- (void)handleCustomAction:(NSString*)action
{
if ([@"Module Info" isEqualToString:action])
[[RetroArch_iOS get] pushViewController:[[RAModuleInfoList alloc] initWithModuleInfo:[RetroArch_iOS get].moduleInfo] animated:YES];
}
@end

View File

@ -0,0 +1,210 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#import <objc/runtime.h>
#import "settings.h"
static const char* const SETTINGID = "SETTING";
@implementation RASettingsSubList
{
NSArray* settings;
};
- (id)initWithSettings:(NSArray*)values title:(NSString*)title
{
self = [super initWithStyle:UITableViewStyleGrouped];
settings = values;
[self setTitle:title];
return self;
}
- (void)handleCustomAction:(NSString*)action
{
}
- (void)writeSettings:(NSArray*)settingList toConfig:(RAConfig*)config
{
NSArray* list = settingList ? settingList : settings;
for (int i = 0; i != [list count]; i ++)
{
NSArray* group = [list objectAtIndex:i];
for (int j = 1; j < [group count]; j ++)
{
RASettingData* setting = [group objectAtIndex:j];
switch (setting.type)
{
case GroupSetting:
[self writeSettings:setting.subValues toConfig:config];
break;
case FileListSetting:
if ([setting.value length] > 0)
[config putStringNamed:setting.name value:[setting.path stringByAppendingPathComponent:setting.value]];
else
[config putStringNamed:setting.name value:@""];
break;
case ButtonSetting:
if (setting.msubValues[0] && [setting.msubValues[0] length])
[config putStringNamed:setting.name value:setting.msubValues[0]];
if (setting.msubValues[1] && [setting.msubValues[1] length])
[config putStringNamed:[setting.name stringByAppendingString:@"_btn"] value:setting.msubValues[1]];
break;
case AspectSetting:
[config putStringNamed:@"video_force_aspect" value:[@"Fill Screen" isEqualToString:setting.value] ? @"false" : @"true"];
[config putStringNamed:@"video_aspect_ratio_auto" value:[@"Game Aspect" isEqualToString:setting.value] ? @"true" : @"false"];
[config putStringNamed:@"video_aspect_ratio" value:@"-1.0"];
if([@"4:3" isEqualToString:setting.value])
[config putStringNamed:@"video_aspect_ratio" value:@"1.33333333"];
else if([@"16:9" isEqualToString:setting.value])
[config putStringNamed:@"video_aspect_ratio" value:@"1.777777777"];
break;
case CustomAction:
break;
default:
[config putStringNamed:setting.name value:setting.value];
break;
}
}
}
}
- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
RASettingData* setting = [[settings objectAtIndex:indexPath.section] objectAtIndex:indexPath.row + 1];
switch (setting.type)
{
case EnumerationSetting:
case FileListSetting:
case AspectSetting:
[[RetroArch_iOS get] pushViewController:[[RASettingEnumerationList alloc] initWithSetting:setting fromTable:(UITableView*)self.view] animated:YES];
break;
case ButtonSetting:
(void)[[RAButtonGetter alloc] initWithSetting:setting fromTable:(UITableView*)self.view];
break;
case GroupSetting:
[[RetroArch_iOS get] pushViewController:[[RASettingsSubList alloc] initWithSettings:setting.subValues title:setting.label] animated:YES];
break;
case CustomAction:
[self handleCustomAction:setting.label];
break;
default:
break;
}
}
- (void)handleBooleanSwitch:(UISwitch*)swt
{
RASettingData* setting = objc_getAssociatedObject(swt, SETTINGID);
setting.value = (swt.on ? @"true" : @"false");
}
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
RASettingData* setting = [[settings objectAtIndex:indexPath.section] objectAtIndex:indexPath.row + 1];
UITableViewCell* cell = nil;
switch (setting.type)
{
case BooleanSetting:
{
cell = [self.tableView dequeueReusableCellWithIdentifier:@"boolean"];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"boolean"];
UISwitch* accessory = [[UISwitch alloc] init];
[accessory addTarget:self action:@selector(handleBooleanSwitch:) forControlEvents:UIControlEventValueChanged];
cell.accessoryView = accessory;
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
}
UISwitch* swt = (UISwitch*)cell.accessoryView;
swt.on = [setting.value isEqualToString:@"true"];
objc_setAssociatedObject(swt, SETTINGID, setting, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
break;
case EnumerationSetting:
case FileListSetting:
case ButtonSetting:
case CustomAction:
case AspectSetting:
{
cell = [self.tableView dequeueReusableCellWithIdentifier:@"enumeration"];
cell = cell ? cell : [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"enumeration"];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
break;
case GroupSetting:
{
cell = [self.tableView dequeueReusableCellWithIdentifier:@"group"];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"group"];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
}
break;
}
cell.textLabel.text = setting.label;
if (setting.type != ButtonSetting)
cell.detailTextLabel.text = setting.value;
else
cell.detailTextLabel.text = [NSString stringWithFormat:@"[KB:%@] [JS:%@]",
[setting.msubValues[0] length] ? setting.msubValues[0] : @"N/A",
[setting.msubValues[1] length] ? setting.msubValues[1] : @"N/A"];
return cell;
}
// UITableView item counts
- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView
{
return [settings count];
}
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
return [[settings objectAtIndex:section] count] -1;
}
- (NSString*)tableView:(UITableView*)tableView titleForHeaderInSection:(NSInteger)section
{
return [[settings objectAtIndex:section] objectAtIndex:0];
}
@end

View File

@ -0,0 +1,52 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
enum SettingTypes
{
BooleanSetting, ButtonSetting, EnumerationSetting, FileListSetting,
GroupSetting, AspectSetting, CustomAction
};
@interface RASettingData : NSObject
@property enum SettingTypes type;
@property (strong) NSString* label;
@property (strong) NSString* name;
@property (strong) NSString* value;
@property (strong) NSString* path;
@property (strong) NSArray* subValues;
@property (strong) NSMutableArray* msubValues;
- (id)initWithType:(enum SettingTypes)aType label:(NSString*)aLabel name:(NSString*)aName;
@end
@interface RAButtonGetter : NSObject<UIAlertViewDelegate>
- (id)initWithSetting:(RASettingData*)setting fromTable:(UITableView*)table;
@end
@interface RASettingEnumerationList : UITableViewController
- (id)initWithSetting:(RASettingData*)setting fromTable:(UITableView*)table;
@end
@interface RASettingsSubList : UITableViewController
- (id)initWithSettings:(NSArray*)values title:(NSString*)title;
- (void)handleCustomAction:(NSString*)action;
- (void)writeSettings:(NSArray*)settingList toConfig:(RAConfig*)config;
@end
@interface RASettingsList : RASettingsSubList
+ (void)refreshConfigFile;
@end

40
ios/RetroArch/views.h Normal file
View File

@ -0,0 +1,40 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2013 - Jason Fetters
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
#import "RAConfig.h"
@interface RAGameView : UIViewController
+ (RAGameView*)get;
- (void)openPauseMenu;
- (void)closePauseMenu;
@end
@interface RAModuleInfo : NSObject
@property (strong) NSString* displayName;
@property (strong) NSString* path;
@property (strong) NSString* configPath;
@property (strong) RAConfig* data;
@property (strong) NSArray* supportedExtensions;
+ (RAModuleInfo*)moduleWithPath:(NSString*)thePath data:(RAConfig*)theData;
- (bool)supportsFileAtPath:(NSString*)path;
@end
@interface RAModuleInfoList : UITableViewController
- (id)initWithModuleInfo:(RAModuleInfo*)info;
@end

View File

@ -0,0 +1,5 @@
display_name = "Nintendo DS"
recommended_extensions = "nds"
emuname = "DeSmuME"
manufacturer = "Nintendo"
systemname = "Nintendo DS"

17
ios/modules/example.info Normal file
View File

@ -0,0 +1,17 @@
## All data is optional, but helps improve user experience.
# Name displayed when the user is selecting an emulator.
# display_name = "NES / Famicom"
# List of supported extensions.
# supported_extensions = "nes|fds"
# Name the emulator core.
# emuname = "Nestopia"
# The developer of the emulated device.
# manufacturer = "Nintendo"
# The name of the emulated device.
# systemname = "Nintendo Entertainment System"

View File

@ -0,0 +1,5 @@
display_name = "Game Boy / Game Boy Color"
supported_extensions = "gb|gbc|dmg"
emuname = "gambatte"
manufacturer = "Nintendo"
systemname = "Game Boy Color"

View File

@ -0,0 +1,6 @@
display_name = "Sega (MS/GG/MD/CD)"
supported_extensions = "md|smd|gen|sms|gg|sg|bin|cue|ios"
emuname = "Genesis Plus GX"
manufacturer = "Sega"
systemname = "Sega (Various)"

View File

@ -0,0 +1,6 @@
display_name = "Neo Geo Pocket (Color)"
supported_extensions = "ngp|ngc"
emuname = "Mednafen Neopop"
manufacturer = "SNK"
systemname = "Neo Geo Pocket (Color)"

View File

@ -0,0 +1,6 @@
display_name = "PC Engine/TurboGrafx-16"
supported_extensions = "pce|sgx|cue"
emuname = "Mednafen PCE Fast"
manufacturer = "NEC"
systemname = "PC Engine/TurboGrafx-16"

View File

@ -0,0 +1,6 @@
display_name = "PlayStation"
supported_extensions = "cue|toc"
emuname = "Mednafen PSX"
manufacturer = "Sony"
systemname = "PlayStation"

View File

@ -0,0 +1,6 @@
display_name = "Virtual Boy"
supported_extensions = "vb|vboy|bin"
emuname = "Mednafen VB"
manufacturer = "Nintendo"
systemname = "Virtual Boy"

View File

@ -0,0 +1,6 @@
display_name = "WonderSwan (Color)"
supported_extensions = "ws|wsc"
emuname = "Mednafen WonderSwan"
manufacturer = "Bandai"
systemname = "WonderSwan (Color)"

View File

@ -0,0 +1,6 @@
display_name = "NES / Famicom"
supported_extensions = "nes|fds"
emuname = "Nestopia"
manufacturer = "Nintendo"
systemname = "Nintendo Entertainment System"

View File

@ -0,0 +1,3 @@
display_name = "NXEngine (Cave Story)"
supported_extensions = "exe"
emuname = "NXEngine"

View File

@ -0,0 +1,3 @@
display_name = "PrBoom (DOOM)"
supported_extensions = "wad|iwad"
emuname = "prboom"

View File

@ -0,0 +1,5 @@
display_name = "SNES / Super Famicom"
supported_extensions = "smc|fig|sfc|gd3|gd7|dx2|bsx|swc"
emuname = "SNES9x Next"
manufacturer = "Nintendo"
systemname = "Super Nintendo Entertainment System"

View File

@ -0,0 +1,6 @@
display_name = "Atari 2600"
supported_extensions = "a26|bin"
emuname = "Stella"
manufacturer = "Atari"
systemname = "Atari 2600"

View File

@ -0,0 +1,3 @@
display_name = "TyrQuake"
supported_extensions = "pak"
emuname = "prboom"

View File

@ -0,0 +1,5 @@
display_name = "Game Boy Advance"
supported_extensions = "gba"
emuname = "VBA Next"
manufacturer = "Nintendo"
systemname = "Game Boy Advance"

View File

@ -130,6 +130,8 @@ const char *config_get_default_input(void)
return "gx";
case INPUT_LINUXRAW:
return "linuxraw";
case INPUT_IOS:
return "ios_input";
case INPUT_NULL:
return "null";
default: