Compare commits

..

522 Commits

Author SHA1 Message Date
elasota
4182a1a107 Bump version to 1.1.3 2022-06-22 21:31:46 -04:00
elasota
5643f464cc Discard security exception on getSerialNumber, fixes Android USB gamepad crash. 2022-06-22 21:29:55 -04:00
elasota
a04c5f10df Reject zip archives with non-zero file count but empty central directory 2022-06-22 21:28:28 -04:00
Eric Lasota
3e13877788 Merge pull request #11 from ryandesign/stdlib
Include stdlib.h where its functions are used
2022-03-17 13:10:33 -04:00
Ryan Schmidt
53ff18d337 Include stdlib.h where its functions are used
Fixes build failure on OS X 10.10 and 10.11:

error: use of undeclared identifier 'free'
error: use of undeclared identifier 'malloc'
error: use of undeclared identifier 'qsort'
2022-03-17 02:54:37 -05:00
elasota
e33c01cc40 Change line endings to UNIX 2022-03-17 02:00:06 -04:00
Eric Lasota
0c891d3117 Merge pull request #10 from ryandesign/mac
Improve CMake macOS build
2022-03-17 01:58:13 -04:00
elasota
e4d2d9f9a4 Fix bad null compares 2022-03-17 01:50:19 -04:00
elasota
41c0312921 Add more nullptr_t operators 2022-03-17 01:48:16 -04:00
Ryan Schmidt
e78b01a5f3 Improve CMake macOS build
Now actually builds on macOS.

Closes #9
2022-03-17 00:39:55 -05:00
elasota
d470bb5eeb Add nullptr_t constructor to THandle 2022-03-16 21:24:47 -04:00
elasota
1fe94e4f06 Split AerofoilTools package 2021-11-10 18:05:19 -05:00
elasota
5f2f73e176 Bump to API 30 2021-11-10 17:51:59 -05:00
elasota
7c5864d59b Update installer to VS2019 C++ runtime. 2021-11-10 17:22:01 -05:00
elasota
d698ff23db Bump version to 1.1.2 2021-11-10 17:21:45 -05:00
elasota
92cb961208 Update credits 2021-11-10 17:21:29 -05:00
elasota
545798600e Fix gamepads not working, enable on Android 2021-10-22 22:01:33 -04:00
elasota
9ba15c6d78 Update C++ projects to VS2019, fix warnings/errors 2021-10-22 22:01:14 -04:00
elasota
0706640bc9 Merge branch 'master' of https://github.com/elasota/GlidePort 2021-10-22 13:07:56 -04:00
Eric Lasota
bdb7ddbdbb Create README.md 2021-10-10 11:28:36 -04:00
Eric Lasota
c5dee3ce9e Update README.md 2021-10-10 11:25:21 -04:00
elasota
7e3569500a Handle RGB15BE blit 2021-09-02 04:13:43 -04:00
elasota
376fdf16c4 Add name-to-comment support to GPAs 2021-09-01 22:06:43 -04:00
Eric Lasota
40b38046b9 Merge pull request #6 from pmarell/mac
Mac-specific improvements
2021-08-25 10:06:53 -04:00
Phil Marell
5b5fb15780 Send events through queue instead of calling game methods directly 2021-08-08 16:59:27 +10:00
Phil Marell
744b06796d Remove no longer true comment 2021-08-08 16:57:25 +10:00
Phil Marell
7ba11df286 Add quit event to queue without using private SDL method 2021-08-08 16:57:24 +10:00
Phil Marell
9f5699a61e Return correct value for macOS 2021-08-08 16:56:19 +10:00
Phil Marell
5ab966ea98 Fix build errors 2021-08-08 16:56:19 +10:00
Phil Marell
42ec9e3646 Use #pragma once 2021-08-05 18:46:36 +10:00
Phil Marell
83978d0397 Merge branch 'master' into mac
# Conflicts:
#	AerofoilSDL/GpDisplayDriver_SDL_GL2.cpp
#	PortabilityLayer/MenuManager.cpp
2021-08-05 18:46:18 +10:00
elasota
82fe38dfc7 Fix about dialog misalignments 2021-08-04 02:34:54 -04:00
elasota
2c90110668 Fix key names not lining up correctly, bump prefs version since new key specials don't match old prefs 2021-08-02 01:19:20 -04:00
elasota
eac270670d Add command/meta key support 2021-08-02 01:04:22 -04:00
elasota
c04aeeb962 Add Inter font, map Chicago system symbol reservations using it, use Command symbol on macOS 2021-08-01 22:24:41 -04:00
elasota
f9109850a6 Remove tools from install package so they can be split off into a separate download. 2021-08-01 22:23:56 -04:00
elasota
71955ac729 Fixed GenerateFonts bugs 2021-07-31 17:40:57 -04:00
Phil Marell
f36a8da95f Allow native menu to unhighlight 2021-07-28 20:14:34 +10:00
Phil Marell
0e3534d902 Wire objects/actions in nib 2021-07-28 19:44:07 +10:00
Phil Marell
90943d945b Expose certain methods to be used for Mac menu items 2021-07-28 19:44:07 +10:00
Phil Marell
00488c6fea Override Cocoa app lifecycle defined by SDL 2021-07-28 19:44:07 +10:00
Phil Marell
f16ffa0c4c Replace Main.storyboard with custom MainMenu.xib
The new xib file contains the default main menu created by Xcode, but removes the command-H shortcut from the Hide menu item, so that the in-game High Scores item takes effect instead.

Also SDL only supports custom nib files, not storyboards.
2021-07-28 19:44:03 +10:00
Phil Marell
e098370249 Use Command key instead of Control as modifier on macOS
This provides an experience closer to the original classic Mac version.

This is supported by the earlier changes to gracefully quit the game.
2021-07-24 16:27:55 +10:00
Phil Marell
c3eb23af4b Handle external quits while in editor
This mitigates against potential data loss after having finished editing a house.
2021-07-24 16:27:55 +10:00
Phil Marell
a32b33ef1b Ask to save game if user “externally” quits while in-game
An “external quit” being defined as one where the portability handler receives a request to quit the app, such as closing the window, or on macOS using the Quit menu item.

This can prevent the user losing their progress after having finished with a game.
2021-07-24 16:27:55 +10:00
Phil Marell
b83fd1b28f Move “end game” logic into common function 2021-07-24 16:27:55 +10:00
Phil Marell
ab6c447a2f Compile on Xcode 13 2021-07-24 16:27:55 +10:00
Phil Marell
e02919f1fc Use tabs in Xcode project 2021-07-24 16:27:55 +10:00
Phil Marell
ccceec8b3c Xcode 13 automatic change 2021-07-24 16:18:10 +10:00
Phil Marell
40c1a39c40 Add .DS_Store to .gitignore 2021-07-24 16:18:10 +10:00
elasota
c27d78d329 Bulk import script early version + StuffIt 5 fixes. 2021-06-30 00:45:53 -04:00
elasota
66cce6bcd4 Bump version to 1.1.1 2021-06-06 01:12:31 -04:00
elasota
b616c6bf6e Icon refactor 2021-06-06 01:12:21 -04:00
elasota
2e9954677d Merge branch 'master' of https://github.com/elasota/GlidePort 2021-05-16 12:08:38 -04:00
elasota
3d5265258e Disable asserts 2021-05-16 12:08:23 -04:00
Eric Lasota
6103007ed2 Update README.md 2021-05-16 00:28:58 -04:00
elasota
240ba88f53 Fix Android crash 2021-05-14 19:30:31 -04:00
elasota
71115b2366 Fix AerofoilX compile failure 2021-05-14 19:19:03 -04:00
elasota
2bde3bc60b Update version to 1.1.0 2021-05-14 19:09:35 -04:00
elasota
0524c312d0 Update credits 2021-05-14 18:55:28 -04:00
Eric Lasota
8a69d7b921 Merge pull request #5 from madthijs/mac
Mac version 1.1.0
2021-05-14 18:07:05 -04:00
Madthijs
20a3918535 Merge branch 'master' into mac
# Conflicts:
#	GpApp/House.cpp
2021-05-14 21:44:39 +02:00
Madthijs
bd61b23bde code review changes 2021-05-14 21:40:56 +02:00
elasota
b1bebc2afd Disable asserts in web release build 2021-05-13 12:14:18 -04:00
elasota
5b16ed826f Fix compile failure in debug 2021-05-13 12:13:45 -04:00
elasota
567b82eb23 Tag 1.1.0 rc2 2021-05-11 21:36:12 -04:00
elasota
e8565a122d EOL fix 2021-05-11 21:32:00 -04:00
elasota
32ff2f6fe8 Finish clearing out std::vector from PortabilityLayer 2021-05-11 21:27:40 -04:00
elasota
95260f8d8a Zero out structures when creating houses and rooms 2021-05-11 19:47:05 -04:00
elasota
e7b02f37a5 Fix memory corruption when creating a new house 2021-05-11 19:42:05 -04:00
elasota
781c6ce610 Fix missing return value 2021-05-11 19:18:24 -04:00
elasota
98c7a1eea6 Fix MergeGPF missing from simple release 2021-05-11 19:18:15 -04:00
Madthijs
0a9b5ded9b unneeded changes 2021-05-11 17:15:21 +02:00
Madthijs
09485b7cf4 compile + hardened security 2021-05-11 15:43:09 +02:00
Madthijs
6fd1edd2c8 Merge branch 'master' into mac
# Conflicts:
#	GpApp/Main.cpp
2021-05-11 14:28:46 +02:00
elasota
6858230dde Update copyright 2021-05-11 01:52:56 -04:00
elasota
a6486c137b Change version tag to 1.1.0 rc1 2021-05-11 01:42:06 -04:00
elasota
318a5868d7 Disable loading room editor if the current house is damaged beyond repair, and re-enable loading a different house. 2021-05-11 01:39:17 -04:00
elasota
295db5f064 Fix memory leak 2021-05-11 01:29:40 -04:00
elasota
0def879db1 Fix build 2021-05-11 00:58:26 -04:00
elasota
ac56badffa Add download house option 2021-05-11 00:41:35 -04:00
elasota
75923ad7b0 Fix deleted houses still being in the house list 2021-05-10 23:21:40 -04:00
elasota
32d96798cc Fix file deletion not working in web version 2021-05-10 19:43:52 -04:00
elasota
c354d49e9f Replace beep sound with a simple 80% 440hz + 20% 60hz + fadeout generated tone 2021-05-10 00:10:14 -04:00
elasota
24169507bd Add beep sound to platforms with no beep 2021-05-09 23:45:27 -04:00
elasota
d54ad576bc Add delete option to File browser 2021-05-09 22:48:23 -04:00
elasota
222927d56f Fix double-free when handle is shrunk to zero size 2021-05-09 22:47:57 -04:00
elasota
d74c4b70f3 Remove unused function 2021-05-08 10:55:22 -04:00
elasota
fb7b9b02d9 Web save mechanism 2021-05-07 23:09:35 -04:00
elasota
1ac8032411 Log to stdout 2021-05-07 23:08:29 -04:00
elasota
a012c6d3e7 Fix web build and add export path 2021-05-07 20:12:41 -04:00
elasota
08c6fd5108 EOL fix 2021-05-07 20:12:27 -04:00
elasota
a7cf9d48e3 Fix double GP_RESTRICT definition 2021-05-07 13:29:45 -04:00
elasota
d6ce07685e Clean up vector use 2021-05-07 10:59:31 -04:00
elasota
5e060279af Clean up warnings 2021-05-07 10:59:19 -04:00
elasota
a2ca1725fa Improve export house errors 2021-05-07 03:02:30 -04:00
elasota
f32866510e EOL fix 2021-05-07 02:59:30 -04:00
elasota
ac15c26bef Update installer manufacturer ID 2021-05-07 02:52:28 -04:00
elasota
9510aaee32 Re-enable initial launch disclaimer 2021-05-07 02:30:20 -04:00
elasota
c6aa82db35 Adjust credits 2021-05-07 02:26:10 -04:00
elasota
6d12b6ff1a Add house export to room editor 2021-05-07 02:16:25 -04:00
elasota
c3b2a7e8af EOL fix 2021-05-07 02:14:50 -04:00
elasota
b0d5673d95 Add export dir to Windows 2021-05-07 02:12:58 -04:00
Madthijs
14ef73a8ab add readme for resources 2021-05-05 10:47:42 +02:00
Madthijs
456a134041 ignore resources 2021-05-05 10:47:28 +02:00
Madthijs
27ae449353 remove resources from git 2021-05-05 10:46:12 +02:00
elasota
0de26b5e71 Remove header guards 2021-05-04 18:26:33 -04:00
elasota
65d4784618 Fix desaturation filter not isolating wand star. 2021-05-04 18:26:23 -04:00
elasota
5187ef5dc6 Use upscale filter at 1.5x and 2.5x 2021-05-04 18:25:49 -04:00
Madthijs
b96acad0ca logging fix 2021-05-04 18:11:03 +02:00
Madthijs
2277d1340c Merge branch 'master' into mac 2021-05-04 16:18:01 +02:00
Madthijs
57798c05c6 Undo runtime fonts 2021-05-04 16:05:15 +02:00
elasota
f24b0a0e77 Preprocess audio into S16 for faster mixing. 2021-04-29 20:16:24 -04:00
elasota
7442b92dd3 Make FS use new allocator API 2021-04-28 23:18:24 -04:00
Madthijs
0725828a5f allocator + font stuff 2021-04-28 20:06:51 +02:00
Madthijs
c5a1303b71 Merge branch 'master' into mac
# Conflicts:
#	AerofoilX/GpMain_SDL_X.cpp
2021-04-28 19:17:55 +02:00
elasota
a2d374f650 Allocator refactor 2021-04-28 01:46:07 -04:00
elasota
472462c535 AerofoilX logging fixes 2021-04-27 10:38:09 -04:00
elasota
80683464af Buildfix AerofoilX 2021-04-27 10:00:23 -04:00
elasota
2cd4e4f178 UTF refactor to eliminate duplicated code outside of PL 2021-04-27 09:54:01 -04:00
elasota
07df94fb00 Use combined files on Android for faster build 2021-04-27 09:52:59 -04:00
elasota
d6b206195d Buildfix Android 2021-04-27 09:51:05 -04:00
elasota
65e68f790a Don't allow empty images. 2021-04-27 03:37:24 -04:00
elasota
56d999f400 Don't allow empty sounds 2021-04-27 03:37:14 -04:00
elasota
2991a7490e Preserve active room on editor resolution change. 2021-04-27 01:33:23 -04:00
elasota
0c43370353 Patch Castle o' the Air start room. Fixes issue #4. 2021-04-27 00:14:59 -04:00
elasota
9ba5b14db6 Add HouseTool 2021-04-27 00:14:16 -04:00
elasota
5e1f5655a0 Fix unpacktool not working 2021-04-27 00:13:51 -04:00
Madthijs
67b594fe68 renames 2021-04-26 20:24:38 +02:00
elasota
0385c28002 User house validation 2021-04-25 22:08:15 -04:00
elasota
19b1a307e7 EOL fix 2021-04-25 22:07:04 -04:00
elasota
9341c4c378 EOL fix 2021-04-25 01:59:48 -04:00
elasota
5d9dde6589 Refactor audio buffering API, this should mainly prevent SDL audio driver from allocating memory in the mixer callback. 2021-04-25 00:34:02 -04:00
elasota
f9d3b91f72 Fixed redundant set 2021-04-20 21:02:11 -04:00
elasota
1dc86a703e Fix somewhat incorrect upscale texture handling. Replace InstancedResources reset with something that's less likely to be undefined behavior. 2021-04-20 20:42:30 -04:00
Madthijs
82aaefe342 removed logging + fixed crash issue 2021-04-20 20:24:25 +02:00
Madthijs
d77cf6f5db AppIcon added 2021-04-20 16:44:49 +02:00
Madthijs
a9b6d1dc4f Xcode project clean up target build SDL 2021-04-19 16:01:14 +02:00
Madthijs
08349fc6db Xcode project 2021-04-19 12:10:34 +02:00
elasota
77e02927c6 Fix web crash when dragging or closing popup windows 2021-04-19 02:44:19 -04:00
elasota
c473271e4e Super-minimal shell 2021-04-19 02:28:34 -04:00
elasota
84a4d16aed Web file system implementation + fixes 2021-04-19 01:02:10 -04:00
elasota
f15a87041a Improve synthetic bold quality and use synthetic bold for most Open Sans uses 2021-04-18 18:11:38 -04:00
elasota
d826a908e9 Remove font cache from web 2021-04-18 18:06:45 -04:00
elasota
3d3f839801 Add synthetic bold hack to better support Geneva CY 2021-04-16 00:26:42 -04:00
elasota
141f840888 Specialize font paths for preinstalled font selection instead 2021-04-15 20:56:31 -04:00
elasota
22ae442f87 Handle preinstalled fonts case 2021-04-15 20:08:16 -04:00
elasota
5dd8bf28f5 Add IsUsingPreinstalledFonts API 2021-04-15 20:03:06 -04:00
elasota
bd422d0eb3 EOL fix 2021-04-15 19:52:50 -04:00
elasota
4e8e76f8fc Re-enable runtime font rendering if a font handler is assigned, clean up some font handler things. 2021-04-15 19:49:43 -04:00
Madthijs
bf1dad34c3 It's alive!!! 🦄 2021-04-13 17:18:54 +02:00
elasota
c49bab04d5 Fix modulation being applied twice 2021-04-11 10:57:33 -04:00
Madthijs
e6df94dac0 First compile, Logger works 2021-04-11 14:07:16 +02:00
elasota
71f27f0a05 Fix some suboptimal audio constants. 2021-04-11 01:22:48 -04:00
elasota
b9fe553ff8 Improve mixer performance 2021-04-10 23:56:25 -04:00
elasota
7ae31bc7fc Use fixed latency offset in SDL mixer. This should fix score tick sounds having uneven spacing.
Reduce buffer to 512 samples again.
2021-04-10 21:54:41 -04:00
elasota
2234190f24 Remove FreeType from basic release 2021-04-08 02:01:43 -04:00
elasota
d62de80ff1 Flag GenerateFonts as a dependency for the release package installer 2021-04-08 01:43:00 -04:00
elasota
03f07d7c4f Remove resources dir from basic release 2021-04-08 01:40:48 -04:00
elasota
b90d0c303c Fix AerofoilX 2021-04-07 11:42:57 -04:00
elasota
7842e721e5 Remove Resources dir from Windows release 2021-04-07 10:52:18 -04:00
elasota
93195207dc Disable glGetError calls in release config 2021-04-07 00:03:09 -04:00
elasota
59741dd966 Bump Android version to 1.1.0 2021-04-07 00:02:52 -04:00
elasota
1ac69c2c75 Misc tweaks, partially bump version to 1.1 2021-03-30 05:24:37 -04:00
elasota
69f59f769d Fix broken thread events 2021-03-30 05:24:18 -04:00
elasota
bb2af5abb3 Fix bad parameter 2021-03-30 05:23:54 -04:00
elasota
3692a966bf Tweak some values to help with sound choppiness in Chrome slightly, until we port to OpenAL 2021-03-30 05:23:44 -04:00
elasota
7916c72fc4 Fix incorrect fast AA calculations 2021-03-30 05:23:02 -04:00
elasota
7951f579e0 Remove logs 2021-03-30 00:01:37 -04:00
elasota
b64c904558 Remove junk 2021-03-30 00:00:17 -04:00
elasota
a6f6ffcdcc Remove log func 2021-03-30 00:00:07 -04:00
elasota
6fb45f480b Emscripten port 2021-03-29 21:41:11 -04:00
elasota
9ba0e9f13d Remove FreeType from Android build 2021-03-29 11:57:04 -04:00
elasota
0df94405f8 Faster AA table generation 2021-03-27 03:01:43 -04:00
elasota
ae69696cbd Remove unused header 2021-03-27 03:01:30 -04:00
elasota
50f420d2b1 Font system refactor, use pre-rendered fonts 2021-03-27 03:01:19 -04:00
elasota
c87f238563 Display driver loop refactor 2021-03-26 17:05:38 -04:00
elasota
48fe83bb33 EOL fix 2021-03-19 03:45:43 -04:00
elasota
b2b27ef335 Fix some splash screen inconsistencies 2021-03-19 03:16:59 -04:00
elasota
89831f0ff6 Fix Windows SDL build 2021-03-18 19:05:16 -04:00
elasota
99fd71196d Tentative Linux readme 2021-03-18 18:16:18 -04:00
elasota
7060676b73 Cygwin port 2021-03-18 17:08:11 -04:00
elasota
184f867f79 Fix Android symlinks 2021-03-18 17:05:29 -04:00
elasota
cbd759c754 Add README.md 2021-03-16 03:02:18 -04:00
elasota
2ada8029d0 Fixed source code export not working 2021-03-15 19:36:07 -04:00
elasota
8e0d8e261e Bump version to 1.0.17 2021-03-12 18:42:54 -05:00
elasota
20b9eef64d Fix missing FreeType2 PDBs 2021-03-12 18:26:08 -05:00
elasota
c147e1100e Change MiniRez to use WindowsUnicodeToolShim 2021-03-11 22:28:01 -05:00
elasota
0a2e730d26 Change FTagData to use WindowsUnicodeToolShim 2021-03-11 22:22:30 -05:00
elasota
922cd0fd06 Add debug/release props to WindowsUnicodeToolShim 2021-03-11 22:22:14 -05:00
elasota
b8bf6be44f Change hqx2gp to use WindowsUnicodeToolShim 2021-03-11 22:18:07 -05:00
elasota
2897c4ffab Fix saved game deletion not working 2021-03-11 21:40:22 -05:00
elasota
35c174984b Bump version to 1.0.16 2021-03-11 21:36:06 -05:00
elasota
da16edea8d Fix broken AA table preloader, use load bar first time on desktop 2021-03-09 04:15:59 -05:00
elasota
2fe1ea2ee7 Fix objects being draggable when there is no room 2021-03-08 21:59:11 -05:00
elasota
0931f25f23 Fix wrong house sort order 2021-03-08 21:58:46 -05:00
elasota
7f4d782c0d Fix room count mismatch 2021-03-08 21:58:34 -05:00
elasota
f2cda23b0f Bump version to 1.0.15 2021-03-08 19:08:12 -05:00
elasota
6292705968 Fix insensitive compare not working properly with strings of different length 2021-03-08 19:07:31 -05:00
elasota
6931b3f505 EOL fix 2021-03-08 19:07:06 -05:00
elasota
d70a5b3bfc Add more optimizations 2021-03-07 05:45:24 -05:00
elasota
f0913d0d6a Add MergeGPF to installer project 2021-03-07 05:37:29 -05:00
elasota
80584eb781 Fix California or Bust! house on Android 2021-03-07 05:16:15 -05:00
elasota
fe4a8a55c6 Don't partially delete house when overwriting the current house 2021-03-07 05:08:01 -05:00
elasota
55ec6c516c Fix overwrite warnings not working 2021-03-07 05:07:23 -05:00
elasota
3917e1a370 File system refactor, bug fixes 2021-03-07 04:31:05 -05:00
elasota
6715bcb030 Fix music playing in the editor 2021-03-06 11:34:47 -05:00
elasota
c981a97a20 Fix fullscreen prefs not working in SDL. Add SDL GameController support. 2021-03-05 01:56:32 -05:00
elasota
a5562f96e0 Move portable things to AerofoilPortable. 2021-03-05 00:35:43 -05:00
elasota
d97bd8ad1c Move some C++11 types from Android to AerofoilSDL. 2021-03-01 01:06:56 -05:00
elasota
a43f32ab88 Fix trademark usage 2021-03-01 00:40:30 -05:00
elasota
e94d1511b1 Add 32-bit support to SDL version 2021-02-28 23:31:13 -05:00
elasota
95e4eb4e34 Bump version to 1.0.14, update copyright year 2021-02-28 23:01:53 -05:00
elasota
15bdb97c38 Add volume control to SDL audio driver. 2021-02-28 22:58:01 -05:00
elasota
e7246c1444 Reduce marquee speed 2021-02-20 15:26:44 -05:00
elasota
35b8e922d7 Fix Windows size minimum not working correctly 2021-02-20 15:16:47 -05:00
elasota
3090f70ee6 Bump version to 1.0.13 2021-02-20 15:16:31 -05:00
elasota
484e18a9af Fix resolution desync if resize occurs during the loading screen. 2021-02-20 15:11:31 -05:00
elasota
83d37a7c94 Add system clipboard support to Windows 2020-12-18 00:07:21 -05:00
elasota
fc043af3a1 Bump version to 1.0.12 2020-12-18 00:02:52 -05:00
elasota
2ebd3f2cf3 Whole-word selection in editboxes 2020-12-17 19:49:09 -05:00
elasota
ebab2ee188 Fix incorrect carat pulse timing 2020-12-17 19:48:57 -05:00
elasota
032e44d981 Fix some missing dependencies of ReleasePackageInstaller necessary to import resources. 2020-11-30 23:32:50 -05:00
elasota
7961ca3af7 Fixed some more cases where the screen resolution could become desynced during startup. 2020-11-30 19:02:42 -05:00
elasota
6851025147 Preload entire font file if the font file isn't efficiently seekable, which should fix very slow startup times on Android. 2020-11-30 18:43:17 -05:00
elasota
f0b1d6fff9 Add memory buffer stream 2020-11-30 18:42:35 -05:00
elasota
70e0948847 Log initial resolution 2020-11-30 18:42:22 -05:00
elasota
a698286087 Adjust disclaimer 2020-11-30 10:27:01 -05:00
elasota
b75313fd7b Commit resolution changes 2020-11-30 03:50:57 -05:00
elasota
cab862ed8b Fix exit to shell not working 2020-11-30 03:18:09 -05:00
elasota
553e343abe Fix display resolution desynchronizing with auto-position 2020-11-30 02:59:02 -05:00
elasota
0aa36b27a9 Remove Bluetooth and vibrate permissions 2020-11-30 00:24:58 -05:00
elasota
f6185b1c78 Bump version to 1.0.11 2020-11-30 00:24:23 -05:00
elasota
ff29d5b92c Updated disclaimer 2020-11-30 00:11:21 -05:00
elasota
964c9b8858 Reduce load ring images to grayscale 2020-11-28 11:46:07 -05:00
elasota
8a48726b2e Compress cached fonts 2020-11-28 11:45:51 -05:00
elasota
de06669239 Fix read overrun 2020-11-28 11:45:29 -05:00
elasota
c0abd77dc4 Add source package to PackageReleaseArchives (for itch.io distro) 2020-11-25 18:42:08 -05:00
elasota
754b988f09 Fix initial launch disclaimer always displaying 2020-11-25 18:12:31 -05:00
elasota
1c57a51316 Fix wrong house name displaying after quitting demo 2020-11-25 18:12:00 -05:00
elasota
80abb498af Add first-time launch disclaimer 2020-11-25 18:09:09 -05:00
elasota
29cc376438 Remove trademark symbols since there does not appear to be, or have ever been, a registered trademark. 2020-11-25 15:57:11 -05:00
elasota
ef12c471a7 Fix missing directory entries for resource additions that have no existing directory (i.e. LICS) 2020-11-25 15:51:12 -05:00
elasota
8f4ecfafe1 Speed up load screen 2020-11-25 15:35:12 -05:00
elasota
3c3f9e3675 Add 2-stage startup for mobile init so there's less black screen 2020-11-25 15:09:31 -05:00
elasota
5c640b72eb Code cleanup, move a lot of "Host" APIs to GpCommon 2020-11-25 12:05:59 -05:00
elasota
9d0f2c35b3 Bump version to 1.0.10 2020-11-24 09:42:25 -05:00
elasota
7df624d9b1 Remove "Empty House" (which is locked) and "Sampler" from distribution. 2020-11-24 09:39:43 -05:00
elasota
29fbe83e8d Remove sounds with questionable license status 2020-11-24 09:35:25 -05:00
elasota
ad3a878a16 Added pause to Android source copy script to check if it fails 2020-11-24 09:34:27 -05:00
elasota
f0e7379db6 Increase Android build number 2020-11-13 21:04:34 -05:00
elasota
e34fec38a2 Update readme with new platforms 2020-11-13 16:32:20 -05:00
elasota
76db422456 Fix define collision 2020-11-13 00:57:59 -05:00
elasota
396d107608 Bump Gradle to 4.1.1 2020-11-13 00:55:07 -05:00
elasota
30b39c6991 Add AA table caching 2020-11-13 00:52:10 -05:00
elasota
9dafba1092 Add cancel option to save game prompt 2020-11-12 19:16:47 -05:00
elasota
801408077a Use SV_POSITION instead to compute pixel coordinates instead of fiddling with the polygon. This should fix the image being offset by 1 pixel. 2020-11-12 19:15:50 -05:00
elasota
2c073937c3 Fix houses not being read-only in itch.io release 2020-11-12 19:13:22 -05:00
elasota
bbd147e1ab Fix broken shader compiler 2020-11-12 19:12:53 -05:00
elasota
66a111dd23 Restrict file browser UI by file type 2020-11-12 02:35:07 -05:00
elasota
2febed5d2a Remove "Export Source Code..." menu item 2020-11-12 01:50:36 -05:00
elasota
b47813330a EOL fixes 2020-11-10 20:05:11 -05:00
elasota
0f630a74a2 Add file details 2020-11-10 20:04:29 -05:00
elasota
edc43e0bed Remove crtdbg.h 2020-11-10 20:04:08 -05:00
elasota
2aca0b6b28 Move high scores up to fix overlap on some mobile resolutions 2020-11-10 19:02:18 -05:00
elasota
f9a101486c Add delete to resume game UI 2020-11-09 01:57:49 -05:00
elasota
dbf3303145 Preload AA tables to speed up high scores screen on mobile. 2020-11-09 00:05:01 -05:00
elasota
a28a4cd73d Clean up warnings 2020-11-08 13:40:47 -05:00
elasota
4c6e646133 Clean up language so we can bundle source without getting dinged on age rating. 2020-11-06 17:51:58 -05:00
elasota
a13f90bd71 Fix upscale filter crash 2020-11-04 17:44:22 -05:00
elasota
3d0e457008 Increase version to 1.0.9b2 2020-11-04 17:05:38 -05:00
elasota
1bded36339 Reduce size of file browser UI even more when using OSK to fix obscuring on newer Android 2020-11-04 17:05:24 -05:00
elasota
14b0afbdd2 Increase Android build version to 1.0.9b1 2020-11-03 20:33:52 -05:00
elasota
6986dd5528 Remove WinCursors directory from installation 2020-11-03 20:06:02 -05:00
elasota
3a327a27e7 Add stop demo button 2020-11-03 19:59:26 -05:00
elasota
0d304e8a96 Disallow menu shortcuts when the menu is visible. Fixes unintended feature access when using OTG keyboards on Android. 2020-11-03 19:08:53 -05:00
elasota
43cfb7ea6b Write out scores and prefs immediately upon change instead of when app exits, since Android doesn't really exit normally. 2020-11-03 19:02:22 -05:00
elasota
3b5f222d98 Remove unused function 2020-11-03 18:53:30 -05:00
elasota
02bccda8a0 Re-enable text input when selecting a text box if OSK was dismissed 2020-11-03 18:37:31 -05:00
elasota
e727e462d8 Move save dialog to top of screen when using OSK. 2020-11-03 18:37:08 -05:00
elasota
47e23fbc71 Fix bad window reposition math when using touchscreen menu style 2020-11-02 22:45:44 -05:00
elasota
98c217d0bb Adjust progress bar color scheme, nicer wording 2020-11-02 22:36:39 -05:00
elasota
3b91d0492e Fix downward scroll when deleting a character in a multi-line edit box when all of the lines fit inside of the edit box 2020-11-02 22:22:31 -05:00
elasota
2b3a9f1669 Fix compile failure on Windows 2020-11-02 22:09:39 -05:00
elasota
0d5db76492 Add text input on mobile 2020-11-02 22:06:38 -05:00
elasota
2ab1416eef Improve upscale filter quality on mobile 2020-11-02 19:04:49 -05:00
elasota
c0f71ca1af EOL fix 2020-11-02 18:19:31 -05:00
elasota
64a2d712e3 Improve GL error logging, fix wrong texture upload on GLES2 2020-11-02 01:08:18 -05:00
elasota
10884f4c1a Bump Android build number 2020-11-01 23:01:26 -05:00
elasota
6d0d2f86d3 Increase touchscreen menu spacing 2020-11-01 21:45:48 -05:00
elasota
b11ff6fdd4 Save prefs after first run so first-time setup screen looks different 2020-11-01 21:45:37 -05:00
elasota
475b8d21fb Improve load screen responsiveness 2020-11-01 20:59:52 -05:00
elasota
47be9d73e3 Fix monospace font collision, cache more fonts 2020-11-01 18:03:40 -05:00
elasota
2f32c4f434 Add touchscreen pause menu 2020-11-01 17:43:15 -05:00
elasota
232a98a380 Log OpenGL context resets 2020-11-01 13:48:14 -05:00
elasota
b6cb8535a5 Fix wrong source export window position on Android 2020-11-01 13:48:00 -05:00
elasota
a07b7d41e4 Fix broken window repositioning on mobile 2020-11-01 13:47:22 -05:00
elasota
96867a3ed8 Log Window Manager automatic repositions 2020-11-01 13:31:27 -05:00
elasota
4a4dcc995f Add Android logger 2020-11-01 13:31:04 -05:00
elasota
f2052b835b Fix main menu UI showing in demo, add menu button (not functional yet) 2020-10-25 16:32:49 -04:00
elasota
b983d11009 Clean up copyrights 2020-10-25 16:32:25 -04:00
elasota
b9c600860d Don't render touchscreen controls in demo 2020-10-25 15:42:27 -04:00
elasota
5bdbcc9147 Increase MMUI speed, fix alignment bug on resolution change 2020-10-25 15:26:01 -04:00
elasota
f9c794efee Fix crash when ending game on mobile 2020-10-25 00:47:46 -04:00
elasota
a774320324 Minor license text fixups 2020-10-24 23:57:17 -04:00
elasota
71c152026c Use darken effect while exporting source 2020-10-24 23:56:18 -04:00
elasota
b50508f85f Fix missing RapidJSON license 2020-10-24 23:56:04 -04:00
elasota
37fc11a023 Better source export file name 2020-10-24 23:50:04 -04:00
elasota
eb1f59afbd Fix touch screen controls lingering on resolution change while in a sub-UI 2020-10-24 23:49:51 -04:00
elasota
47eaf75778 Fix license reader crash 2020-10-24 23:47:27 -04:00
elasota
b9ea9450f1 Add license browser, improve touchscreen UI 2020-10-24 23:39:55 -04:00
elasota
4f70ce1c9f Undo lower-screen offset of main window in touchscreen mode 2020-10-24 14:35:43 -04:00
elasota
0452e61043 Blacken scoreboard after game since it doesn't have a region reserved on mobile 2020-10-24 14:35:06 -04:00
elasota
26c423bb58 Cache rendered fonts to speed up mobile load 2020-10-24 11:41:39 -04:00
elasota
daebba7d47 Default fit to screen so it works properly on mobile 2020-10-24 10:03:36 -04:00
elasota
3f6f540bf5 Fix scale issue 2020-10-24 10:03:22 -04:00
elasota
36eb111d26 Add proper source export prompt 2020-10-24 10:03:12 -04:00
elasota
23b69cf0ee Lots of Android fixes and stubs. Increase SDL log level on Android. Add GL context loss handling. 2020-10-20 23:43:02 -04:00
elasota
f26f631ae2 Finish up source export 2020-10-19 18:51:18 -04:00
elasota
1b4754ae13 EOL fix 2020-10-19 17:46:06 -04:00
elasota
47b742ed45 Use GPAs on Android and just don't compress them. 2020-10-19 03:03:33 -04:00
elasota
c96a1ab251 Fix EOL 2020-10-18 14:56:04 -04:00
elasota
1da35d45ae Don't default to fullscreen in touchscreen simulation mode 2020-10-18 14:55:56 -04:00
elasota
30b07a77a9 Source code export placeholder 2020-10-17 22:56:27 -04:00
elasota
842f070ff5 Fix missing Davis Station movie 2020-10-17 22:55:18 -04:00
elasota
0f1ed5d10a Add directory scan to Android 2020-10-17 18:08:31 -04:00
elasota
fbf73fd832 Use system zlib 2020-10-17 18:04:28 -04:00
elasota
8031f66226 Add source package 2020-10-17 17:39:56 -04:00
elasota
37499638fc Move apps to new package 2020-10-17 12:42:47 -04:00
elasota
7c025337dc Fix Android manifest name 2020-10-17 12:22:56 -04:00
elasota
10a5b85edb Fix aar name 2020-10-17 10:37:05 -04:00
elasota
bece23dc8c Update package, update to SDK 29 and Android Studio 4.1 2020-10-17 03:38:31 -04:00
elasota
babf7cce89 Update privacy policy to remove speculative future events 2020-10-17 03:37:19 -04:00
elasota
7aa45cc9d6 No loading screen on desktop 2020-10-16 20:08:08 -04:00
elasota
bfb2c727ea Loading screen 2020-10-16 20:06:47 -04:00
elasota
ad5a0795f4 GL error checks, remove GL_ALPHA_TEST check, which doesn't work in GLES2. 2020-10-16 20:06:32 -04:00
elasota
febd0e05ac Privacy policy stub for GPS 2020-10-16 20:03:07 -04:00
elasota
e1c83d7b47 Redo touch controls 2020-10-15 01:14:10 -04:00
elasota
1e1c91915a Fix OpenGL scaling and improve performance 2020-10-14 19:02:55 -04:00
elasota
ad1912379a Use landscape orientation only 2020-10-14 18:28:52 -04:00
elasota
64ce75caa8 Fix touch screen flip 2020-10-14 18:26:13 -04:00
elasota
b682004f29 Character encoding fixups 2020-10-14 18:18:57 -04:00
elasota
7858aff6cd Flush file operations before using FD. Fixes things like score files becoming corrupted. 2020-10-14 18:12:47 -04:00
elasota
3347e94b88 Fix GLES2 compile failure 2020-10-14 18:12:12 -04:00
elasota
e4cddda183 Touchscreen improvements 2020-10-14 18:12:02 -04:00
elasota
62e234c777 Fix cursor crash 2020-10-14 18:08:41 -04:00
elasota
780f4842b0 Increase touchscreen control size 2020-10-13 10:02:17 -04:00
elasota
c13677122c Fix flicker 2020-10-13 10:01:55 -04:00
elasota
14f7a5beed Change app name 2020-10-13 10:00:43 -04:00
elasota
aeef313a8a Fix GLES2 flicker crash 2020-10-13 10:00:32 -04:00
elasota
aa2f6dfd2c Fix desaturation not working 2020-10-13 09:47:25 -04:00
elasota
b03561c76b Fix wrong month on calendar 2020-10-13 09:44:39 -04:00
elasota
dd269d52e0 Fix up Android asset directory scan 2020-10-13 00:15:56 -04:00
elasota
1ecef6f8ef Add support for unpackaged resources to speed up loads on Android, i.e. so we don't have to decompress entire GPAs to load a single resource. 2020-10-12 18:48:53 -04:00
elasota
ec56bdace2 Android package cleanup 2020-10-12 10:43:37 -04:00
elasota
3b6ae1dba5 Android fixups 2020-10-12 00:37:32 -04:00
elasota
5fbf3f5df0 Clean up game thread in SDL version 2020-10-12 00:37:11 -04:00
elasota
522e99afb9 Fix audio deadlock 2020-10-12 00:36:13 -04:00
elasota
184daefe7b Touchscreen things 2020-10-11 19:50:03 -04:00
elasota
5c98783bbb More Android stub-outs and bug fixes. Fix broken SDL fiber sync. 2020-10-10 02:42:06 -04:00
elasota
a2f19f5ccb Android stubs 2020-10-09 21:46:30 -04:00
elasota
3a736296ce Add symlinks and other things 2020-10-09 18:57:42 -04:00
elasota
9be2885a1e Copy Android project 2020-10-09 18:10:20 -04:00
elasota
1b497cb1f7 Add SDL2 sources 2020-10-09 16:19:18 -04:00
elasota
15282f2ab2 Remove Win32 log driver from SDL project 2020-10-09 16:16:11 -04:00
elasota
4445ea5770 SDL event translation 2020-09-28 17:38:39 -04:00
elasota
9cb60af2b3 SDL audio driver 2020-09-28 09:58:19 -04:00
elasota
2400d68aeb Bump version to 1.0.9 2020-09-26 21:16:19 -04:00
elasota
e88c9e7112 Redo mouse cursor handling 2020-09-26 21:15:43 -04:00
elasota
db54e92425 Fix flicker effect in OpenGL driver 2020-09-26 16:54:30 -04:00
elasota
c3b1f45f96 Fix missing return value 2020-09-26 16:47:45 -04:00
elasota
45aa5b4cba OpenGL display driver 2020-09-26 16:45:52 -04:00
elasota
da3cdb3a17 Bump ImportToolsFeature level to 4 so it doesn't install with Typical 2020-09-13 02:05:28 -04:00
elasota
9669f6039d Update readmes 2020-09-12 23:09:53 -04:00
elasota
1fb135a6d2 Clean up project configs. Remove Win32 targets. Enforce that GP_DEBUG_CONFIG is set correctly. 2020-09-12 23:07:44 -04:00
elasota
b23bb93506 Redo file prompts with in-game UI 2020-09-12 22:29:57 -04:00
elasota
8518d01c70 Conform porting doc to 80-char word wrap 2020-09-12 14:36:15 -04:00
elasota
1b215e891f SDL baseline 2020-09-12 14:23:02 -04:00
elasota
987a1dea75 Move font API to GpCommon 2020-09-12 14:01:51 -04:00
elasota
f07137b52d Move IOStream to GpCommon 2020-09-12 13:32:53 -04:00
elasota
480a4b6098 Add desktop shortcut 2020-09-12 12:09:59 -04:00
elasota
5ab7a675a3 Bump version to 1.0.8 2020-09-12 11:52:36 -04:00
elasota
1d3c84a473 Move font handler and platform-independent shell stuff 2020-09-12 11:52:19 -04:00
elasota
4a78f10975 Remove x86/Win32 target 2020-09-11 23:54:16 -04:00
elasota
69f279cc64 EOL fixes 2020-09-11 23:53:51 -04:00
elasota
1efb34f710 Undef CreateFile 2020-09-11 23:53:25 -04:00
elasota
84d5c46506 EOL fixes 2020-08-11 18:35:39 -04:00
elasota
c95be907de Bump version to 1.0.7 2020-07-21 12:23:31 -04:00
elasota
3fd7fabb98 Fix full-screen being disabled when going to preferences but not viewing display options 2020-07-21 12:23:13 -04:00
elasota
53ecdabb43 Add full-screen option 2020-07-20 23:38:31 -04:00
elasota
7bf6147fa1 Fix transitions not spanning the entire screen and fix dissolve replaying when toggling full-screen 2020-07-20 12:17:36 -04:00
elasota
a77e1d868f Bump version to 1.0.6 2020-07-20 12:16:41 -04:00
elasota
c14d904ca7 Fix bad ZIP structure 2020-07-20 12:16:31 -04:00
elasota
ec275fcefd Bump version to 1.0.5 2020-07-04 00:35:18 -04:00
elasota
550465088e Remove architecture string code that doesn't work 2020-07-04 00:34:46 -04:00
elasota
68444a7240 Add transitions 2020-07-04 00:26:28 -04:00
elasota
7db0f8d7eb Fix loading empty house in the editor not working correctly 2020-07-03 19:40:26 -04:00
elasota
1fc846f7d8 Fix menu shortcuts activating disabled menu items 2020-07-03 19:31:30 -04:00
elasota
d978267c3e Remove selection scroll timer (which doesn't work anyway) 2020-07-03 18:06:30 -04:00
elasota
e05f37a28d Fix oval frames not drawing in 32-bit color mode 2020-07-03 17:32:26 -04:00
elasota
7069f4bf82 Add package release script 2020-07-03 14:04:54 -04:00
elasota
b197a9fb4b Fix copyright 2020-07-03 13:53:19 -04:00
elasota
62427de092 Add ICC profile option 2020-07-03 13:53:10 -04:00
elasota
037994d0f7 Add additional credits 2020-07-03 13:48:48 -04:00
elasota
7cdfc40ad9 Clarified ambiguous language 2020-07-03 13:48:33 -04:00
elasota
c53275d3fb Bump version to 1.0.4 2020-07-03 05:08:52 -04:00
elasota
8d58d22731 Add build version 2020-07-03 05:05:12 -04:00
elasota
58ad634085 Re-add clipboard functions 2020-07-03 04:59:40 -04:00
elasota
00ea1b2982 Fix invert image draw not working correctly if the image is partially out of bounds (happens when moving a tiki torch in the editor) 2020-07-03 04:18:08 -04:00
elasota
87bab26d47 Add home/end key support to edit box 2020-07-03 04:08:09 -04:00
elasota
3dbb231c5a Add edit box automatic scrolling 2020-07-03 04:00:25 -04:00
elasota
a2e8d414e8 Fix typing sound not working with forward delete 2020-07-03 03:16:42 -04:00
elasota
137f00b657 Fixed selection drawing out of bounds in multi-line editbox 2020-07-03 03:05:25 -04:00
elasota
7fec622e51 Add forward delete to editbox 2020-07-03 03:02:43 -04:00
elasota
05604e5604 Add API for saving driver prefs. Save fullscreen state in D3D11 driver. 2020-07-03 02:46:43 -04:00
elasota
ed9e0fec97 Update readme 2020-07-03 02:45:41 -04:00
elasota
82b93c627e Save house after opening so it doesn't throw an IO error if the user quits immediately. 2020-06-20 02:16:43 -04:00
elasota
c204b5ef7f Fix debug compile failure 2020-06-20 02:09:32 -04:00
elasota
343a8a0f04 Bump build version to 1.0.3 2020-06-20 02:08:35 -04:00
elasota
9d5adb9bb6 Remove port state, fix background not darkening in room editor 2020-06-20 02:08:22 -04:00
elasota
3bf689664f Default menu bar color to black 2020-06-20 01:46:38 -04:00
elasota
ccb7dd2edb Remove unused var 2020-06-20 01:46:27 -04:00
elasota
20acb4ef6b Bump prefs version 2020-06-20 01:33:47 -04:00
elasota
3c662845b4 Add option to enable or disable auto-scale 2020-06-20 01:33:26 -04:00
elasota
1981320afe Add support for 16-bit, stereo, and MACE 3:1 sound import. 2020-06-20 01:03:41 -04:00
elasota
1c15ea5940 Update project readme 2020-06-13 05:32:27 -04:00
elasota
4920781619 32-bit color support 2020-06-13 04:33:41 -04:00
elasota
24f43b973a Fix build timestamp 2020-06-07 22:17:02 -04:00
elasota
649d78a61b Tag release 1.0.1 2020-06-07 21:05:08 -04:00
elasota
836fc95f4a EOL fix 2020-06-07 20:45:30 -04:00
elasota
5b95fd8c6b Use Apple RGB color profile 2020-06-07 20:42:49 -04:00
elasota
b55a508686 Use error diffusion for 24-to-8 picture draws 2020-06-07 20:42:07 -04:00
elasota
47291cbf1d Support custom pause and banner graphics 2020-06-07 19:36:53 -04:00
elasota
01e6ff4f5d Fix >16bpp images emitting color table data, causing image corruption, especially in 24bpp images 2020-06-07 17:45:29 -04:00
elasota
d828eacd38 Clean "Packaged" dir when converting resources 2020-06-07 17:20:21 -04:00
elasota
b37b0a4f8a Add MACE 6:1 decompression to fix some missing audio samples, parallelize deflate compression 2020-06-07 17:18:04 -04:00
elasota
4c3ccbd7fa Only copy PDBs that are actually in the release package 2020-06-06 02:32:05 -04:00
elasota
9624c283c8 Updated version tagging and added about dialog 2020-06-06 02:25:10 -04:00
elasota
cfb66d9c9b Fix res patch file adding not working 2020-06-06 01:49:54 -04:00
elasota
c185c8d9ec Fix filters 2020-06-05 23:33:58 -04:00
elasota
7636fd6fa8 Remove unused file 2020-06-05 23:25:08 -04:00
elasota
6c48debecd Code cleanup 2020-06-05 23:21:56 -04:00
elasota
3f55eedcf0 Remove unused header 2020-06-05 23:19:47 -04:00
elasota
856c7d5297 Remove unused header 2020-06-05 22:17:35 -04:00
elasota
d7301402c5 Fix broken release script 2020-06-04 17:26:02 -04:00
elasota
2a98bfbc8c Remove AppleEvents API 2020-06-02 20:01:16 -04:00
elasota
eac923c475 Refactoring 2020-06-01 00:38:24 -04:00
elasota
6fe0f2d964 API refactoring 2020-06-01 00:33:50 -04:00
elasota
a4abb0d95f Use XAudio 2.9 redistributable instead of 2.9 SDK. Should fix Win7 compatibility. 2020-05-31 21:19:51 -04:00
elasota
c856607f46 Fixed some documentation issues 2020-05-29 22:03:13 -04:00
elasota
611f53ef91 Fix audio driver starting in debug mode, add -diagnostics mode 2020-05-29 21:56:33 -04:00
elasota
98afd82d64 Fix missing NDEBUG flags 2020-05-29 01:45:40 -04:00
elasota
efae9cacd8 Add PDB directory to release script 2020-05-29 00:00:11 -04:00
elasota
5184d1594f Fix comment typo 2020-05-28 23:10:30 -04:00
elasota
42e124a90c Fix crash if audio init fails (especially if there are no output devices) 2020-05-28 23:05:32 -04:00
elasota
11628ddd93 Fix link crash 2020-05-27 18:06:22 -04:00
elasota
7d5f844fd4 Fix bad usage formatting 2020-05-27 18:06:12 -04:00
elasota
f5ff8eb013 Add color fade 2020-05-22 21:14:43 -04:00
elasota
482487d81c Change window title 2020-05-22 20:42:18 -04:00
elasota
2d8b6a29aa Add scroll bar highlights 2020-05-22 20:22:51 -04:00
elasota
8f433c11e8 Revert "Add menu selection flicker"
This reverts commit bd9676be40.
2020-05-22 20:22:40 -04:00
elasota
9c32a6fdd4 Add stripes to window chrome 2020-05-22 05:41:07 -04:00
elasota
bd9676be40 Add menu selection flicker 2020-05-22 05:09:59 -04:00
elasota
392c5d0583 Lighten window chrome 2020-05-22 05:09:48 -04:00
elasota
14cc4b43df Fix a bug where getting a high score in a read-only house and then opening an editable house would cause the editable house to be overwritten 2020-05-21 08:23:09 -04:00
elasota
de342cb368 Fix bad string compare function (caused houses to not appear if they started with the same name as another house) 2020-05-21 08:22:29 -04:00
elasota
b68cfab6d8 Fix wrong prompt font size 2020-05-21 05:04:41 -04:00
elasota
5869571747 Finish removing QDState 2020-05-21 05:01:16 -04:00
elasota
432cdbcc3a Refactor out clip rect 2020-05-21 03:39:33 -04:00
elasota
438e7b2138 Refactor out forecolor 2020-05-21 03:30:11 -04:00
elasota
a1c45d4fc8 Factor out back color 2020-05-20 23:51:25 -04:00
elasota
66fc278ce9 Refactor QD ports so they no longer need to be the first member of draw surfaces 2020-05-20 23:33:17 -04:00
elasota
f53dc21475 Window API refactor 2020-05-20 17:20:50 -04:00
elasota
49c438b088 Fix flicker in load house UI 2020-05-18 04:38:16 -04:00
elasota
5e6ecaf0fa EOL fixes 2020-05-18 04:23:38 -04:00
elasota
47e36f1c3c Remove dead files from VS project, fix broken filters 2020-05-18 04:23:30 -04:00
elasota
d7a769e397 Change window icon 2020-05-18 04:05:18 -04:00
elasota
1abb542301 Fix sound prefs window flickering dark when changing the volume 2020-05-18 03:51:36 -04:00
elasota
afb9474340 Improve popup menu triangle visibility 2020-05-18 03:51:20 -04:00
elasota
50ab5f5bdb Inset stars window by 1px to account for border being present in the image 2020-05-18 03:36:48 -04:00
elasota
b12151f665 Use exclusive stack to keep darken during flicker 2020-05-18 03:36:20 -04:00
elasota
8135c68c49 Add flicker effect to chrome (replaces zooms) 2020-05-18 03:30:25 -04:00
elasota
5c07ce08bb Remove dead headers 2020-05-18 02:06:07 -04:00
elasota
ea16d0ffca More window chrome improvements 2020-05-18 02:03:17 -04:00
elasota
f590613f83 Default button chrome improvement 2020-05-17 23:02:08 -04:00
elasota
c0878fe66d Moved Aerofoil package defs out of the installer project to reduce chance of people getting bad ideas 2020-05-17 22:23:28 -04:00
elasota
9c18a2ba55 Installer improvements for 1.0b3 2020-05-17 22:04:07 -04:00
elasota
0b8a5cb38c Fixed resolution changed event using physical resolution instead of virtual 2020-05-17 19:55:16 -04:00
elasota
fde390ac73 Improve button chrome 2020-05-17 19:50:34 -04:00
elasota
153213e079 Improve PICT compatibility, add batch mode to gpr2gpa 2020-05-17 17:54:58 -04:00
elasota
35308e41f3 Fix MSI installer not setting houses read-only 2020-05-11 05:35:29 -04:00
elasota
931d7e0f30 Move install package generation to MakeRelease, fix missing read-only attribute on MSI installation houses 2020-05-11 04:34:43 -04:00
elasota
539af1f9b5 Adjust formatting, recommend FTagData parameters 2020-05-11 04:18:28 -04:00
elasota
e052628ed3 Remove ReleasePackageInstaller from Build Solution.
Add resource import batches to ReleasePackageInstaller build steps.
Together, this means that Build Solution and then building ReleasePackageInstaller from clean source should produce a working installer.
2020-05-11 01:21:10 -04:00
elasota
e52735ae7f Remove warning macro 2020-05-11 00:03:36 -04:00
elasota
e539b93de9 Adjust MSI package dir 2020-05-10 20:13:53 -04:00
elasota
f952b1c63a Add MSI installer project 2020-05-10 20:04:58 -04:00
elasota
44c32a06ab EOL fix 2020-05-10 20:04:10 -04:00
elasota
e1f9e86c56 EOL fix 2020-05-10 20:03:59 -04:00
elasota
231c4b411f Update docs in line with flattenmov's new behavior 2020-05-09 23:50:34 -04:00
elasota
9ddaec8add Fix Compact Pro extractor data corruption 2020-05-09 23:11:39 -04:00
elasota
e9d65697f3 Add some missing defs, change flattenmov to use triplets 2020-05-09 23:11:16 -04:00
elasota
ebb6d7608e Add unpacktool 2020-05-09 21:05:58 -04:00
elasota
b849d23f4e Update readme 2020-05-02 16:08:52 -04:00
elasota
62d9766ee0 Add close box support to floating windows 2020-05-02 02:59:19 -04:00
elasota
e546c05ea0 Fix scroll bar SetMin/SetMax not working correctly 2020-05-02 02:14:52 -04:00
elasota
3b7858f96a Add support for map window resize in editor 2020-05-02 01:51:32 -04:00
elasota
92c4878492 Fix scaling behavior when the window size doesn't divide evenly into the pixel scale 2020-04-26 01:56:55 -04:00
elasota
e2127038ef Add fullscreen keybind to docs 2020-04-25 21:22:22 -04:00
elasota
cea0a72ebc Remove more obsolete API 2020-04-25 21:22:09 -04:00
elasota
3c5dd5f562 Manually pass dx/dy instead of using ddx/ddy, should improve interpolation somewhat 2020-04-25 18:54:56 -04:00
elasota
5bb6b074f0 Scale quads after rendering to a screen texture instead of scaling directly. Fixes discontinuities in room editor and menu edges. 2020-04-25 16:35:34 -04:00
elasota
ea217285c0 Decrease scale granularity to 2.0 2020-04-25 00:53:43 -04:00
elasota
cd4e0ae8de Use Fant filter to resize to intermediate resolutions. 2020-04-25 00:51:44 -04:00
elasota
c357ca2b7c Fix artifacts if a resize reordered the saved maps of an item.
(Happens in Slumberland if resizing the window from 1600x900 to 800x600 in the second room, then picking up the cuckoo.)
2020-04-25 00:50:28 -04:00
elasota
b5a3db860f Update license 2020-04-23 21:41:19 -04:00
elasota
69e3fb3023 Handle Quit event more gracefully 2020-04-18 05:51:39 -04:00
elasota
94f26d0be1 Redraw grease on room lighting change. Fixes regression where grease would not be visible after turning lights on. 2020-04-18 05:23:40 -04:00
elasota
d893b356f1 Fix dynamic indexes desynchronizing on resolution change (fixes Slumberland boombox switch not working if you change resolution on the screen with the switch) 2020-04-18 00:42:45 -04:00
elasota
c3f3fb4621 Override alt-enter behavior, use borderless fullscreen instead of exclusive fullscreen. This should fix the game breaking when bringing up the open file dialog in fullscreen.
(Note that this doesn't handle resolution changes yet)
2020-04-18 00:18:20 -04:00
elasota
0335dd7786 Fix alt-enter 2020-04-06 03:34:31 -04:00
elasota
ffd9d9cc1f Keep level editor windows in screen bounds 2020-04-05 22:31:22 -04:00
2095 changed files with 590611 additions and 11350 deletions

41
.gitignore vendored
View File

@@ -20,7 +20,48 @@
*.idb
*.aps
*.res
*.a
*.recipe
*.FileListAbsolute.txt
.vs/*
Packaged/*
DebugData/*
ReleasePkg/*
InstallerPackages/*
*.msi
*.wixpdb
*.wixobj
*.CopyComplete
*.lnk
*.cmake
ReleasePackageInstaller/obj/*
ReleasePackageInstaller/bin/*
ReleasePackageInstaller/AerofoilPackageDefs.wxi
ReleasePackageInstaller/AerofoilPackageVersion.wxi
packages/*
!SDL2-2.0.12/lib/x64/*
CMakeCache.txt
CMakeFiles/*
Makefile
SDL2-2.0.12/CMakeFiles/*
SDL2-2.0.12/build
SDL2-2.0.12/config.status
SDL2-2.0.12/libtool
SDL2-2.0.12/Makefile.rules
SDL2-2.0.12/sdl2.pc
SDL2-2.0.12/sdl2-config
install_manifest.txt
## macOS
.DS_Store
## Xcode projects
AerofoilMac/xcuserdata/
AerofoilMac/*.xcodeproj/xcuserdata/
AerofoilMac/build/
AerofoilMac/DerivedData/
*.xcuserstate
SDL2-2.0.12/Xcode/SDL/SDL.xcodeproj/xcuserdata/*
AerofoilMac/Resources/*.gpf
AerofoilMac/Resources/Houses/*.gpf
AerofoilMac/*.xcodeproj/project.xcworkspace/xcuserdata

4
.gitmodules vendored Normal file
View File

@@ -0,0 +1,4 @@
[submodule "AerofoilWeb/FileSaverDotJS"]
path = AerofoilWeb/FileSaverDotJS
url = https://github.com/eligrey/FileSaver.js.git
branch = master

66
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,66 @@
{
"files.associations": {
"iostream": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"codecvt": "cpp",
"compare": "cpp",
"complex": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"map": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"ratio": "cpp",
"regex": "cpp",
"string": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"ostream": "cpp",
"ranges": "cpp",
"shared_mutex": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp"
}
}

View File

@@ -1,24 +0,0 @@
Open Sans font:
(c)2011 Google
Distributed under the Apache License (see Resources/Fonts/OpenSans/LICENSE)
stb_image_write:
Created by Sean Barrett
MacRomanConversion uses a table from LIBICONV:
Copyright (C) 1999-2001, 2016 Free Software Foundation, Inc.
Distributed under the LGPLv2 license (See MacRomanConversion/LICENSE.txt)
RapidJSON:
Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
Distributed under the MIT license (See rapidjson/license.txt)
zlib:
(C) 1995-2017 Jean-loup Gailly and Mark Adler
Distributed under zlib license (See zlib/README)

322
ASADTool/ASADTool.cpp Normal file
View File

@@ -0,0 +1,322 @@
#include "WindowsUnicodeToolShim.h"
#include "PLBigEndian.h"
#include "MacFileInfo.h"
#include "CombinedTimestamp.h"
#include "CFileStream.h"
#include "PLCore.h"
#include <algorithm>
// https://tools.ietf.org/rfc/rfc1740
int ProcessFork(FILE *f, uint32_t length, const char *basePath, const char *suffix)
{
const size_t kBufferSize = 4096;
uint8_t buffer[kBufferSize];
std::string combinedPath = std::string(basePath) + suffix;
FILE *outF = fopen_utf8(combinedPath.c_str(), "wb");
if (!outF)
{
fprintf(stderr, "Failed to open output file '%s'", combinedPath.c_str());
return -1;
}
while (length > 0)
{
const size_t amountToCopy = std::min<size_t>(length, kBufferSize);
if (fread(buffer, 1, amountToCopy, f) != amountToCopy)
{
fprintf(stderr, "Failed to copy data");
fclose(outF);
return -1;
}
if (fwrite(buffer, 1, amountToCopy, outF) != amountToCopy)
{
fprintf(stderr, "Failed to copy data");
fclose(outF);
return -1;
}
length -= static_cast<uint32_t>(amountToCopy);
}
fclose(outF);
return 0;
}
int ProcessFileDatesInfo(FILE *f, uint32_t length, PortabilityLayer::MacFileProperties &mfp, PortabilityLayer::CombinedTimestamp &ts)
{
struct ASFileDates
{
BEInt32_t m_created;
BEInt32_t m_modified;
BEInt32_t m_backup;
BEInt32_t m_access;
};
ASFileDates fileDates;
if (length < sizeof(fileDates))
{
fprintf(stderr, "File dates block was truncated");
return -1;
}
if (fread(&fileDates, 1, sizeof(fileDates), f) != sizeof(fileDates))
{
fprintf(stderr, "Failed to read file dates");
return -1;
}
const int64_t asEpochToMacEpoch = -3029547600LL;
// Mac epoch in Unix time: -2082844800
// ASAD epoch in Unix time: 946702800
mfp.m_createdTimeMacEpoch = static_cast<int64_t>(fileDates.m_created) + asEpochToMacEpoch;
mfp.m_modifiedTimeMacEpoch = static_cast<int64_t>(fileDates.m_modified) + asEpochToMacEpoch;
ts.SetMacEpochTime(mfp.m_modifiedTimeMacEpoch);
return 0;
}
int ProcessFinderInfo(FILE *f, uint32_t length, PortabilityLayer::MacFileProperties &mfp)
{
struct ASFinderInfo
{
uint8_t m_type[4];
uint8_t m_creator[4];
BEUInt16_t m_finderFlags;
BEPoint m_location;
BEUInt16_t m_folder; // ???
};
struct ASExtendedFinderInfo
{
BEUInt16_t m_iconID;
uint8_t m_unused[6];
uint8_t m_scriptCode;
uint8_t m_xFlags;
BEUInt16_t m_commentID;
BEUInt32_t m_putAwayDirectoryID;
};
ASFinderInfo finderInfo;
if (length < sizeof(finderInfo))
{
fprintf(stderr, "Finder Info block was truncated");
return -1;
}
if (fread(&finderInfo, 1, sizeof(finderInfo), f) != sizeof(finderInfo))
{
fprintf(stderr, "Failed to read Finder info");
return -1;
}
memcpy(mfp.m_fileCreator, finderInfo.m_creator, 4);
memcpy(mfp.m_fileType, finderInfo.m_type, 4);
mfp.m_finderFlags = finderInfo.m_finderFlags;
mfp.m_xPos = finderInfo.m_location.h;
mfp.m_yPos = finderInfo.m_location.v;
return 0;
}
int ProcessMacintoshFileInfo(FILE *f, uint32_t length, PortabilityLayer::MacFileProperties &mfp)
{
struct ASMacInfo
{
uint8_t m_filler[3];
uint8_t m_protected;
};
ASMacInfo macInfo;
if (length < sizeof(macInfo))
{
fprintf(stderr, "File dates block was truncated");
return -1;
}
if (fread(&macInfo, 1, sizeof(macInfo), f) != sizeof(macInfo))
{
fprintf(stderr, "Failed to read file dates");
return -1;
}
mfp.m_protected = macInfo.m_protected;
return 0;
}
int ProcessFile(FILE *f, const char *outPath, PortabilityLayer::CombinedTimestamp ts, bool isDouble)
{
struct ASHeader
{
BEUInt32_t m_version;
uint8_t m_filler[16];
BEUInt16_t m_numEntries;
};
struct ASEntry
{
BEUInt32_t m_entryID;
BEUInt32_t m_offset;
BEUInt32_t m_length;
};
ASHeader header;
if (fread(&header, 1, sizeof(header), f) != sizeof(header))
{
fprintf(stderr, "Failed to read header");
return -1;
}
const uint32_t numEntries = header.m_numEntries;
if (numEntries > 0xffff)
{
fprintf(stderr, "Too many entries");
return -1;
}
if (numEntries == 0)
return 0;
std::vector<ASEntry> entries;
entries.resize(static_cast<uint32_t>(numEntries));
PortabilityLayer::MacFileProperties mfp;
if (fread(&entries[0], 1, sizeof(ASEntry) * numEntries, f) != sizeof(ASEntry) * numEntries)
{
fprintf(stderr, "Failed to read entries");
return -1;
}
for (const ASEntry &asEntry : entries)
{
int fseekResult = fseek(f, asEntry.m_offset, SEEK_SET);
if (fseekResult != 0)
return fseekResult;
int rc = 0;
switch (static_cast<uint32_t>(asEntry.m_entryID))
{
case 1:
if (asEntry.m_length > 0)
rc = ProcessFork(f, asEntry.m_length, outPath, ".gpd");
break;
case 2:
if (asEntry.m_length > 0)
rc = ProcessFork(f, asEntry.m_length, outPath, ".gpr");
break;
case 4:
if (asEntry.m_length > 0)
rc = ProcessFork(f, asEntry.m_length, outPath, ".gpc");
break;
case 8:
rc = ProcessFileDatesInfo(f, asEntry.m_length, mfp, ts);
break;
case 9:
rc = ProcessFinderInfo(f, asEntry.m_length, mfp);
break;
case 10:
rc = ProcessMacintoshFileInfo(f, asEntry.m_length, mfp);
break;
case 3: // Real name
case 5: // B&W icon
case 6: // Color icon
case 11: // ProDOS file info
case 12: // MS-DOS file info
case 13: // AFP short name
case 14: // AFP file info
case 15: // AFP directory ID
break;
default:
fprintf(stderr, "Unknown entry type %i", static_cast<int>(static_cast<uint32_t>(asEntry.m_entryID)));
return -1;
}
if (rc != 0)
return rc;
}
PortabilityLayer::MacFilePropertiesSerialized mfps;
mfps.Serialize(mfp);
std::string gpfPath = std::string(outPath) + ".gpf";
FILE *gpfFile = fopen_utf8(gpfPath.c_str(), "wb");
if (!gpfFile)
{
fprintf(stderr, "Failed to open output gpf");
return -1;
}
PortabilityLayer::CFileStream gpfStream(gpfFile);
mfps.WriteAsPackage(gpfStream, ts);
gpfStream.Close();
return 0;
}
int toolMain(int argc, const char **argv)
{
BEUInt32_t magic;
if (argc != 4)
{
fprintf(stderr, "Usage: ASADTool <input> <timestamp.ts> <output>");
return -1;
}
PortabilityLayer::CombinedTimestamp ts;
FILE *tsFile = fopen_utf8(argv[2], "rb");
if (!tsFile)
{
fprintf(stderr, "Could not open timestamp file");
return -1;
}
if (fread(&ts, 1, sizeof(ts), tsFile) != sizeof(ts))
{
fprintf(stderr, "Could not read timestamp file");
return -1;
}
fclose(tsFile);
FILE *asadFile = fopen_utf8(argv[1], "rb");
if (!asadFile)
{
fprintf(stderr, "Could not open input file");
return -1;
}
if (fread(&magic, 1, 4, asadFile) != 4)
{
fprintf(stderr, "Could not read file magic");
return -1;
}
int returnCode = 0;
if (magic == 0x00051607)
returnCode = ProcessFile(asadFile, argv[3], ts, true);
else if (magic == 0x00051600)
returnCode = ProcessFile(asadFile, argv[3], ts, false);
else
{
fprintf(stderr, "Unknown file type %x", static_cast<int>(magic));
return -1;
}
fclose(asadFile);
return returnCode;
}

96
ASADTool/ASADTool.vcxproj Normal file
View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{DF692F94-3A11-40E1-8846-9815B4DBBDB0}</ProjectGuid>
<RootNamespace>ASADTool</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\Debug.props" />
<Import Project="..\WindowsUnicodeToolShim.props" />
<Import Project="..\PortabilityLayer.props" />
<Import Project="..\GpCommon.props" />
<Import Project="..\Common.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\Release.props" />
<Import Project="..\WindowsUnicodeToolShim.props" />
<Import Project="..\PortabilityLayer.props" />
<Import Project="..\GpCommon.props" />
<Import Project="..\Common.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ProjectReference Include="..\PortabilityLayer\PortabilityLayer.vcxproj">
<Project>{6ec62b0f-9353-40a4-a510-3788f1368b33}</Project>
</ProjectReference>
<ProjectReference Include="..\WindowsUnicodeToolShim\WindowsUnicodeToolShim.vcxproj">
<Project>{15009625-1120-405e-8bba-69a16cd6713d}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ASADTool.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ASADTool.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@@ -13,8 +13,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GpApp", "GpApp\GpApp.vcxpro
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hqx2gp", "hqx2gp\hqx2gp.vcxproj", "{5FDE4822-C771-46A5-B6B2-FD12BACE86BF}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PictChecker", "PictChecker\PictChecker.vcxproj", "{99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GpAudioDriver_XAudio2", "GpAudioDriver_XAudio2\GpAudioDriver_XAudio2.vcxproj", "{E3BDC783-8646-433E-ADF0-8B6390D36669}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FTagData", "FTagData\FTagData.vcxproj", "{A8FCDC5E-729C-4A80-BF9F-B669C52B2AE3}"
@@ -43,6 +41,38 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MakeTimestamp", "MakeTimest
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "flattenmov", "flattenmov\flattenmov.vcxproj", "{89F8D13E-F216-4B67-8DE9-7F842D349E94}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unpacktool", "unpacktool\unpacktool.vcxproj", "{A778D062-DE76-49F6-8D05-EB26852DD605}"
EndProject
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ReleasePackageInstaller", "ReleasePackageInstaller\ReleasePackageInstaller.wixproj", "{D26BD501-28A7-4849-8130-FB5EA0A2B82F}"
ProjectSection(ProjectDependencies) = postProject
{7EFF1E21-C375-45EA-A069-4E2232C8A72B} = {7EFF1E21-C375-45EA-A069-4E2232C8A72B}
{B852D549-4020-4477-8BFB-E199FF78B047} = {B852D549-4020-4477-8BFB-E199FF78B047}
{2FF15659-5C72-48B8-B55B-3C658E4125B5} = {2FF15659-5C72-48B8-B55B-3C658E4125B5}
{3B7FD18D-7A50-4DF5-AC25-543E539BFACE} = {3B7FD18D-7A50-4DF5-AC25-543E539BFACE}
{B31BFF9D-2D14-4B1A-A625-8348CC3D8D67} = {B31BFF9D-2D14-4B1A-A625-8348CC3D8D67}
{36DAF5FA-6ADB-4F20-9810-1610DE0AE653} = {36DAF5FA-6ADB-4F20-9810-1610DE0AE653}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsUnicodeToolShim", "WindowsUnicodeToolShim\WindowsUnicodeToolShim.vcxproj", "{15009625-1120-405E-8BBA-69A16CD6713D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EmitWiXVersion", "EmitWiXVersion\EmitWiXVersion.vcxproj", "{7EFF1E21-C375-45EA-A069-4E2232C8A72B}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GpShell", "GpShell\GpShell.vcxproj", "{10CF9B5F-61D0-4B5B-89F4-810B58FC053D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GpFontHandler_FreeType2", "GpFontHandler_FreeType2\GpFontHandler_FreeType2.vcxproj", "{4B564030-8985-4975-91E1-E1B2C16AE2A1}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AerofoilSDL", "AerofoilSDL\AerofoilSDL.vcxproj", "{33542FF0-0473-4802-BC79-3B8261790F65}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MergeGPF", "MergeGPF\MergeGPF.vcxproj", "{36DAF5FA-6ADB-4F20-9810-1610DE0AE653}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GenerateFonts", "GenerateFonts\GenerateFonts.vcxproj", "{3B7FD18D-7A50-4DF5-AC25-543E539BFACE}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bin2h", "bin2h\bin2h.vcxproj", "{D045F28D-F245-44DD-B576-CC91BF3BE6E9}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HouseTool", "HouseTool\HouseTool.vcxproj", "{B31BFF9D-2D14-4B1A-A625-8348CC3D8D67}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ASADTool", "ASADTool\ASADTool.vcxproj", "{DF692F94-3A11-40E1-8846-9815B4DBBDB0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -69,10 +99,6 @@ Global
{5FDE4822-C771-46A5-B6B2-FD12BACE86BF}.Debug|x64.Build.0 = Debug|x64
{5FDE4822-C771-46A5-B6B2-FD12BACE86BF}.Release|x64.ActiveCfg = Release|x64
{5FDE4822-C771-46A5-B6B2-FD12BACE86BF}.Release|x64.Build.0 = Release|x64
{99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Debug|x64.ActiveCfg = Debug|x64
{99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Debug|x64.Build.0 = Debug|x64
{99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Release|x64.ActiveCfg = Release|x64
{99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Release|x64.Build.0 = Release|x64
{E3BDC783-8646-433E-ADF0-8B6390D36669}.Debug|x64.ActiveCfg = Debug|x64
{E3BDC783-8646-433E-ADF0-8B6390D36669}.Debug|x64.Build.0 = Debug|x64
{E3BDC783-8646-433E-ADF0-8B6390D36669}.Release|x64.ActiveCfg = Release|x64
@@ -129,6 +155,52 @@ Global
{89F8D13E-F216-4B67-8DE9-7F842D349E94}.Debug|x64.Build.0 = Debug|x64
{89F8D13E-F216-4B67-8DE9-7F842D349E94}.Release|x64.ActiveCfg = Release|x64
{89F8D13E-F216-4B67-8DE9-7F842D349E94}.Release|x64.Build.0 = Release|x64
{A778D062-DE76-49F6-8D05-EB26852DD605}.Debug|x64.ActiveCfg = Debug|x64
{A778D062-DE76-49F6-8D05-EB26852DD605}.Debug|x64.Build.0 = Debug|x64
{A778D062-DE76-49F6-8D05-EB26852DD605}.Release|x64.ActiveCfg = Release|x64
{A778D062-DE76-49F6-8D05-EB26852DD605}.Release|x64.Build.0 = Release|x64
{D26BD501-28A7-4849-8130-FB5EA0A2B82F}.Debug|x64.ActiveCfg = Debug|x64
{D26BD501-28A7-4849-8130-FB5EA0A2B82F}.Release|x64.ActiveCfg = Release|x64
{15009625-1120-405E-8BBA-69A16CD6713D}.Debug|x64.ActiveCfg = Debug|x64
{15009625-1120-405E-8BBA-69A16CD6713D}.Debug|x64.Build.0 = Debug|x64
{15009625-1120-405E-8BBA-69A16CD6713D}.Release|x64.ActiveCfg = Release|x64
{15009625-1120-405E-8BBA-69A16CD6713D}.Release|x64.Build.0 = Release|x64
{7EFF1E21-C375-45EA-A069-4E2232C8A72B}.Debug|x64.ActiveCfg = Debug|x64
{7EFF1E21-C375-45EA-A069-4E2232C8A72B}.Debug|x64.Build.0 = Debug|x64
{7EFF1E21-C375-45EA-A069-4E2232C8A72B}.Release|x64.ActiveCfg = Release|x64
{7EFF1E21-C375-45EA-A069-4E2232C8A72B}.Release|x64.Build.0 = Release|x64
{10CF9B5F-61D0-4B5B-89F4-810B58FC053D}.Debug|x64.ActiveCfg = Debug|x64
{10CF9B5F-61D0-4B5B-89F4-810B58FC053D}.Debug|x64.Build.0 = Debug|x64
{10CF9B5F-61D0-4B5B-89F4-810B58FC053D}.Release|x64.ActiveCfg = Release|x64
{10CF9B5F-61D0-4B5B-89F4-810B58FC053D}.Release|x64.Build.0 = Release|x64
{4B564030-8985-4975-91E1-E1B2C16AE2A1}.Debug|x64.ActiveCfg = Debug|x64
{4B564030-8985-4975-91E1-E1B2C16AE2A1}.Debug|x64.Build.0 = Debug|x64
{4B564030-8985-4975-91E1-E1B2C16AE2A1}.Release|x64.ActiveCfg = Release|x64
{4B564030-8985-4975-91E1-E1B2C16AE2A1}.Release|x64.Build.0 = Release|x64
{33542FF0-0473-4802-BC79-3B8261790F65}.Debug|x64.ActiveCfg = Debug|x64
{33542FF0-0473-4802-BC79-3B8261790F65}.Debug|x64.Build.0 = Debug|x64
{33542FF0-0473-4802-BC79-3B8261790F65}.Release|x64.ActiveCfg = Release|x64
{33542FF0-0473-4802-BC79-3B8261790F65}.Release|x64.Build.0 = Release|x64
{36DAF5FA-6ADB-4F20-9810-1610DE0AE653}.Debug|x64.ActiveCfg = Debug|x64
{36DAF5FA-6ADB-4F20-9810-1610DE0AE653}.Debug|x64.Build.0 = Debug|x64
{36DAF5FA-6ADB-4F20-9810-1610DE0AE653}.Release|x64.ActiveCfg = Release|x64
{36DAF5FA-6ADB-4F20-9810-1610DE0AE653}.Release|x64.Build.0 = Release|x64
{3B7FD18D-7A50-4DF5-AC25-543E539BFACE}.Debug|x64.ActiveCfg = Debug|x64
{3B7FD18D-7A50-4DF5-AC25-543E539BFACE}.Debug|x64.Build.0 = Debug|x64
{3B7FD18D-7A50-4DF5-AC25-543E539BFACE}.Release|x64.ActiveCfg = Release|x64
{3B7FD18D-7A50-4DF5-AC25-543E539BFACE}.Release|x64.Build.0 = Release|x64
{D045F28D-F245-44DD-B576-CC91BF3BE6E9}.Debug|x64.ActiveCfg = Debug|x64
{D045F28D-F245-44DD-B576-CC91BF3BE6E9}.Debug|x64.Build.0 = Debug|x64
{D045F28D-F245-44DD-B576-CC91BF3BE6E9}.Release|x64.ActiveCfg = Release|x64
{D045F28D-F245-44DD-B576-CC91BF3BE6E9}.Release|x64.Build.0 = Release|x64
{B31BFF9D-2D14-4B1A-A625-8348CC3D8D67}.Debug|x64.ActiveCfg = Debug|x64
{B31BFF9D-2D14-4B1A-A625-8348CC3D8D67}.Debug|x64.Build.0 = Debug|x64
{B31BFF9D-2D14-4B1A-A625-8348CC3D8D67}.Release|x64.ActiveCfg = Release|x64
{B31BFF9D-2D14-4B1A-A625-8348CC3D8D67}.Release|x64.Build.0 = Release|x64
{DF692F94-3A11-40E1-8846-9815B4DBBDB0}.Debug|x64.ActiveCfg = Debug|x64
{DF692F94-3A11-40E1-8846-9815B4DBBDB0}.Debug|x64.Build.0 = Debug|x64
{DF692F94-3A11-40E1-8846-9815B4DBBDB0}.Release|x64.ActiveCfg = Release|x64
{DF692F94-3A11-40E1-8846-9815B4DBBDB0}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -1,14 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
@@ -22,32 +14,19 @@
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{0E383EF0-CEF7-4733-87C6-5AC9844AA1EF}</ProjectGuid>
<RootNamespace>Aerofoil</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
@@ -56,33 +35,15 @@
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\PortabilityLayer.props" />
<Import Project="..\Common.props" />
<Import Project="..\GpCommon.props" />
<Import Project="..\GpMainApp.props" />
<Import Project="..\FreeTypePublic.props" />
<Import Project="..\FreeTypeImport.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\PortabilityLayer.props" />
<Import Project="..\Common.props" />
<Import Project="..\GpCommon.props" />
<Import Project="..\GpMainApp.props" />
<Import Project="..\FreeTypePublic.props" />
<Import Project="..\FreeTypeImport.props" />
<Import Project="..\Release.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\PortabilityLayer.props" />
<Import Project="..\Common.props" />
<Import Project="..\GpCommon.props" />
<Import Project="..\GpMainApp.props" />
<Import Project="..\FreeTypePublic.props" />
<Import Project="..\FreeTypeImport.props" />
<Import Project="..\GpShell.props" />
<Import Project="..\Debug.props" />
<Import Project="..\AerofoilPortable.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
@@ -90,9 +51,9 @@
<Import Project="..\Common.props" />
<Import Project="..\GpCommon.props" />
<Import Project="..\GpMainApp.props" />
<Import Project="..\FreeTypePublic.props" />
<Import Project="..\FreeTypeImport.props" />
<Import Project="..\Release.props" />
<Import Project="..\GpShell.props" />
<Import Project="..\AerofoilPortable.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
@@ -107,28 +68,6 @@
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
@@ -145,68 +84,42 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="GpAppEnvironment.cpp" />
<ClCompile Include="GpAudioDriverFactory.cpp" />
<ClCompile Include="..\AerofoilPortable\GpAllocator_C.cpp" />
<ClCompile Include="GpBWCursor_Win32.cpp" />
<ClCompile Include="GpColorCursor_Win32.cpp" />
<ClCompile Include="GpDisplayDriverFactory.cpp" />
<ClCompile Include="GpFiber_Win32.cpp" />
<ClCompile Include="GpFileStream_Win32.cpp" />
<ClCompile Include="GpFileSystem_Win32.cpp" />
<ClCompile Include="GpFontHandlerFactory.cpp" />
<ClCompile Include="GpFontHandler_FreeType2.cpp" />
<ClCompile Include="GpGlobalConfig.cpp" />
<ClCompile Include="GpInputDriverFactory.cpp" />
<ClCompile Include="GpMain.cpp" />
<ClCompile Include="GpLogDriver_Win32.cpp" />
<ClCompile Include="GpMain_Win32.cpp" />
<ClCompile Include="GpMemoryBuffer.cpp" />
<ClCompile Include="GpMutex_Win32.cpp" />
<ClCompile Include="GpSystemServices_Win32.cpp" />
<ClCompile Include="GpFiberStarter_Win32.cpp" />
<ClCompile Include="GpThreadEvent_Win32.cpp" />
<ClCompile Include="GpVOSEventQueue.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\GpCommon\EGpInputDriverType.h" />
<ClInclude Include="..\GpCommon\EGpStandardCursor.h" />
<ClInclude Include="..\GpCommon\GpApplicationName.h" />
<ClInclude Include="..\GpCommon\GpBuildVersion.h" />
<ClInclude Include="..\GpCommon\GpDisplayDriverTickStatus.h" />
<ClInclude Include="..\GpCommon\GpFileCreationDisposition.h" />
<ClInclude Include="..\GpCommon\GpInputDriverProperties.h" />
<ClInclude Include="..\GpCommon\GpString.h" />
<ClInclude Include="..\GpCommon\GpVector.h" />
<ClInclude Include="..\GpCommon\IGpCursor.h" />
<ClInclude Include="..\GpCommon\IGpAudioChannelCallbacks.h" />
<ClInclude Include="..\GpCommon\GpColorCursor_Win32.h" />
<ClInclude Include="..\GpCommon\IGpDisplayDriverSurface.h" />
<ClInclude Include="GpAppEnvironment.h" />
<ClInclude Include="GpAudioDriverFactory.h" />
<ClInclude Include="GpComPtr.h" />
<ClInclude Include="GpCoreDefs.h" />
<ClInclude Include="GpDisplayDriverFactory.h" />
<ClInclude Include="GpFiber.h" />
<ClInclude Include="GpFiber_Win32.h" />
<ClInclude Include="..\GpCommon\IGpLogDriver.h" />
<ClInclude Include="..\GpCommon\IGpPrefsHandler.h" />
<ClInclude Include="GpBWCursor_Win32.h" />
<ClInclude Include="GpFileStream_Win32.h" />
<ClInclude Include="GpFileSystem_Win32.h" />
<ClInclude Include="GpFontHandler_FreeType2.h" />
<ClInclude Include="GpFontHandlerFactory.h" />
<ClInclude Include="GpGlobalConfig.h" />
<ClInclude Include="GpInputDriverFactory.h" />
<ClInclude Include="GpMain.h" />
<ClInclude Include="GpMemoryBuffer.h" />
<ClInclude Include="GpLogDriver_Win32.h" />
<ClInclude Include="GpMutex_Win32.h" />
<ClInclude Include="GpRingBuffer.h" />
<ClInclude Include="GpSystemServices_Win32.h" />
<ClInclude Include="GpFiberStarter.h" />
<ClInclude Include="GpThreadEvent_Win32.h" />
<ClInclude Include="GpVOSEventQueue.h" />
<ClInclude Include="GpWindows.h" />
<ClInclude Include="IGpAudioChannel.h" />
<ClInclude Include="IGpAudioDriver.h" />
<ClInclude Include="IGpDisplayDriver.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FreeType\FreeType.vcxproj">
<Project>{487216d8-16ba-4b4c-b5bf-43feedfee03a}</Project>
</ProjectReference>
<ProjectReference Include="..\GpApp\GpApp.vcxproj">
<Project>{6233c3f2-5781-488e-b190-4fa8836f5a77}</Project>
</ProjectReference>
@@ -219,6 +132,9 @@
<ProjectReference Include="..\GpInputDriver_XInput\GpInputDriver_XInput.vcxproj">
<Project>{17b96f07-ef92-47cd-95a5-8e6ee38ab564}</Project>
</ProjectReference>
<ProjectReference Include="..\GpShell\GpShell.vcxproj">
<Project>{10cf9b5f-61d0-4b5b-89f4-810b58fc053d}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Aerofoil.rc" />

View File

@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="GpColorCursor_Win32.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GpFileStream_Win32.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GpFileSystem_Win32.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GpMain_Win32.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GpMutex_Win32.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GpSystemServices_Win32.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GpThreadEvent_Win32.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GpLogDriver_Win32.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GpBWCursor_Win32.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\AerofoilPortable\GpAllocator_C.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\GpCommon\EGpInputDriverType.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\GpCommon\EGpStandardCursor.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\GpCommon\GpApplicationName.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\GpCommon\GpDisplayDriverTickStatus.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\GpCommon\GpFileCreationDisposition.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GpFileStream_Win32.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GpFileSystem_Win32.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\GpCommon\GpInputDriverProperties.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GpMutex_Win32.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GpSystemServices_Win32.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GpThreadEvent_Win32.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\GpCommon\IGpAudioChannelCallbacks.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\GpCommon\IGpCursor.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\GpCommon\IGpDisplayDriverSurface.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Resource Files</Filter>
</ClInclude>
<ClInclude Include="..\GpCommon\IGpLogDriver.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GpLogDriver_Win32.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\GpCommon\IGpPrefsHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\GpCommon\GpBuildVersion.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GpBWCursor_Win32.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\GpCommon\GpVector.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\GpCommon\GpString.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="ConvertedResources\Large128.ico">
<Filter>Resource Files</Filter>
</Image>
<Image Include="ConvertedResources\Small128.ico">
<Filter>Resource Files</Filter>
</Image>
</ItemGroup>
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{0db467fa-83af-4c89-b36b-2478899f4f9e}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{bdb8c57b-c9f7-443a-be30-89718b8ca3e5}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{8ed8ebed-2aea-4f6d-8f2f-c18a64eb6e20}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Aerofoil.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@@ -1,181 +0,0 @@
#include "GpAppEnvironment.h"
#include "GpFiberStarter.h"
#include "GpAppInterface.h"
#include "GpDisplayDriverTickStatus.h"
#include "GpFontHandlerFactory.h"
#include "HostSuspendCallArgument.h"
#include "IGpDisplayDriver.h"
#include "IGpFiber.h"
#include "IGpInputDriver.h"
#include <assert.h>
GpAppEnvironment::GpAppEnvironment()
: m_applicationState(ApplicationState_NotStarted)
, m_displayDriver(nullptr)
, m_audioDriver(nullptr)
, m_inputDrivers(nullptr)
, m_numInputDrivers(0)
, m_fontHandler(nullptr)
, m_vosEventQueue(nullptr)
, m_applicationFiber(nullptr)
, m_vosFiber(nullptr)
, m_suspendCallID(PortabilityLayer::HostSuspendCallID_Unknown)
, m_suspendArgs(nullptr)
, m_suspendReturnValue(nullptr)
{
}
GpAppEnvironment::~GpAppEnvironment()
{
assert(m_applicationFiber == nullptr);
}
void GpAppEnvironment::Init()
{
}
GpDisplayDriverTickStatus_t GpAppEnvironment::Tick(IGpFiber *vosFiber)
{
GpAppInterface_Get()->PL_IncrementTickCounter(1);
m_vosFiber = vosFiber;
if (m_applicationState == ApplicationState_WaitingForEvents)
m_applicationState = ApplicationState_Running;
for (;;)
{
switch (m_applicationState)
{
case ApplicationState_NotStarted:
InitializeApplicationState();
m_applicationFiber = GpFiberStarter::StartFiber(GpAppEnvironment::StaticAppThreadFunc, this, vosFiber);
m_applicationState = ApplicationState_Running;
break;
case ApplicationState_WaitingForEvents:
return GpDisplayDriverTickStatuses::kOK;
case ApplicationState_Running:
SynchronizeState();
m_applicationFiber->YieldTo();
break;
case ApplicationState_SystemCall:
{
PortabilityLayer::HostSuspendCallID callID = m_suspendCallID;
const PortabilityLayer::HostSuspendCallArgument *args = m_suspendArgs;
PortabilityLayer::HostSuspendCallArgument *returnValue = m_suspendReturnValue;
DispatchSystemCall(callID, args, returnValue);
assert(m_applicationState != ApplicationState_SystemCall);
}
break;
case ApplicationState_TimedSuspend:
if (m_delaySuspendTicks == 0)
m_applicationState = ApplicationState_Running;
else
{
m_delaySuspendTicks--;
return GpDisplayDriverTickStatuses::kOK;
}
break;
case ApplicationState_Terminated:
m_applicationFiber->Destroy();
m_applicationFiber = nullptr;
return GpDisplayDriverTickStatuses::kApplicationTerminated;
default:
assert(false);
break;
};
}
}
void GpAppEnvironment::Render()
{
GpAppInterface_Get()->PL_Render(m_displayDriver);
}
void GpAppEnvironment::AdjustRequestedResolution(unsigned int &width, unsigned int &height)
{
GpAppInterface_Get()->PL_AdjustRequestedResolution(width, height);
}
void GpAppEnvironment::SetDisplayDriver(IGpDisplayDriver *displayDriver)
{
m_displayDriver = displayDriver;
}
void GpAppEnvironment::SetAudioDriver(IGpAudioDriver *audioDriver)
{
m_audioDriver = audioDriver;
}
void GpAppEnvironment::SetInputDrivers(IGpInputDriver *const* inputDrivers, size_t numDrivers)
{
m_inputDrivers = inputDrivers;
m_numInputDrivers = numDrivers;
}
void GpAppEnvironment::SetFontHandler(PortabilityLayer::HostFontHandler *fontHandler)
{
m_fontHandler = fontHandler;
}
void GpAppEnvironment::SetVOSEventQueue(GpVOSEventQueue *eventQueue)
{
m_vosEventQueue = eventQueue;
}
void GpAppEnvironment::StaticAppThreadFunc(void *context)
{
static_cast<GpAppEnvironment*>(context)->AppThreadFunc();
}
void GpAppEnvironment::AppThreadFunc()
{
GpAppInterface_Get()->ApplicationMain();
m_applicationState = ApplicationState_Terminated;
m_vosFiber->YieldTo();
}
void GpAppEnvironment::InitializeApplicationState()
{
GpAppInterface_Get()->PL_HostDisplayDriver_SetInstance(m_displayDriver);
GpAppInterface_Get()->PL_HostAudioDriver_SetInstance(m_audioDriver);
GpAppInterface_Get()->PL_InstallHostSuspendHook(GpAppEnvironment::StaticSuspendHookFunc, this);
GpAppInterface_Get()->PL_HostFontHandler_SetInstance(m_fontHandler);
GpAppInterface_Get()->PL_HostVOSEventQueue_SetInstance(m_vosEventQueue);
}
void GpAppEnvironment::SynchronizeState()
{
const size_t numInputDrivers = m_numInputDrivers;
for (size_t i = 0; i < numInputDrivers; i++)
m_inputDrivers[i]->ProcessInput();
}
void GpAppEnvironment::StaticSuspendHookFunc(void *context, PortabilityLayer::HostSuspendCallID callID, const PortabilityLayer::HostSuspendCallArgument *args, PortabilityLayer::HostSuspendCallArgument *returnValue)
{
GpAppEnvironment *appEnv = static_cast<GpAppEnvironment*>(context);
appEnv->m_suspendCallID = callID;
appEnv->m_suspendArgs = args;
appEnv->m_suspendReturnValue = returnValue;
appEnv->m_applicationState = ApplicationState_SystemCall;
appEnv->m_vosFiber->YieldTo();
}
void GpAppEnvironment::DispatchSystemCall(PortabilityLayer::HostSuspendCallID callID, const PortabilityLayer::HostSuspendCallArgument *args, PortabilityLayer::HostSuspendCallArgument *returnValue)
{
switch (callID)
{
case PortabilityLayer::HostSuspendCallID_Delay:
m_applicationState = ApplicationState_TimedSuspend;
m_delaySuspendTicks = args[0].m_uint;
break;
default:
assert(false);
}
}

View File

@@ -0,0 +1,113 @@
/*
Portions of this file based on Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "GpBWCursor_Win32.h"
#include "GpWindows.h"
#include "IGpAllocator.h"
#include <stdint.h>
#include <stdlib.h>
#include <new>
#include <algorithm>
extern GpWindowsGlobals g_gpWindowsGlobals;
void GpBWCursor_Win32::Destroy()
{
this->DecRef();
}
IGpCursor_Win32 *GpBWCursor_Win32::Create(IGpAllocator *alloc, size_t width, size_t height, const void *pixelData, const void *maskData, size_t hotSpotX, size_t hotSpotY)
{
size_t numBits = width * height;
size_t numBytes = (numBits + 7) / 8;
uint8_t *convertedAndData = static_cast<uint8_t*>(alloc->Alloc(numBytes));
uint8_t *convertedXorData = static_cast<uint8_t*>(alloc->Alloc(numBytes));
if (!convertedAndData || !convertedXorData)
{
if (convertedAndData)
alloc->Release(convertedAndData);
if (convertedXorData)
alloc->Release(convertedXorData);
return nullptr;
}
for (size_t i = 0; i < numBytes; i++)
{
// MacPx0 MacPx1
// MacMask0 1a 0x 1a 1x
// MacMask1 0a 1x 0a 0x
convertedAndData[i] = static_cast<const uint8_t*>(maskData)[i] ^ 0xff;
convertedXorData[i] = static_cast<const uint8_t*>(maskData)[i] ^ static_cast<const uint8_t*>(pixelData)[i];
}
HCURSOR hcursor = CreateCursor(g_gpWindowsGlobals.m_hInstance, static_cast<int>(hotSpotX), static_cast<int>(hotSpotY), static_cast<int>(width), static_cast<int>(height), convertedAndData, convertedXorData);
alloc->Release(convertedAndData);
alloc->Release(convertedXorData);
if (!hcursor)
return nullptr;
void *storage = alloc->Alloc(sizeof(GpBWCursor_Win32));
if (!storage)
{
DestroyCursor(hcursor);
return nullptr;
}
return new (storage) GpBWCursor_Win32(hcursor, alloc);
}
GpBWCursor_Win32::GpBWCursor_Win32(HCURSOR cursor, IGpAllocator *alloc)
: m_cursor(cursor)
, m_refCount(1)
, m_alloc(alloc)
{
}
GpBWCursor_Win32::~GpBWCursor_Win32()
{
DestroyCursor(m_cursor);
}
const HCURSOR &GpBWCursor_Win32::GetHCursor() const
{
return m_cursor;
}
void GpBWCursor_Win32::IncRef()
{
m_refCount++;
}
void GpBWCursor_Win32::DecRef()
{
m_refCount--;
if (m_refCount == 0)
{
IGpAllocator *alloc = m_alloc;
this->~GpBWCursor_Win32();
alloc->Release(this);
}
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include "IGpCursor_Win32.h"
#include "GpWindows.h"
struct IGpAllocator;
class GpBWCursor_Win32 final : public IGpCursor_Win32
{
public:
void Destroy() override;
const HCURSOR &GetHCursor() const override;
void IncRef() override;
void DecRef() override;
static IGpCursor_Win32 *Create(IGpAllocator *alloc, size_t width, size_t height, const void *pixelData, const void *maskData, size_t hotSpotX, size_t hotSpotY);
private:
GpBWCursor_Win32(HCURSOR cursor, IGpAllocator *alloc);
~GpBWCursor_Win32();
HCURSOR m_cursor;
int m_refCount;
IGpAllocator *m_alloc;
};

View File

@@ -1,58 +1,137 @@
#include "GpCursor_Win32.h"
/*
Portions of this file based on Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "GpColorCursor_Win32.h"
#include "IGpAllocator.h"
#include <stdint.h>
#include <stdlib.h>
#include <new>
#include <algorithm>
void GpCursor_Win32::Destroy()
void GpColorCursor_Win32::Destroy()
{
this->DecRef();
}
IGpCursor_Win32 *GpCursor_Win32::Load(const wchar_t *path)
IGpCursor_Win32 *GpColorCursor_Win32::Create(IGpAllocator *alloc, size_t width, size_t height, const void *pixelDataRGBA, size_t hotSpotX, size_t hotSpotY)
{
HANDLE imageH = LoadImageW(nullptr, path, IMAGE_CURSOR, 0, 0, LR_LOADFROMFILE);
const size_t paddingBits = (sizeof(void*) * 8);
if (imageH == nullptr)
BITMAPV4HEADER bmp;
memset(&bmp, 0, sizeof(bmp));
bmp.bV4Size = sizeof(bmp);
bmp.bV4Width = width;
bmp.bV4Height = -static_cast<LONG>(height);
bmp.bV4Planes = 1;
bmp.bV4BitCount = 32;
bmp.bV4V4Compression = BI_BITFIELDS;
bmp.bV4AlphaMask = 0xFF000000;
bmp.bV4RedMask = 0x00FF0000;
bmp.bV4GreenMask = 0x0000FF00;
bmp.bV4BlueMask = 0x000000FF;
size_t maskPitch = width + paddingBits - 1;
maskPitch -= maskPitch % paddingBits;
LPVOID maskBits = alloc->Alloc(maskPitch * height);
if (!maskBits)
return nullptr;
HCURSOR cursor = reinterpret_cast<HCURSOR>(imageH);
void *storage = malloc(sizeof(GpCursor_Win32));
memset(maskBits, 0xff, maskPitch * height);
HDC hdc = GetDC(NULL);
LPVOID pixels;
ICONINFO ii;
memset(&ii, 0, sizeof(ii));
ii.fIcon = FALSE;
ii.xHotspot = (DWORD)hotSpotX;
ii.yHotspot = (DWORD)hotSpotY;
ii.hbmColor = CreateDIBSection(hdc, (BITMAPINFO*)&bmp, DIB_RGB_COLORS, &pixels, NULL, 0);
ii.hbmMask = CreateBitmap(width, height, 1, 1, maskBits);
ReleaseDC(NULL, hdc);
alloc->Release(maskBits);
size_t cursorPitch = width * 4;
size_t numPixels = width * height;
memcpy(pixels, pixelDataRGBA, numPixels * 4);
for (size_t i = 0; i < numPixels; i++)
{
uint8_t *pixel = static_cast<uint8_t*>(pixels) + i * 4;
std::swap(pixel[0], pixel[2]);
}
HICON hicon = CreateIconIndirect(&ii);
DeleteObject(ii.hbmColor);
DeleteObject(ii.hbmMask);
if (!hicon)
return nullptr;
void *storage = alloc->Alloc(sizeof(GpColorCursor_Win32));
if (!storage)
{
DestroyCursor(cursor);
DestroyIcon(hicon);
return nullptr;
}
return new (storage) GpCursor_Win32(reinterpret_cast<HCURSOR>(cursor));
return new (storage) GpColorCursor_Win32(alloc, reinterpret_cast<HCURSOR>(hicon));
}
GpCursor_Win32::GpCursor_Win32(HCURSOR cursor)
GpColorCursor_Win32::GpColorCursor_Win32(IGpAllocator *alloc, HCURSOR cursor)
: m_cursor(cursor)
, m_refCount(1)
, m_alloc(alloc)
{
}
GpCursor_Win32::~GpCursor_Win32()
GpColorCursor_Win32::~GpColorCursor_Win32()
{
DestroyCursor(m_cursor);
DestroyIcon(m_cursor);
}
const HCURSOR &GpCursor_Win32::GetHCursor() const
const HCURSOR &GpColorCursor_Win32::GetHCursor() const
{
return m_cursor;
}
void GpCursor_Win32::IncRef()
void GpColorCursor_Win32::IncRef()
{
m_refCount++;
}
void GpCursor_Win32::DecRef()
void GpColorCursor_Win32::DecRef()
{
m_refCount--;
if (m_refCount == 0)
{
this->~GpCursor_Win32();
free(this);
}
{
IGpAllocator *alloc = m_alloc;
this->~GpColorCursor_Win32();
alloc->Release(this);
}
}

View File

@@ -1,18 +1,11 @@
#pragma once
#include "IGpColorCursor.h"
#include "IGpCursor_Win32.h"
#include "GpWindows.h"
struct IGpColorCursor_Win32 : public IGpColorCursor
{
virtual const HCURSOR &GetHCursor() const = 0;
struct IGpAllocator;
virtual void IncRef() = 0;
virtual void DecRef() = 0;
};
class GpColorCursor_Win32 final : public IGpColorCursor_Win32
class GpColorCursor_Win32 final : public IGpCursor_Win32
{
public:
void Destroy() override;
@@ -22,12 +15,13 @@ public:
void IncRef() override;
void DecRef() override;
static IGpColorCursor_Win32 *Load(const wchar_t *path);
static IGpCursor_Win32 *Create(IGpAllocator *alloc, size_t width, size_t height, const void *pixelDataRGBA, size_t hotSpotX, size_t hotSpotY);
private:
GpColorCursor_Win32(HCURSOR cursor);
GpColorCursor_Win32(IGpAllocator *alloc, HCURSOR cursor);
~GpColorCursor_Win32();
HCURSOR m_cursor;
int m_refCount;
IGpAllocator *m_alloc;
};

View File

@@ -1,11 +0,0 @@
#pragma once
struct IGpFiber;
class GpFiberStarter
{
public:
typedef void(*ThreadFunc_t)(void *context);
static IGpFiber *StartFiber(ThreadFunc_t threadFunc, void *context, IGpFiber *creatingFiber);
};

View File

@@ -1,56 +0,0 @@
#include "GpFiberStarter.h"
#include "GpFiber_Win32.h"
#include "GpWindows.h"
#include <assert.h>
namespace GpFiberStarter_Win32
{
struct FiberStartState
{
GpFiberStarter::ThreadFunc_t m_threadFunc;
IGpFiber *m_creatingFiber;
void *m_context;
};
static VOID WINAPI FiberStartRoutine(LPVOID lpThreadParameter)
{
const FiberStartState *tss = static_cast<const FiberStartState*>(lpThreadParameter);
GpFiberStarter::ThreadFunc_t threadFunc = tss->m_threadFunc;
IGpFiber *creatingFiber = tss->m_creatingFiber;
void *context = tss->m_context;
creatingFiber->YieldTo();
threadFunc(context);
assert(!"Fiber function exited");
}
}
IGpFiber *GpFiberStarter::StartFiber(ThreadFunc_t threadFunc, void *context, IGpFiber *creatingFiber)
{
ULONG_PTR lowLimit;
ULONG_PTR highLimit;
#if 0
GetCurrentThreadStackLimits(&lowLimit, &highLimit);
ULONG_PTR stackSize = highLimit - lowLimit;
#else
ULONG_PTR stackSize = 1024 * 1024;
#endif
GpFiberStarter_Win32::FiberStartState startState;
startState.m_context = context;
startState.m_creatingFiber = creatingFiber;
startState.m_threadFunc = threadFunc;
void *fiber = CreateFiber(static_cast<SIZE_T>(stackSize), GpFiberStarter_Win32::FiberStartRoutine, &startState);
if (!fiber)
return nullptr;
SwitchToFiber(fiber);
return GpFiber_Win32::Create(fiber);
}

View File

@@ -1,32 +0,0 @@
#include "GpFiber_Win32.h"
#include <new>
GpFiber_Win32::GpFiber_Win32(LPVOID fiber)
: m_fiber(fiber)
{
}
void GpFiber_Win32::YieldTo()
{
SwitchToFiber(m_fiber);
}
void GpFiber_Win32::Destroy()
{
this->~GpFiber_Win32();
free(this);
}
GpFiber_Win32::~GpFiber_Win32()
{
DeleteFiber(m_fiber);
}
IGpFiber *GpFiber_Win32::Create(LPVOID fiber)
{
void *storage = malloc(sizeof(GpFiber_Win32));
if (!storage)
return nullptr;
return new (storage) GpFiber_Win32(fiber);
}

View File

@@ -1,18 +0,0 @@
#pragma once
#include "GpWindows.h"
#include "IGpFiber.h"
class GpFiber_Win32 final : public IGpFiber
{
public:
void YieldTo() override;
void Destroy() override;
static IGpFiber *Create(LPVOID fiber);
private:
explicit GpFiber_Win32(LPVOID fiber);
~GpFiber_Win32();
LPVOID m_fiber;
};

View File

@@ -1,144 +1,153 @@
#include "GpFileStream_Win32.h"
GpFileStream_Win32::GpFileStream_Win32(HANDLE handle, bool readable, bool writeable, bool seekable)
: m_handle(handle)
, m_readable(readable)
, m_writeable(writeable)
, m_seekable(seekable)
{
}
size_t GpFileStream_Win32::Read(void *bytesOut, size_t size)
{
if (!m_readable)
return 0;
size_t totalRead = 0;
while (size)
{
const DWORD chunkSizeToRead = (size > MAXDWORD) ? MAXDWORD : size;
DWORD numRead = 0;
BOOL readSucceeded = ReadFile(m_handle, bytesOut, chunkSizeToRead, &numRead, nullptr);
if (!readSucceeded)
return totalRead;
totalRead += static_cast<size_t>(numRead);
size -= static_cast<size_t>(numRead);
bytesOut = static_cast<void*>(static_cast<uint8_t*>(bytesOut) + numRead);
if (numRead != chunkSizeToRead)
return totalRead;
}
return totalRead;
}
size_t GpFileStream_Win32::Write(const void *bytes, size_t size)
{
if (!m_writeable)
return 0;
size_t totalWritten = 0;
while (size)
{
const DWORD chunkSizeToWrite = (size > MAXDWORD) ? MAXDWORD : size;
DWORD numWritten = 0;
BOOL writeSucceeded = WriteFile(m_handle, bytes, chunkSizeToWrite, &numWritten, nullptr);
#include "GpFileStream_Win32.h"
#include "IGpAllocator.h"
#include <new>
GpFileStream_Win32::GpFileStream_Win32(IGpAllocator *alloc, HANDLE handle, bool readable, bool writeable, bool seekable)
: m_alloc(alloc)
, m_handle(handle)
, m_readable(readable)
, m_writeable(writeable)
, m_seekable(seekable)
{
}
GpFileStream_Win32 *GpFileStream_Win32::Create(IGpAllocator *alloc, HANDLE handle, bool readable, bool writeable, bool seekable)
{
void *storage = alloc->Alloc(sizeof(GpFileStream_Win32));
if (!storage)
return nullptr;
return new (storage) GpFileStream_Win32(alloc, handle, readable, writeable, seekable);
}
GpFileStream_Win32::~GpFileStream_Win32()
{
CloseHandle(m_handle);
}
size_t GpFileStream_Win32::Read(void *bytesOut, size_t size)
{
if (!m_readable)
return 0;
size_t totalRead = 0;
while (size)
{
const DWORD chunkSizeToRead = (size > MAXDWORD) ? MAXDWORD : size;
DWORD numRead = 0;
BOOL readSucceeded = ReadFile(m_handle, bytesOut, chunkSizeToRead, &numRead, nullptr);
if (!readSucceeded)
return totalRead;
totalRead += static_cast<size_t>(numRead);
size -= static_cast<size_t>(numRead);
bytesOut = static_cast<void*>(static_cast<uint8_t*>(bytesOut) + numRead);
if (numRead != chunkSizeToRead)
return totalRead;
}
return totalRead;
}
size_t GpFileStream_Win32::Write(const void *bytes, size_t size)
{
if (!m_writeable)
return 0;
size_t totalWritten = 0;
while (size)
{
const DWORD chunkSizeToWrite = (size > MAXDWORD) ? MAXDWORD : size;
DWORD numWritten = 0;
BOOL writeSucceeded = WriteFile(m_handle, bytes, chunkSizeToWrite, &numWritten, nullptr);
if (!writeSucceeded)
{
DWORD lastError = GetLastError();
DWORD lastError = GetLastError();
return totalWritten;
}
totalWritten += static_cast<size_t>(numWritten);
size -= static_cast<size_t>(numWritten);
bytes = static_cast<const void*>(static_cast<const uint8_t*>(bytes) + numWritten);
if (numWritten != chunkSizeToWrite)
return totalWritten;
}
return totalWritten;
}
bool GpFileStream_Win32::IsSeekable() const
{
return m_seekable;
}
bool GpFileStream_Win32::IsReadOnly() const
{
return !m_writeable;
}
bool GpFileStream_Win32::IsWriteOnly() const
{
return !m_readable;
}
bool GpFileStream_Win32::SeekStart(PortabilityLayer::UFilePos_t loc)
{
LARGE_INTEGER li;
li.QuadPart = static_cast<LONGLONG>(loc);
return SetFilePointerEx(m_handle, li, nullptr, FILE_BEGIN) != 0;
}
bool GpFileStream_Win32::SeekCurrent(PortabilityLayer::FilePos_t loc)
{
LARGE_INTEGER li;
li.QuadPart = static_cast<LONGLONG>(loc);
return SetFilePointerEx(m_handle, li, nullptr, FILE_CURRENT) != 0;
}
bool GpFileStream_Win32::SeekEnd(PortabilityLayer::UFilePos_t loc)
{
LARGE_INTEGER li;
li.QuadPart = -static_cast<LONGLONG>(loc);
return SetFilePointerEx(m_handle, li, nullptr, FILE_END) != 0;
}
bool GpFileStream_Win32::Truncate(PortabilityLayer::UFilePos_t loc)
{
if (!m_writeable)
return false;
PortabilityLayer::UFilePos_t oldPos = Tell();
if (!SeekStart(loc))
return false;
if (!SetEndOfFile(m_handle))
return false;
if (!SeekStart(oldPos))
return false;
return true;
}
PortabilityLayer::UFilePos_t GpFileStream_Win32::Size() const
{
LARGE_INTEGER fsize;
if (!GetFileSizeEx(m_handle, &fsize))
return 0;
return static_cast<PortabilityLayer::UFilePos_t>(fsize.QuadPart);
}
PortabilityLayer::UFilePos_t GpFileStream_Win32::Tell() const
{
LARGE_INTEGER zero;
zero.QuadPart = 0;
LARGE_INTEGER fpos;
if (!SetFilePointerEx(m_handle, zero, &fpos, FILE_CURRENT))
return 0;
return static_cast<PortabilityLayer::UFilePos_t>(fpos.QuadPart);
}
void GpFileStream_Win32::Close()
{
CloseHandle(m_handle);
}
}
totalWritten += static_cast<size_t>(numWritten);
size -= static_cast<size_t>(numWritten);
bytes = static_cast<const void*>(static_cast<const uint8_t*>(bytes) + numWritten);
if (numWritten != chunkSizeToWrite)
return totalWritten;
}
return totalWritten;
}
bool GpFileStream_Win32::IsSeekable() const
{
return m_seekable;
}
bool GpFileStream_Win32::IsReadOnly() const
{
return !m_writeable;
}
bool GpFileStream_Win32::IsWriteOnly() const
{
return !m_readable;
}
bool GpFileStream_Win32::SeekStart(GpUFilePos_t loc)
{
LARGE_INTEGER li;
li.QuadPart = static_cast<LONGLONG>(loc);
return SetFilePointerEx(m_handle, li, nullptr, FILE_BEGIN) != 0;
}
bool GpFileStream_Win32::SeekCurrent(GpFilePos_t loc)
{
LARGE_INTEGER li;
li.QuadPart = static_cast<LONGLONG>(loc);
return SetFilePointerEx(m_handle, li, nullptr, FILE_CURRENT) != 0;
}
bool GpFileStream_Win32::SeekEnd(GpUFilePos_t loc)
{
LARGE_INTEGER li;
li.QuadPart = -static_cast<LONGLONG>(loc);
return SetFilePointerEx(m_handle, li, nullptr, FILE_END) != 0;
}
GpUFilePos_t GpFileStream_Win32::Size() const
{
LARGE_INTEGER fsize;
if (!GetFileSizeEx(m_handle, &fsize))
return 0;
return static_cast<GpUFilePos_t>(fsize.QuadPart);
}
GpUFilePos_t GpFileStream_Win32::Tell() const
{
LARGE_INTEGER zero;
zero.QuadPart = 0;
LARGE_INTEGER fpos;
if (!SetFilePointerEx(m_handle, zero, &fpos, FILE_CURRENT))
return 0;
return static_cast<GpUFilePos_t>(fpos.QuadPart);
}
void GpFileStream_Win32::Close()
{
IGpAllocator *alloc = m_alloc;
this->~GpFileStream_Win32();
alloc->Release(this);
}
void GpFileStream_Win32::Flush()
{
FlushFileBuffers(m_handle);
}

View File

@@ -1,30 +1,39 @@
#pragma once
#include "GpCoreDefs.h"
#include "GpWindows.h"
#include "IOStream.h"
class GpFileStream_Win32 final : public PortabilityLayer::IOStream
{
public:
explicit GpFileStream_Win32(HANDLE handle, bool readable, bool writeable, bool seekable);
size_t Read(void *bytesOut, size_t size) override;
size_t Write(const void *bytes, size_t size) override;
bool IsSeekable() const override;
bool IsReadOnly() const override;
bool IsWriteOnly() const override;
bool SeekStart(PortabilityLayer::UFilePos_t loc) override;
bool SeekCurrent(PortabilityLayer::FilePos_t loc) override;
bool SeekEnd(PortabilityLayer::UFilePos_t loc) override;
bool Truncate(PortabilityLayer::UFilePos_t loc) override;
PortabilityLayer::UFilePos_t Size() const override;
PortabilityLayer::UFilePos_t Tell() const override;
void Close() override;
private:
HANDLE m_handle;
bool m_readable;
bool m_writeable;
bool m_seekable;
};
#pragma once
#include "GpCoreDefs.h"
#include "GpWindows.h"
#include "GpIOStream.h"
class GpFileStream_Win32 final : public GpIOStream
{
public:
~GpFileStream_Win32();
size_t Read(void *bytesOut, size_t size) override;
size_t Write(const void *bytes, size_t size) override;
bool IsSeekable() const override;
bool IsReadOnly() const override;
bool IsWriteOnly() const override;
bool SeekStart(GpUFilePos_t loc) override;
bool SeekCurrent(GpFilePos_t loc) override;
bool SeekEnd(GpUFilePos_t loc) override;
GpUFilePos_t Size() const override;
GpUFilePos_t Tell() const override;
void GP_ASYNCIFY_PARANOID_NAMED(Close)() override;
void Flush() override;
#if GP_ASYNCIFY_PARANOID
void Close();
#endif
static GpFileStream_Win32 *Create(IGpAllocator *alloc, HANDLE handle, bool readable, bool writeable, bool seekable);
private:
GpFileStream_Win32(IGpAllocator *alloc, HANDLE handle, bool readable, bool writeable, bool seekable);
IGpAllocator *m_alloc;
HANDLE m_handle;
bool m_readable;
bool m_writeable;
bool m_seekable;
};

View File

@@ -1,43 +1,49 @@
#include "GpFileSystem_Win32.h"
#include "GpAllocator_C.h"
#include "GpApplicationName.h"
#include "GpFileStream_Win32.h"
#include "GpWindows.h"
#include "GpMemoryBuffer.h"
#include "HostDirectoryCursor.h"
#include "IGpAllocator.h"
#include "IGpDirectoryCursor.h"
#include <string>
#include <Shlwapi.h>
#include <ShlObj.h>
#include <commdlg.h>
#include <assert.h>
class GpDirectoryCursor_Win32 final : public PortabilityLayer::HostDirectoryCursor
struct IGpAllocator;
extern GpWindowsGlobals g_gpWindowsGlobals;
class GpDirectoryCursor_Win32 final : public IGpDirectoryCursor
{
public:
static GpDirectoryCursor_Win32 *Create(const HANDLE &handle, const WIN32_FIND_DATAW &findData);
static GpDirectoryCursor_Win32 *Create(IGpAllocator *alloc, const HANDLE &handle, const WIN32_FIND_DATAW &findData);
bool GetNext(const char *&outFileName) override;
void Destroy() override;
private:
GpDirectoryCursor_Win32(const HANDLE &handle, const WIN32_FIND_DATAW &findData);
GpDirectoryCursor_Win32(IGpAllocator *alloc, const HANDLE &handle, const WIN32_FIND_DATAW &findData);
~GpDirectoryCursor_Win32();
IGpAllocator *m_alloc;
HANDLE m_handle;
WIN32_FIND_DATAW m_findData;
char m_chars[MAX_PATH + 1];
bool m_haveNext;
};
GpDirectoryCursor_Win32 *GpDirectoryCursor_Win32::Create(const HANDLE &handle, const WIN32_FIND_DATAW &findData)
GpDirectoryCursor_Win32 *GpDirectoryCursor_Win32::Create(IGpAllocator *alloc, const HANDLE &handle, const WIN32_FIND_DATAW &findData)
{
void *storage = malloc(sizeof(GpDirectoryCursor_Win32));
void *storage = alloc->Alloc(sizeof(GpDirectoryCursor_Win32));
if (!storage)
return nullptr;
return new (storage) GpDirectoryCursor_Win32(handle, findData);
return new (storage) GpDirectoryCursor_Win32(alloc, handle, findData);
}
bool GpDirectoryCursor_Win32::GetNext(const char *&outFileName)
@@ -81,14 +87,16 @@ bool GpDirectoryCursor_Win32::GetNext(const char *&outFileName)
void GpDirectoryCursor_Win32::Destroy()
{
IGpAllocator *alloc = m_alloc;
this->~GpDirectoryCursor_Win32();
free(this);
alloc->Release(this);
}
GpDirectoryCursor_Win32::GpDirectoryCursor_Win32(const HANDLE &handle, const WIN32_FIND_DATAW &findData)
GpDirectoryCursor_Win32::GpDirectoryCursor_Win32(IGpAllocator *alloc, const HANDLE &handle, const WIN32_FIND_DATAW &findData)
: m_handle(handle)
, m_findData(findData)
, m_haveNext(true)
, m_alloc(alloc)
{
}
@@ -97,41 +105,76 @@ GpDirectoryCursor_Win32::~GpDirectoryCursor_Win32()
FindClose(m_handle);
}
GpFileSystem_Win32::GpFileSystem_Win32()
GpFileSystem_Win32::GpFileSystem_Win32(IGpAllocator *alloc)
: m_alloc(alloc)
, m_prefsDir(alloc)
, m_scoresDir(alloc)
, m_packagedDir(alloc)
, m_housesDir(alloc)
, m_logsDir(alloc)
, m_userHousesDir(alloc)
, m_userSavesDir(alloc)
, m_resourcesDir(alloc)
, m_exportDir(alloc)
{
// GP TODO: This shouldn't be static init since it allocates memory
m_executablePath[0] = 0;
}
void GpFileSystem_Win32::Destroy()
{
IGpAllocator *alloc = m_alloc;
this->~GpFileSystem_Win32();
alloc->Release(this);
}
bool GpFileSystem_Win32::Init()
{
PWSTR docsPath;
if (!FAILED(SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_DEFAULT, nullptr, &docsPath)))
if (FAILED(SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_DEFAULT, nullptr, &docsPath)))
return false;
if (!m_prefsDir.Set(docsPath))
{
try
{
m_prefsDir = docsPath;
}
catch(...)
{
CoTaskMemFree(docsPath);
throw;
}
m_prefsDir.append(L"\\" GP_APPLICATION_NAME_W);
m_userHousesDir = m_prefsDir + L"\\Houses";
m_userSavesDir = m_prefsDir + L"\\SavedGames";
m_scoresDir = m_prefsDir + L"\\Scores";
CreateDirectoryW(m_prefsDir.c_str(), nullptr);
CreateDirectoryW(m_scoresDir.c_str(), nullptr);
CreateDirectoryW(m_userHousesDir.c_str(), nullptr);
CreateDirectoryW(m_userSavesDir.c_str(), nullptr);
m_prefsDir.append(L"\\");
m_scoresDir.append(L"\\");
m_userHousesDir.append(L"\\");
m_userSavesDir.append(L"\\");
CoTaskMemFree(docsPath);
return false;
}
CoTaskMemFree(docsPath);
if (!m_prefsDir.Append(L"\\" GP_APPLICATION_NAME_W))
return false;
if (!m_userHousesDir.Set(m_prefsDir) || !m_userHousesDir.Append(L"\\Houses"))
return false;
if (!m_userSavesDir.Set(m_prefsDir) || !m_userSavesDir.Append(L"\\SavedGames"))
return false;
if (!m_scoresDir.Set(m_prefsDir) || !m_scoresDir.Append(L"\\Scores"))
return false;
if (!m_logsDir.Set(m_prefsDir) || !m_logsDir.Append(L"\\Logs"))
return false;
if (!m_exportDir.Set(m_prefsDir) || !m_exportDir.Append(L"\\Export"))
return false;
CreateDirectoryW(m_prefsDir.Buffer(), nullptr);
CreateDirectoryW(m_scoresDir.Buffer(), nullptr);
CreateDirectoryW(m_userHousesDir.Buffer(), nullptr);
CreateDirectoryW(m_userSavesDir.Buffer(), nullptr);
CreateDirectoryW(m_logsDir.Buffer(), nullptr);
CreateDirectoryW(m_exportDir.Buffer(), nullptr);
if (!m_prefsDir.Append(L"\\") ||
!m_scoresDir.Append(L"\\") ||
!m_userHousesDir.Append(L"\\") ||
!m_userSavesDir.Append(L"\\") ||
!m_logsDir.Append(L"\\") ||
!m_resourcesDir.Append(L"\\") ||
!m_exportDir.Append(L"\\"))
return false;
DWORD modulePathSize = GetModuleFileNameW(nullptr, m_executablePath, MAX_PATH);
if (modulePathSize == MAX_PATH || modulePathSize == 0)
m_executablePath[0] = 0;
@@ -151,7 +194,7 @@ GpFileSystem_Win32::GpFileSystem_Win32()
continue;
}
if (wcscat_s(m_executablePath, L"Resources"))
if (wcscat_s(m_executablePath, L"Packaged"))
{
currentPathLength = 0;
break;
@@ -166,51 +209,58 @@ GpFileSystem_Win32::GpFileSystem_Win32()
currentPathLength--;
}
if (currentPathLength > 0)
{
m_packagedDir = std::wstring(m_executablePath) + L"Packaged\\";
m_housesDir = std::wstring(m_executablePath) + L"Packaged\\Houses\\";
m_resourcesDir = std::wstring(m_executablePath) + L"Resources\\";
}
if (currentPathLength == 0)
return false;
if (!m_packagedDir.Set(m_executablePath) || !m_packagedDir.Append(L"Packaged\\"))
return false;
if (!m_housesDir.Set(m_executablePath) || !m_housesDir.Append(L"Packaged\\Houses\\"))
return false;
if (!m_resourcesDir.Set(m_executablePath) || !m_resourcesDir.Append(L"Resources\\"))
return false;
return true;
}
bool GpFileSystem_Win32::FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path)
{
wchar_t winPath[MAX_PATH + 1];
if (!ResolvePath(virtualDirectory, path, winPath))
if (!ResolvePath(virtualDirectory, &path, 1, winPath))
return false;
return PathFileExistsW(winPath) != 0;
}
bool GpFileSystem_Win32::FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool *exists)
bool GpFileSystem_Win32::FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &exists)
{
wchar_t winPath[MAX_PATH + 1];
if (!ResolvePath(virtualDirectory, path, winPath))
if (!ResolvePath(virtualDirectory, &path, 1, winPath))
{
*exists = false;
exists = false;
return false;
}
DWORD attribs = GetFileAttributesW(winPath);
if (attribs == INVALID_FILE_ATTRIBUTES)
{
*exists = false;
exists = false;
return false;
}
*exists = true;
exists = true;
return (attribs & FILE_ATTRIBUTE_READONLY) != 0;
}
PortabilityLayer::IOStream *GpFileSystem_Win32::OpenFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, GpFileCreationDisposition_t createDisposition)
GpIOStream *GpFileSystem_Win32::OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition)
{
wchar_t winPath[MAX_PATH + 1];
if (!ResolvePath(virtualDirectory, path, winPath))
return false;
if (!ResolvePath(virtualDirectory, paths, numPaths, winPath))
return nullptr;
const DWORD desiredAccess = writeAccess ? (GENERIC_WRITE | GENERIC_READ) : GENERIC_READ;
DWORD winCreationDisposition = 0;
@@ -233,21 +283,21 @@ PortabilityLayer::IOStream *GpFileSystem_Win32::OpenFile(PortabilityLayer::Virtu
winCreationDisposition = TRUNCATE_EXISTING;
break;
default:
return false;
return nullptr;
}
HANDLE h = CreateFileW(winPath, desiredAccess, FILE_SHARE_READ, nullptr, winCreationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr);
if (h == INVALID_HANDLE_VALUE)
return false;
return nullptr;
return new GpFileStream_Win32(h, true, writeAccess, true);
return GpFileStream_Win32::Create(m_alloc, h, true, writeAccess, true);
}
bool GpFileSystem_Win32::DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed)
{
wchar_t winPath[MAX_PATH + 1];
if (!ResolvePath(virtualDirectory, path, winPath))
if (!ResolvePath(virtualDirectory, &path, 1, winPath))
return false;
if (DeleteFileW(winPath))
@@ -265,11 +315,22 @@ bool GpFileSystem_Win32::DeleteFile(PortabilityLayer::VirtualDirectory_t virtual
return false;
}
PortabilityLayer::HostDirectoryCursor *GpFileSystem_Win32::ScanDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory)
IGpDirectoryCursor *GpFileSystem_Win32::ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths)
{
wchar_t winPath[MAX_PATH + 2];
if (!ResolvePath(virtualDirectory, "*", winPath))
const char **expandedPaths = static_cast<const char**>(m_alloc->Alloc(sizeof(const char*) * (numPaths + 1)));
if (!expandedPaths)
return nullptr;
for (size_t i = 0; i < numPaths; i++)
expandedPaths[i] = paths[i];
expandedPaths[numPaths] = "*";
const bool isPathResolved = ResolvePath(virtualDirectory, expandedPaths, numPaths + 1, winPath);
m_alloc->Release(expandedPaths);
if (!isPathResolved)
return nullptr;
WIN32_FIND_DATAW findData;
@@ -278,173 +339,31 @@ PortabilityLayer::HostDirectoryCursor *GpFileSystem_Win32::ScanDirectory(Portabi
if (ff == INVALID_HANDLE_VALUE)
return nullptr;
return GpDirectoryCursor_Win32::Create(ff, findData);
return GpDirectoryCursor_Win32::Create(m_alloc, ff, findData);
}
bool GpFileSystem_Win32::PromptSaveFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, char *path, size_t &outPathLength, size_t pathCapacity, const char *initialFileName)
bool GpFileSystem_Win32::ValidateFilePathUnicodeChar(uint32_t c) const
{
wchar_t baseFN[MAX_PATH + 5];
wchar_t baseDir[MAX_PATH + 5];
if (c >= '0' && c <= '9')
return true;
const size_t existingPathLen = strlen(initialFileName);
if (existingPathLen >= MAX_PATH)
return false;
if (c == '_' || c == '\'')
return true;
for (size_t i = 0; i < existingPathLen; i++)
baseFN[i] = static_cast<wchar_t>(initialFileName[i]);
baseFN[existingPathLen] = 0;
if (c == ' ')
return true;
if (!ResolvePath(virtualDirectory, "", baseDir))
return false;
if (c >= 'a' && c <= 'z')
return true;
OPENFILENAMEW ofn;
memset(&ofn, 0, sizeof(ofn));
if (c >= 'A' && c <= 'Z')
return true;
ofn.lStructSize = sizeof(ofn);
ofn.lpstrFilter = GP_APPLICATION_NAME_W L" File (*.gpf)\0*.gpf\0";
ofn.lpstrFile = baseFN;
ofn.lpstrDefExt = L"gpf";
ofn.nMaxFile = MAX_PATH;
ofn.lpstrInitialDir = baseDir;
ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT;
if (!GetSaveFileNameW(&ofn))
return false;
if (ofn.Flags & OFN_EXTENSIONDIFFERENT)
{
MessageBeep(MB_ICONERROR);
MessageBoxW(nullptr, L"Save file failed: Saved files must have the '.gpf' extension", L"Invalid file path", MB_OK);
return false;
}
const wchar_t *fn = ofn.lpstrFile + ofn.nFileOffset;
size_t fnLengthWithoutExt = wcslen(fn);
if (ofn.nFileExtension - 1 > ofn.nFileOffset) // Off by 1 because extension doesn't include .
fnLengthWithoutExt = ofn.nFileExtension - ofn.nFileOffset - 1;
if (fnLengthWithoutExt >= pathCapacity)
{
wchar_t msg[256];
wsprintfW(msg, L"Save file failed: File name is too long. Limit is %i characters.", static_cast<int>(pathCapacity));
MessageBeep(MB_ICONERROR);
MessageBoxW(nullptr, msg, L"Invalid file path", MB_OK);
return false;
}
if (ofn.nFileOffset != wcslen(baseDir) || memcmp(ofn.lpstrFile, baseDir, ofn.nFileOffset * sizeof(wchar_t)))
{
wchar_t msg[256 + MAX_PATH];
wsprintfW(msg, L"Save file failed: File can't be saved here, it must be saved in %s", baseDir);
MessageBeep(MB_ICONERROR);
MessageBoxW(nullptr, msg, L"Invalid file path", MB_OK);
return false;
}
const wchar_t *unsupportedCharMsg = L"File name contains unsupported characters.";
for (size_t i = 0; i < fnLengthWithoutExt; i++)
{
if (fn[i] < static_cast<wchar_t>(0) || fn[i] >= static_cast<wchar_t>(128))
{
MessageBeep(MB_ICONERROR);
MessageBoxW(nullptr, unsupportedCharMsg, L"Invalid file path", MB_OK);
return false;
}
path[i] = static_cast<char>(fn[i]);
}
if (!ValidateFilePath(path, fnLengthWithoutExt))
{
MessageBeep(MB_ICONERROR);
MessageBoxW(nullptr, unsupportedCharMsg, L"Invalid file path", MB_OK);
return false;
}
outPathLength = fnLengthWithoutExt;
return true;
return false;
}
bool GpFileSystem_Win32::PromptOpenFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, char *path, size_t &outPathLength, size_t pathCapacity)
void GpFileSystem_Win32::SetDelayCallback(GpFileSystem_Win32::DelayCallback_t delayCallback)
{
wchar_t baseFN[MAX_PATH + 5];
wchar_t baseDir[MAX_PATH + 5];
baseFN[0] = 0;
if (!ResolvePath(virtualDirectory, "", baseDir))
return false;
OPENFILENAMEW ofn;
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.lpstrFilter = GP_APPLICATION_NAME_W L" File (*.gpf)\0*.gpf\0";
ofn.lpstrFile = baseFN;
ofn.lpstrDefExt = L"gpf";
ofn.nMaxFile = MAX_PATH;
ofn.lpstrInitialDir = baseDir;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST;
if (!GetOpenFileNameW(&ofn))
return false;
if (ofn.Flags & OFN_EXTENSIONDIFFERENT)
{
MessageBeep(MB_ICONERROR);
MessageBoxW(nullptr, L"Open file failed: Files must have the '.gpf' extension", L"Invalid file path", MB_OK);
return false;
}
const wchar_t *fn = ofn.lpstrFile + ofn.nFileOffset;
size_t fnLengthWithoutExt = wcslen(fn);
if (ofn.nFileExtension - 1 > ofn.nFileOffset) // Off by 1 because extension doesn't include .
fnLengthWithoutExt = ofn.nFileExtension - ofn.nFileOffset - 1;
if (fnLengthWithoutExt >= pathCapacity)
{
wchar_t msg[256];
wsprintfW(msg, L"Open file failed: File name is too long. Limit is %i characters.", static_cast<int>(pathCapacity));
MessageBeep(MB_ICONERROR);
MessageBoxW(nullptr, msg, L"Invalid file path", MB_OK);
return false;
}
if (ofn.nFileOffset != wcslen(baseDir) || memcmp(ofn.lpstrFile, baseDir, ofn.nFileOffset * sizeof(wchar_t)))
{
wchar_t msg[256 + MAX_PATH];
wsprintfW(msg, L"Open file failed: File can't be opened from here, it must be in %s", baseDir);
MessageBeep(MB_ICONERROR);
MessageBoxW(nullptr, msg, L"Invalid file path", MB_OK);
return false;
}
const wchar_t *unsupportedCharMsg = L"File name contains unsupported characters.";
for (size_t i = 0; i < fnLengthWithoutExt; i++)
{
if (fn[i] < static_cast<wchar_t>(0) || fn[i] >= static_cast<wchar_t>(128))
{
MessageBeep(MB_ICONERROR);
MessageBoxW(nullptr, unsupportedCharMsg, L"Invalid file path", MB_OK);
return false;
}
path[i] = static_cast<char>(fn[i]);
}
if (!ValidateFilePath(path, fnLengthWithoutExt))
{
MessageBeep(MB_ICONERROR);
MessageBoxW(nullptr, unsupportedCharMsg, L"Invalid file path", MB_OK);
return false;
}
outPathLength = fnLengthWithoutExt;
return true;
}
bool GpFileSystem_Win32::ValidateFilePath(const char *str, size_t length) const
@@ -455,7 +374,7 @@ bool GpFileSystem_Win32::ValidateFilePath(const char *str, size_t length) const
if (c >= '0' && c <= '9')
continue;
if (c == '_' || c == '.' || c == '\'')
if (c == '_' || c == '.' || c == '\'' || c == '!')
continue;
if (c == ' ' && i != 0 && i != length - 1)
@@ -470,6 +389,71 @@ bool GpFileSystem_Win32::ValidateFilePath(const char *str, size_t length) const
return false;
}
const char *bannedNames[] =
{
"CON",
"PRN",
"AUX",
"NUL",
"COM1",
"COM2",
"COM3",
"COM4",
"COM5",
"COM6",
"COM7",
"COM8",
"COM9",
"LPT1",
"LPT2",
"LPT3",
"LPT4",
"LPT5",
"LPT6",
"LPT7",
"LPT8",
"LPT9"
};
size_t nameLengthWithoutExt = length;
for (size_t i = 0; i < length; i++)
{
if (str[i] == '.')
{
nameLengthWithoutExt = i;
break;
}
}
const size_t numBannedNames = sizeof(bannedNames) / sizeof(bannedNames[0]);
for (size_t i = 0; i < numBannedNames; i++)
{
const char *bannedName = bannedNames[i];
const size_t banLength = strlen(bannedName);
if (banLength == nameLengthWithoutExt)
{
bool isBanned = true;
for (size_t j = 0; j < banLength; j++)
{
char checkCH = str[j];
if (checkCH >= 'a' && checkCH <= 'z')
checkCH += ('A' - 'a');
if (bannedName[j] != checkCH)
{
isBanned = false;
break;
}
}
if (isBanned)
return false;
}
}
return true;
}
@@ -478,37 +462,61 @@ const wchar_t *GpFileSystem_Win32::GetBasePath() const
return m_executablePath;
}
GpFileSystem_Win32 *GpFileSystem_Win32::GetInstance()
GpFileSystem_Win32 *GpFileSystem_Win32::CreateInstance(IGpAllocator *alloc)
{
return &ms_instance;
void *storage = alloc->Alloc(sizeof(GpFileSystem_Win32));
if (!storage)
return nullptr;
GpFileSystem_Win32 *fs = new (storage) GpFileSystem_Win32(alloc);
if (!fs->Init())
{
fs->Destroy();
return nullptr;
}
ms_instance = fs;
return fs;
}
bool GpFileSystem_Win32::ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, wchar_t *outPath)
GpFileSystem_Win32 *GpFileSystem_Win32::GetInstance()
{
return ms_instance;
}
bool GpFileSystem_Win32::ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, wchar_t *outPath)
{
const wchar_t *baseDir = nullptr;
switch (virtualDirectory)
{
case PortabilityLayer::VirtualDirectories::kApplicationData:
baseDir = m_packagedDir.c_str();
baseDir = m_packagedDir.Buffer();
break;
case PortabilityLayer::VirtualDirectories::kGameData:
baseDir = m_housesDir.c_str();
baseDir = m_housesDir.Buffer();
break;
case PortabilityLayer::VirtualDirectories::kUserData:
baseDir = m_userHousesDir.c_str();
baseDir = m_userHousesDir.Buffer();
break;
case PortabilityLayer::VirtualDirectories::kUserSaves:
baseDir = m_userSavesDir.c_str();
baseDir = m_userSavesDir.Buffer();
break;
case PortabilityLayer::VirtualDirectories::kPrefs:
baseDir = m_prefsDir.c_str();
baseDir = m_prefsDir.Buffer();
break;
case PortabilityLayer::VirtualDirectories::kFonts:
baseDir = m_resourcesDir.c_str();
baseDir = m_resourcesDir.Buffer();
break;
case PortabilityLayer::VirtualDirectories::kHighScores:
baseDir = m_scoresDir.c_str();
baseDir = m_scoresDir.Buffer();
break;
case PortabilityLayer::VirtualDirectories::kLogs:
baseDir = m_logsDir.Buffer();
break;
case PortabilityLayer::VirtualDirectories::kSourceExport:
baseDir = m_exportDir.Buffer();
break;
default:
return false;
@@ -518,24 +526,40 @@ bool GpFileSystem_Win32::ResolvePath(PortabilityLayer::VirtualDirectory_t virtua
return false;
const size_t baseDirLen = wcslen(baseDir);
const size_t pathLen = strlen(path);
if (baseDirLen >= MAX_PATH || MAX_PATH - baseDirLen < pathLen)
return false;
memcpy(outPath, baseDir, sizeof(wchar_t) * baseDirLen);
for (size_t i = 0; i < pathLen; i++)
outPath[baseDirLen] = static_cast<wchar_t>(0);
for (size_t i = 0; i < numPaths; i++)
{
char c = path[i];
if (c == '/')
c = '\\';
size_t outDirLen = wcslen(outPath);
outPath[baseDirLen + i] = static_cast<wchar_t>(c);
if (i != 0)
{
if (baseDirLen >= MAX_PATH || MAX_PATH - baseDirLen < 1)
return false;
outPath[outDirLen++] = '\\';
}
const char *path = paths[i];
const size_t pathLen = strlen(path);
if (baseDirLen >= MAX_PATH || MAX_PATH - baseDirLen < pathLen)
return false;
for (size_t j = 0; j < pathLen; j++)
{
char c = path[j];
if (c == '/')
c = '\\';
outPath[outDirLen + j] = static_cast<wchar_t>(c);
}
outPath[outDirLen + pathLen] = static_cast<wchar_t>(0);
}
outPath[baseDirLen + pathLen] = static_cast<wchar_t>(0);
return true;
}
GpFileSystem_Win32 GpFileSystem_Win32::ms_instance;
GpFileSystem_Win32 *GpFileSystem_Win32::ms_instance;

View File

@@ -1,43 +1,51 @@
#pragma once
#include "HostFileSystem.h"
#include "IGpFileSystem.h"
#include "GpCoreDefs.h"
#include "GpWindows.h"
#include "GpString.h"
#include <string>
class GpFileSystem_Win32 final : public PortabilityLayer::HostFileSystem
class GpFileSystem_Win32 final : public IGpFileSystem
{
public:
GpFileSystem_Win32();
explicit GpFileSystem_Win32(IGpAllocator *alloc);
void Destroy();
bool FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path) override;
bool FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool *exists) override;
PortabilityLayer::IOStream *OpenFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, GpFileCreationDisposition_t createDisposition) override;
bool FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &exists) override;
GpIOStream *OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition) override;
bool DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed) override;
PortabilityLayer::HostDirectoryCursor *ScanDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory) override;
bool PromptSaveFile(PortabilityLayer::VirtualDirectory_t dirID, char *path, size_t &outPathLength, size_t pathCapacity, const char *initialFileName) override;
bool PromptOpenFile(PortabilityLayer::VirtualDirectory_t dirID, char *path, size_t &outPathLength, size_t pathCapacity) override;
IGpDirectoryCursor *ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) override;
bool ValidateFilePath(const char *path, size_t sz) const override;
bool ValidateFilePathUnicodeChar(uint32_t ch) const override;
void SetDelayCallback(DelayCallback_t delayCallback) override;
const wchar_t *GetBasePath() const;
static GpFileSystem_Win32 *CreateInstance(IGpAllocator *alloc);
static GpFileSystem_Win32 *GetInstance();
private:
bool ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, wchar_t *outPath);
bool Init();
std::wstring m_prefsDir;
std::wstring m_scoresDir;
std::wstring m_packagedDir;
std::wstring m_housesDir;
std::wstring m_userHousesDir;
std::wstring m_userSavesDir;
std::wstring m_resourcesDir;
bool ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, wchar_t *outPath);
GpWString m_prefsDir;
GpWString m_scoresDir;
GpWString m_packagedDir;
GpWString m_housesDir;
GpWString m_logsDir;
GpWString m_userHousesDir;
GpWString m_userSavesDir;
GpWString m_resourcesDir;
GpWString m_exportDir;
wchar_t m_executablePath[MAX_PATH];
static GpFileSystem_Win32 ms_instance;
IGpAllocator *m_alloc;
static GpFileSystem_Win32 *ms_instance;
};

View File

@@ -1,9 +0,0 @@
#include "GpFontHandlerFactory.h"
#include "GpFontHandler_FreeType2.h"
#include <stdlib.h>
PortabilityLayer::HostFontHandler *GpFontHandlerFactory::Create()
{
return GpFontHandler_FreeType2::Create();
}

View File

@@ -1,12 +0,0 @@
#pragma once
namespace PortabilityLayer
{
class HostFontHandler;
}
class GpFontHandlerFactory final
{
public:
static PortabilityLayer::HostFontHandler *Create();
};

View File

@@ -0,0 +1,115 @@
#include "GpAllocator_C.h"
#include "GpLogDriver_Win32.h"
#include "GpFileSystem_Win32.h"
#include "GpApplicationName.h"
#include "GpIOStream.h"
GpLogDriver_Win32::GpLogDriver_Win32()
: m_stream(nullptr)
, m_isInitialized(false)
, m_alloc(GpAllocator_C::GetInstance())
{
}
void GpLogDriver_Win32::Init()
{
ms_instance.InitInternal();
}
void GpLogDriver_Win32::VPrintf(Category category, const char *fmt, va_list args)
{
size_t fmtSize = 0;
bool hasFormatting = false;
for (const char *fmtCheck = fmt; *fmtCheck; fmtCheck++)
{
if (*fmtCheck == '%')
hasFormatting = true;
fmtSize++;
}
SYSTEMTIME sysTime;
GetSystemTime(&sysTime);
char timestampBuffer[64];
sprintf(timestampBuffer, "[%02d:%02d:%02d:%03d] ", sysTime.wHour, sysTime.wMinute, sysTime.wSecond, sysTime.wMilliseconds);
m_stream->Write(timestampBuffer, strlen(timestampBuffer));
const char *debugTag = "";
switch (category)
{
case Category_Warning:
debugTag = "[WARNING] ";
break;
case Category_Error:
debugTag = "[ERROR] ";
break;
};
if (debugTag[0])
m_stream->Write(debugTag, strlen(debugTag));
if (!hasFormatting)
m_stream->Write(fmt, fmtSize);
else
{
int formattedSize = vsnprintf(nullptr, 0, fmt, args);
if (formattedSize <= 0)
return;
char *charBuff = static_cast<char*>(m_alloc->Alloc(formattedSize + 1));
if (!charBuff)
return;
vsnprintf(charBuff, formattedSize + 1, fmt, args);
m_stream->Write(charBuff, formattedSize);
m_alloc->Release(charBuff);
}
m_stream->Write("\n", 1);
m_stream->Flush();
}
void GpLogDriver_Win32::Shutdown()
{
if (m_stream)
m_stream->Close();
}
GpLogDriver_Win32 *GpLogDriver_Win32::GetInstance()
{
if (ms_instance.m_isInitialized)
return &ms_instance;
else
return nullptr;
}
void GpLogDriver_Win32::InitInternal()
{
SYSTEMTIME utcTime;
GetSystemTime(&utcTime);
char logFileName[256];
sprintf(logFileName, GP_APPLICATION_NAME "-%04d-%02d-%02d_%02d-%02d_%02d.txt", utcTime.wYear, utcTime.wMonth, utcTime.wDay, utcTime.wHour, utcTime.wMinute, utcTime.wSecond);
m_stream = GpFileSystem_Win32::GetInstance()->OpenFile(PortabilityLayer::VirtualDirectories::kLogs, logFileName, true, GpFileCreationDispositions::kCreateOrOverwrite);
if (m_stream)
{
this->Printf(IGpLogDriver::Category_Information, GP_APPLICATION_NAME " build " __TIMESTAMP__);
#if !GP_DEBUG_CONFIG
this->Printf(IGpLogDriver::Category_Information, "Configuration: Release");
#else
this->Printf(IGpLogDriver::Category_Information, "Configuration: Debug");
#endif
m_isInitialized = true;
}
}
GpLogDriver_Win32 GpLogDriver_Win32::ms_instance;

View File

@@ -0,0 +1,28 @@
#pragma once
#include "IGpLogDriver.h"
class GpIOStream;
struct IGpAllocator;
class GpLogDriver_Win32 : public IGpLogDriver
{
public:
GpLogDriver_Win32();
static void Init();
void VPrintf(Category category, const char *fmt, va_list args) override;
void Shutdown() override;
static GpLogDriver_Win32 *GetInstance();
private:
void InitInternal();
GpIOStream *m_stream;
IGpAllocator *m_alloc;
bool m_isInitialized;
static GpLogDriver_Win32 ms_instance;
};

View File

@@ -1,20 +1,25 @@
#include "GpMain.h"
#include "GpAllocator_C.h"
#include "GpAudioDriverFactory.h"
#include "GpCursor_Win32.h"
#include "GpBWCursor_Win32.h"
#include "GpColorCursor_Win32.h"
#include "GpDisplayDriverFactory.h"
#include "GpGlobalConfig.h"
#include "GpFiber_Win32.h"
#include "GpFileSystem_Win32.h"
#include "GpLogDriver_Win32.h"
#include "GpFontHandlerFactory.h"
#include "GpInputDriverFactory.h"
#include "GpAppInterface.h"
#include "GpSystemServices_Win32.h"
#include "GpVOSEvent.h"
#include "IGpFileSystem.h"
#include "IGpVOSEventQueue.h"
#include "HostFileSystem.h"
#include "GpWindows.h"
#include "resource.h"
#include <shellapi.h>
#include <stdio.h>
#include <windowsx.h>
@@ -24,7 +29,7 @@ extern "C" __declspec(dllimport) IGpAudioDriver *GpDriver_CreateAudioDriver_XAud
extern "C" __declspec(dllimport) IGpDisplayDriver *GpDriver_CreateDisplayDriver_D3D11(const GpDisplayDriverProperties &properties);
extern "C" __declspec(dllimport) IGpInputDriver *GpDriver_CreateInputDriver_XInput(const GpInputDriverProperties &properties);
static void PostMouseEvent(IGpVOSEventQueue *eventQueue, GpMouseEventType_t eventType, GpMouseButton_t button, int32_t x, int32_t y)
static void PostMouseEvent(IGpVOSEventQueue *eventQueue, GpMouseEventType_t eventType, GpMouseButton_t button, int32_t x, int32_t y, float pixelScaleX, float pixelScaleY)
{
if (GpVOSEvent *evt = eventQueue->QueueEvent())
{
@@ -35,6 +40,12 @@ static void PostMouseEvent(IGpVOSEventQueue *eventQueue, GpMouseEventType_t even
mEvent.m_x = x;
mEvent.m_y = y;
mEvent.m_eventType = eventType;
if (pixelScaleX != 1.0f)
mEvent.m_x = static_cast<int32_t>(static_cast<float>(x) / pixelScaleX);
if (pixelScaleY != 1.0f)
mEvent.m_y = static_cast<int32_t>(static_cast<float>(y) / pixelScaleX);
}
}
@@ -291,7 +302,7 @@ static void PostKeyboardEvent(IGpVOSEventQueue *eventQueue, GpKeyboardInputEvent
}
}
static void TranslateWindowsMessage(const MSG *msg, IGpVOSEventQueue *eventQueue)
static void TranslateWindowsMessage(const MSG *msg, IGpVOSEventQueue *eventQueue, float pixelScaleX, float pixelScaleY)
{
WPARAM wParam = msg->wParam;
LPARAM lParam = msg->lParam;
@@ -299,40 +310,40 @@ static void TranslateWindowsMessage(const MSG *msg, IGpVOSEventQueue *eventQueue
switch (msg->message)
{
case WM_LBUTTONDOWN:
PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kLeft, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kLeft, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY);
break;
case WM_LBUTTONUP:
PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kLeft, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kLeft, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY);
break;
case WM_MBUTTONDOWN:
PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kMiddle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kMiddle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY);
break;
case WM_MBUTTONUP:
PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kMiddle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kMiddle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY);
break;
case WM_RBUTTONDOWN:
PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kRight, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kRight, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY);
break;
case WM_RBUTTONUP:
PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kRight, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kRight, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY);
break;
case WM_XBUTTONDOWN:
if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1)
PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kX1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kX1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY);
else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON2)
PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kX2, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kX2, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY);
break;
case WM_XBUTTONUP:
if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1)
PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kX1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kX1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY);
else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON2)
PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kX2, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kX2, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY);
break;
case WM_MOUSEMOVE:
PostMouseEvent(eventQueue, GpMouseEventTypes::kMove, GpMouseButtons::kNone, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
PostMouseEvent(eventQueue, GpMouseEventTypes::kMove, GpMouseButtons::kNone, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY);
break;
case WM_MOUSELEAVE:
PostMouseEvent(eventQueue, GpMouseEventTypes::kLeave, GpMouseButtons::kNone, 0, 0);
PostMouseEvent(eventQueue, GpMouseEventTypes::kLeave, GpMouseButtons::kNone, 0, 0, pixelScaleX, pixelScaleY);
break;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
@@ -373,6 +384,12 @@ static void TranslateWindowsMessage(const MSG *msg, IGpVOSEventQueue *eventQueue
PostKeyboardEvent(eventQueue, keyEventType, subset, key, (lParam & 0xffff));
}
break;
case WM_QUIT:
{
if (GpVOSEvent *evt = eventQueue->QueueEvent())
evt->m_eventType = GpVOSEventTypes::kQuit;
}
break;
default:
break;
}
@@ -380,22 +397,57 @@ static void TranslateWindowsMessage(const MSG *msg, IGpVOSEventQueue *eventQueue
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
GpAppInterface_Get()->PL_HostFileSystem_SetInstance(GpFileSystem_Win32::GetInstance());
GpAppInterface_Get()->PL_HostSystemServices_SetInstance(GpSystemServices_Win32::GetInstance());
(void)lpCmdLine;
LPWSTR cmdLine = GetCommandLineW();
int nArgs;
LPWSTR *cmdLineArgs = CommandLineToArgvW(cmdLine, &nArgs);
IGpAllocator *alloc = GpAllocator_C::GetInstance();
// Init file system first since logging may depend on it
GpFileSystem_Win32 *fs = GpFileSystem_Win32::CreateInstance(alloc);
if (!fs)
return -1;
for (int i = 1; i < nArgs; i++)
{
if (!wcscmp(cmdLineArgs[i], L"-diagnostics"))
GpLogDriver_Win32::Init();
}
IGpLogDriver *logger = GpLogDriver_Win32::GetInstance();
IGpSystemServices *sysServices = GpSystemServices_Win32::GetInstance();
GpDriverCollection *drivers = GpAppInterface_Get()->PL_GetDriverCollection();
drivers->SetDriver<GpDriverIDs::kFileSystem>(GpFileSystem_Win32::GetInstance());
drivers->SetDriver<GpDriverIDs::kSystemServices>(sysServices);
drivers->SetDriver<GpDriverIDs::kLog>(logger);
drivers->SetDriver<GpDriverIDs::kAlloc>(alloc);
g_gpWindowsGlobals.m_hInstance = hInstance;
g_gpWindowsGlobals.m_hPrevInstance = hPrevInstance;
g_gpWindowsGlobals.m_cmdLine = lpCmdLine;
g_gpWindowsGlobals.m_cmdLine = cmdLine;
g_gpWindowsGlobals.m_cmdLineArgc = nArgs;
g_gpWindowsGlobals.m_cmdLineArgv = cmdLineArgs;
g_gpWindowsGlobals.m_nCmdShow = nCmdShow;
g_gpWindowsGlobals.m_baseDir = GpFileSystem_Win32::GetInstance()->GetBasePath();
g_gpWindowsGlobals.m_hwnd = nullptr;
g_gpWindowsGlobals.m_hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_ICON1));
g_gpWindowsGlobals.m_hIconSm = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_ICON2));
g_gpWindowsGlobals.m_createFiberFunc = GpFiber_Win32::Create;
g_gpWindowsGlobals.m_loadCursorFunc = GpCursor_Win32::Load;
g_gpWindowsGlobals.m_createBWCursorFunc = GpBWCursor_Win32::Create;
g_gpWindowsGlobals.m_createColorCursorFunc = GpColorCursor_Win32::Create;
g_gpWindowsGlobals.m_translateWindowsMessageFunc = TranslateWindowsMessage;
g_gpGlobalConfig.m_displayDriverType = EGpDisplayDriverType_D3D11;
g_gpGlobalConfig.m_audioDriverType = EGpAudioDriverType_XAudio2;
g_gpGlobalConfig.m_fontHandlerType = EGpFontHandlerType_None;
EGpInputDriverType inputDrivers[] =
{
EGpInputDriverType_XInput
@@ -405,10 +457,25 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
g_gpGlobalConfig.m_numInputDrivers = sizeof(inputDrivers) / sizeof(inputDrivers[0]);
g_gpGlobalConfig.m_osGlobals = &g_gpWindowsGlobals;
g_gpGlobalConfig.m_logger = logger;
g_gpGlobalConfig.m_systemServices = sysServices;
g_gpGlobalConfig.m_allocator = alloc;
GpDisplayDriverFactory::RegisterDisplayDriverFactory(EGpDisplayDriverType_D3D11, GpDriver_CreateDisplayDriver_D3D11);
GpAudioDriverFactory::RegisterAudioDriverFactory(EGpAudioDriverType_XAudio2, GpDriver_CreateAudioDriver_XAudio2);
GpInputDriverFactory::RegisterInputDriverFactory(EGpInputDriverType_XInput, GpDriver_CreateInputDriver_XInput);
return GpMain::Run();
if (logger)
logger->Printf(IGpLogDriver::Category_Information, "Windows environment configured, starting up");
int returnCode = GpMain::Run();
if (logger)
logger->Printf(IGpLogDriver::Category_Information, "Windows environment exited with code %i, cleaning up", returnCode);
LocalFree(cmdLineArgs);
fs->Destroy();
return returnCode;
}

View File

@@ -1,49 +0,0 @@
#include "GpMemoryBuffer.h"
#include <new>
void *GpMemoryBuffer::Contents()
{
return reinterpret_cast<uint8_t*>(this) + AlignedSize();
}
size_t GpMemoryBuffer::Size()
{
return m_size;
}
void GpMemoryBuffer::Destroy()
{
delete[] reinterpret_cast<uint8_t*>(this);
}
GpMemoryBuffer *GpMemoryBuffer::Create(size_t sz)
{
const size_t allowedSize = SIZE_MAX - AlignedSize();
if (sz > allowedSize)
return nullptr;
const size_t bufferSize = GpMemoryBuffer::AlignedSize() + sz;
uint8_t *buffer = new uint8_t[bufferSize];
new (buffer) GpMemoryBuffer(sz);
return reinterpret_cast<GpMemoryBuffer*>(buffer);
}
GpMemoryBuffer::GpMemoryBuffer(size_t sz)
: m_size(sz)
{
}
GpMemoryBuffer::~GpMemoryBuffer()
{
}
size_t GpMemoryBuffer::AlignedSize()
{
const size_t paddedSize = (sizeof(GpMemoryBuffer) + GP_SYSTEM_MEMORY_ALIGNMENT - 1);
const size_t sz = paddedSize - paddedSize % GP_SYSTEM_MEMORY_ALIGNMENT;
return sz;
}

View File

@@ -1,21 +0,0 @@
#pragma once
#include "HostMemoryBuffer.h"
class GpMemoryBuffer final : public PortabilityLayer::HostMemoryBuffer
{
public:
void *Contents() override;
size_t Size() override;
void Destroy() override;
static GpMemoryBuffer *Create(size_t sz);
private:
explicit GpMemoryBuffer(size_t sz);
~GpMemoryBuffer();
static size_t AlignedSize();
size_t m_size;
};

View File

@@ -1,5 +1,6 @@
#include "GpMutex_Win32.h"
#include "IGpAllocator.h"
#include "GpWindows.h"
#include <stdlib.h>
@@ -7,8 +8,9 @@
void GpMutex_Win32::Destroy()
{
IGpAllocator *alloc = m_alloc;
this->~GpMutex_Win32();
free(this);
alloc->Release(this);
}
void GpMutex_Win32::Lock()
@@ -22,16 +24,17 @@ void GpMutex_Win32::Unlock()
}
GpMutex_Win32 *GpMutex_Win32::Create()
GpMutex_Win32 *GpMutex_Win32::Create(IGpAllocator *alloc)
{
void *storage = malloc(sizeof(GpMutex_Win32));
void *storage = alloc->Alloc(sizeof(GpMutex_Win32));
if (!storage)
return nullptr;
return new (storage) GpMutex_Win32();
return new (storage) GpMutex_Win32(alloc);
}
GpMutex_Win32::GpMutex_Win32()
GpMutex_Win32::GpMutex_Win32(IGpAllocator *alloc)
: m_alloc(alloc)
{
InitializeCriticalSection(&m_critSection);
}

View File

@@ -1,21 +1,24 @@
#pragma once
#include "HostMutex.h"
#include "IGpMutex.h"
#include "GpWindows.h"
class GpMutex_Win32 final : public PortabilityLayer::HostMutex
struct IGpAllocator;
class GpMutex_Win32 final : public IGpMutex
{
public:
void Destroy() override;
void Lock() override;
void Unlock() override;
static GpMutex_Win32 *Create();
static GpMutex_Win32 *Create(IGpAllocator *alloc);
private:
const GpMutex_Win32();
explicit GpMutex_Win32(IGpAllocator *alloc);
~GpMutex_Win32();
CRITICAL_SECTION m_critSection;
IGpAllocator *m_alloc;
};

View File

@@ -1,15 +1,144 @@
#include "GpSystemServices_Win32.h"
#include "GpMutex_Win32.h"
#include "GpThreadEvent_Win32.h"
#include "GpWindows.h"
#include "GpAllocator_C.h"
#include "IGpClipboardContents.h"
#include "GpUnicode.h"
#include <assert.h>
#include <vector>
#pragma push_macro("CreateMutex")
#ifdef CreateMutex
#undef CreateMutex
#endif
extern GpWindowsGlobals g_gpWindowsGlobals;
namespace GpSystemServices_Win32_Private
{
class RefCountedClipboard
{
public:
RefCountedClipboard();
protected:
virtual ~RefCountedClipboard();
void AddRef();
void DecRef();
unsigned int m_refCount;
};
class TextClipboard : public RefCountedClipboard, public IGpClipboardContentsText
{
public:
TextClipboard(const uint8_t *utf8Text, size_t utf8Size);
~TextClipboard() override;
GpClipboardContentsType_t GetContentsType() const override;
void Destroy() override;
IGpClipboardContents *Clone() const override;
const uint8_t *GetBytes() const override;
size_t GetSize() const override;
private:
std::vector<uint8_t> m_utf8Text;
};
RefCountedClipboard::RefCountedClipboard()
: m_refCount(1)
{
}
RefCountedClipboard::~RefCountedClipboard()
{
}
void RefCountedClipboard::AddRef()
{
m_refCount++;
}
void RefCountedClipboard::DecRef()
{
unsigned int rc = --m_refCount;
if (rc == 0)
delete this;
}
TextClipboard::TextClipboard(const uint8_t *utf8Text, size_t utf8Size)
{
m_utf8Text.resize(utf8Size);
if (utf8Size > 0)
memcpy(&m_utf8Text[0], utf8Text, utf8Size);
}
TextClipboard::~TextClipboard()
{
}
GpClipboardContentsType_t TextClipboard::GetContentsType() const
{
return GpClipboardContentsTypes::kText;
}
void TextClipboard::Destroy()
{
this->DecRef();
}
IGpClipboardContents *TextClipboard::Clone() const
{
const_cast<TextClipboard*>(this)->AddRef();
return const_cast<TextClipboard*>(this);
}
const uint8_t *TextClipboard::GetBytes() const
{
if (m_utf8Text.size() == 0)
return nullptr;
return &m_utf8Text[0];
}
size_t TextClipboard::GetSize() const
{
return m_utf8Text.size();
}
}
struct GpSystemServices_Win32_ThreadStartParams
{
GpSystemServices_Win32::ThreadFunc_t m_threadFunc;
void *m_threadContext;
IGpThreadEvent *m_threadStartEvent;
};
static DWORD WINAPI StaticStartThread(LPVOID lpThreadParameter)
{
const GpSystemServices_Win32_ThreadStartParams *threadParams = static_cast<const GpSystemServices_Win32_ThreadStartParams*>(lpThreadParameter);
GpSystemServices_Win32::ThreadFunc_t threadFunc = threadParams->m_threadFunc;
void *threadContext = threadParams->m_threadContext;
IGpThreadEvent *threadStartEvent = threadParams->m_threadStartEvent;
threadStartEvent->Signal();
return threadFunc(threadContext);
}
GpSystemServices_Win32::GpSystemServices_Win32()
: m_isTouchscreenSimulation(false)
, m_alloc(GpAllocator_C::GetInstance())
{
}
GpSystemServices_Win32::~GpSystemServices_Win32()
{
}
@@ -51,14 +180,43 @@ void GpSystemServices_Win32::GetLocalDateTime(unsigned int &year, unsigned int &
second = localTime.wSecond;
}
PortabilityLayer::HostMutex *GpSystemServices_Win32::CreateMutex()
IGpMutex *GpSystemServices_Win32::CreateMutex()
{
return GpMutex_Win32::Create();
return GpMutex_Win32::Create(m_alloc);
}
PortabilityLayer::HostThreadEvent *GpSystemServices_Win32::CreateThreadEvent(bool autoReset, bool startSignaled)
IGpMutex *GpSystemServices_Win32::CreateRecursiveMutex()
{
return GpThreadEvent_Win32::Create(autoReset, startSignaled);
return GpMutex_Win32::Create(m_alloc);
}
IGpThreadEvent *GpSystemServices_Win32::CreateThreadEvent(bool autoReset, bool startSignaled)
{
return GpThreadEvent_Win32::Create(m_alloc, autoReset, startSignaled);
}
void *GpSystemServices_Win32::CreateThread(ThreadFunc_t threadFunc, void *context)
{
IGpThreadEvent *evt = CreateThreadEvent(true, false);
if (!evt)
return nullptr;
GpSystemServices_Win32_ThreadStartParams startParams;
startParams.m_threadContext = context;
startParams.m_threadFunc = threadFunc;
startParams.m_threadStartEvent = evt;
HANDLE threadHdl = ::CreateThread(nullptr, 0, StaticStartThread, &startParams, 0, nullptr);
if (threadHdl == nullptr)
{
evt->Destroy();
return nullptr;
}
evt->Wait();
evt->Destroy();
return threadHdl;
}
uint64_t GpSystemServices_Win32::GetFreeMemoryCosmetic() const
@@ -74,11 +232,162 @@ uint64_t GpSystemServices_Win32::GetFreeMemoryCosmetic() const
return memStatus.ullAvailPhys;
}
void GpSystemServices_Win32::Beep() const
bool GpSystemServices_Win32::Beep() const
{
MessageBeep(MB_OK);
return true;
}
bool GpSystemServices_Win32::IsTouchscreen() const
{
return m_isTouchscreenSimulation;
}
bool GpSystemServices_Win32::IsUsingMouseAsTouch() const
{
return m_isTouchscreenSimulation;
}
bool GpSystemServices_Win32::IsTextInputObstructive() const
{
return false;
}
bool GpSystemServices_Win32::IsFullscreenPreferred() const
{
return !m_isTouchscreenSimulation;
}
bool GpSystemServices_Win32::IsFullscreenOnStartup() const
{
return false;
}
bool GpSystemServices_Win32::HasNativeFileManager() const
{
return true;
}
GpOperatingSystem_t GpSystemServices_Win32::GetOperatingSystem() const
{
return GpOperatingSystems::kWindows;
}
GpOperatingSystemFlavor_t GpSystemServices_Win32::GetOperatingSystemFlavor() const
{
return GpOperatingSystemFlavors::kGeneric;
}
unsigned int GpSystemServices_Win32::GetCPUCount() const
{
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
return sysInfo.dwNumberOfProcessors;
}
void GpSystemServices_Win32::SetTextInputEnabled(bool isEnabled)
{
(void)isEnabled;
}
bool GpSystemServices_Win32::IsTextInputEnabled() const
{
return true;
}
bool GpSystemServices_Win32::AreFontResourcesSeekable() const
{
return true;
}
IGpClipboardContents *GpSystemServices_Win32::GetClipboardContents() const
{
IGpClipboardContents *cbObject = nullptr;
if (IsClipboardFormatAvailable(CF_UNICODETEXT))
{
if (OpenClipboard(g_gpWindowsGlobals.m_hwnd))
{
HGLOBAL textHandle = GetClipboardData(CF_UNICODETEXT);
if (textHandle)
{
const wchar_t *str = static_cast<const wchar_t*>(GlobalLock(textHandle));
if (str)
{
if (str[0] == 0)
cbObject = new GpSystemServices_Win32_Private::TextClipboard(nullptr, 0);
else
{
int bytesRequired = WideCharToMultiByte(CP_UTF8, 0, str, -1, nullptr, 0, nullptr, nullptr);
if (bytesRequired > 0)
{
std::vector<char> decodedText;
decodedText.resize(bytesRequired);
WideCharToMultiByte(CP_UTF8, 0, str, -1, &decodedText[0], bytesRequired, nullptr, nullptr);
cbObject = new GpSystemServices_Win32_Private::TextClipboard(reinterpret_cast<const uint8_t*>(&decodedText[0]), decodedText.size() - 1);
}
}
GlobalUnlock(textHandle);
}
}
CloseClipboard();
}
}
return cbObject;
}
void GpSystemServices_Win32::SetClipboardContents(IGpClipboardContents *contents)
{
if (!contents)
return;
if (contents->GetContentsType() == GpClipboardContentsTypes::kText)
{
IGpClipboardContentsText *textContents = static_cast<IGpClipboardContentsText*>(contents);
if (OpenClipboard(g_gpWindowsGlobals.m_hwnd))
{
if (EmptyClipboard())
{
int wcharsRequired = MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast<const char*>(textContents->GetBytes()), textContents->GetSize(), nullptr, 0);
std::vector<wchar_t> wideChars;
if (wcharsRequired)
{
wideChars.resize(wcharsRequired + 1);
MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast<const char*>(textContents->GetBytes()), textContents->GetSize(), &wideChars[0], wcharsRequired);
}
else
wideChars.resize(1);
wideChars[wideChars.size() - 1] = static_cast<wchar_t>(0);
HGLOBAL textObject = GlobalAlloc(GMEM_MOVEABLE, wideChars.size() * sizeof(wchar_t));
if (textObject)
{
wchar_t *buffer = static_cast<wchar_t*>(GlobalLock(textObject));
memcpy(buffer, &wideChars[0], wideChars.size() * sizeof(wchar_t));
GlobalUnlock(textObject);
SetClipboardData(CF_UNICODETEXT, textObject);
}
}
CloseClipboard();
}
}
}
void GpSystemServices_Win32::SetTouchscreenSimulation(bool isTouchscreenSimulation)
{
m_isTouchscreenSimulation = isTouchscreenSimulation;
}
GpSystemServices_Win32 *GpSystemServices_Win32::GetInstance()
{

View File

@@ -1,6 +1,6 @@
#pragma once
#include "HostSystemServices.h"
#include "IGpSystemServices.h"
#include "GpCoreDefs.h"
#include "GpWindows.h"
@@ -9,23 +9,52 @@
#undef CreateMutex
#endif
#pragma push_macro("CreateThread")
#ifdef CreateThread
#undef CreateThread
#endif
class GpSystemServices_Win32 final : public PortabilityLayer::HostSystemServices
class GpSystemServices_Win32 final : public IGpSystemServices
{
public:
GpSystemServices_Win32();
~GpSystemServices_Win32();
int64_t GetTime() const override;
void GetLocalDateTime(unsigned int &year, unsigned int &month, unsigned int &day, unsigned int &hour, unsigned int &minute, unsigned int &second) const override;
PortabilityLayer::HostMutex *CreateMutex() override;
PortabilityLayer::HostThreadEvent *CreateThreadEvent(bool autoReset, bool startSignaled) override;
IGpMutex *CreateMutex() override;
IGpMutex *CreateRecursiveMutex() override;
void *CreateThread(ThreadFunc_t threadFunc, void *context) override;
IGpThreadEvent *CreateThreadEvent(bool autoReset, bool startSignaled) override;
uint64_t GetFreeMemoryCosmetic() const override;
void Beep() const override;
bool Beep() const override;
bool IsTouchscreen() const override;
bool IsUsingMouseAsTouch() const override;
bool IsTextInputObstructive() const override;
bool IsFullscreenPreferred() const override;
bool IsFullscreenOnStartup() const override;
bool HasNativeFileManager() const override;
GpOperatingSystem_t GetOperatingSystem() const override;
GpOperatingSystemFlavor_t GetOperatingSystemFlavor() const override;
unsigned int GetCPUCount() const override;
void SetTextInputEnabled(bool isEnabled) override;
bool IsTextInputEnabled() const override;
bool AreFontResourcesSeekable() const override;
IGpClipboardContents *GetClipboardContents() const override;
void SetClipboardContents(IGpClipboardContents *contents) override;
void SetTouchscreenSimulation(bool isTouchscreenSimulation);
static GpSystemServices_Win32 *GetInstance();
private:
bool m_isTouchscreenSimulation;
IGpAllocator *m_alloc;
static GpSystemServices_Win32 ms_instance;
};
#pragma pop_macro("CreateMutex")
#pragma pop_macro("CreateThread")

View File

@@ -1,4 +1,5 @@
#include "GpThreadEvent_Win32.h"
#include "IGpAllocator.h"
#include <stdlib.h>
#include <new>
@@ -8,9 +9,9 @@ void GpThreadEvent_Win32::Wait()
WaitForSingleObject(m_event, INFINITE);
}
void GpThreadEvent_Win32::WaitTimed(uint32_t msec)
bool GpThreadEvent_Win32::WaitTimed(uint32_t msec)
{
WaitForSingleObject(m_event, static_cast<DWORD>(msec));
return WaitForSingleObject(m_event, static_cast<DWORD>(msec)) == WAIT_OBJECT_0;
}
void GpThreadEvent_Win32::Signal()
@@ -20,28 +21,30 @@ void GpThreadEvent_Win32::Signal()
void GpThreadEvent_Win32::Destroy()
{
IGpAllocator *alloc = m_alloc;
this->~GpThreadEvent_Win32();
free(this);
alloc->Release(this);
}
GpThreadEvent_Win32 *GpThreadEvent_Win32::Create(bool autoReset, bool startSignaled)
GpThreadEvent_Win32 *GpThreadEvent_Win32::Create(IGpAllocator *alloc, bool autoReset, bool startSignaled)
{
HANDLE handle = CreateEventA(nullptr, autoReset ? FALSE : TRUE, startSignaled ? TRUE : FALSE, nullptr);
if (handle == nullptr)
return nullptr;
void *storage = malloc(sizeof(GpThreadEvent_Win32));
void *storage = alloc->Alloc(sizeof(GpThreadEvent_Win32));
if (!storage)
{
CloseHandle(handle);
return nullptr;
}
return new (storage) GpThreadEvent_Win32(handle);
return new (storage) GpThreadEvent_Win32(alloc, handle);
}
GpThreadEvent_Win32::GpThreadEvent_Win32(const HANDLE &handle)
GpThreadEvent_Win32::GpThreadEvent_Win32(IGpAllocator *alloc, const HANDLE &handle)
: m_event(handle)
, m_alloc(alloc)
{
}

View File

@@ -1,22 +1,23 @@
#pragma once
#include "HostThreadEvent.h"
#include "IGpThreadEvent.h"
#include "GpWindows.h"
class GpThreadEvent_Win32 final : public PortabilityLayer::HostThreadEvent
class GpThreadEvent_Win32 final : public IGpThreadEvent
{
public:
void Wait() override;
void WaitTimed(uint32_t msec) override;
bool WaitTimed(uint32_t msec) override;
void Signal() override;
void Destroy() override;
static GpThreadEvent_Win32 *Create(bool autoReset, bool startSignaled);
static GpThreadEvent_Win32 *Create(IGpAllocator *alloc, bool autoReset, bool startSignaled);
private:
explicit GpThreadEvent_Win32(const HANDLE &handle);
explicit GpThreadEvent_Win32(IGpAllocator *alloc, const HANDLE &handle);
~GpThreadEvent_Win32();
HANDLE m_event;
IGpAllocator *m_alloc;
};

5
AerofoilAndroid/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.idea
.gradle
gradle
local.properties
build

View File

@@ -0,0 +1,5 @@
Aerofoil - Google Play Store Distribution - Privacy Policy
------------------------------------------------------------------------------
The Aerofoil app does not collect or transmit any personal information,
sensitive information, or any other form of user data.

4
AerofoilAndroid/app/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
.cxx
.externalNativeBuild
build
release

View File

@@ -0,0 +1,77 @@
def buildAsLibrary = project.hasProperty('BUILD_AS_LIBRARY');
def buildAsApplication = !buildAsLibrary
if (buildAsApplication) {
apply plugin: 'com.android.application'
}
else {
apply plugin: 'com.android.library'
}
android {
compileSdkVersion 30
defaultConfig {
if (buildAsApplication) {
applicationId "org.thecodedeposit.aerofoil"
}
minSdkVersion 16
targetSdkVersion 30
versionCode 17
versionName "1.1.3"
externalNativeBuild {
ndkBuild {
arguments "APP_PLATFORM=android-16"
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
// cmake {
// arguments "-DANDROID_APP_PLATFORM=android-16", "-DANDROID_STL=c++_static"
// // abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
// abiFilters 'arm64-v8a'
// }
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
if (!project.hasProperty('EXCLUDE_NATIVE_LIBS')) {
sourceSets.main {
jniLibs.srcDir 'libs'
}
externalNativeBuild {
ndkBuild {
path 'jni/Android.mk'
}
// cmake {
// path 'jni/CMakeLists.txt'
// }
}
}
lintOptions {
abortOnError false
}
if (buildAsLibrary) {
libraryVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith(".aar")) {
def fileName = "org.thecodedeposit.aerofoil.aar";
output.outputFile = new File(outputFile.parent, fileName);
}
}
}
}
aaptOptions {
noCompress 'gpf'
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
}
android.buildTypes.release.ndk.debugSymbolLevel = 'FULL'

14
AerofoilAndroid/app/jni/.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
AerofoilSDL
AerofoilPortable
Common
FreeType
GpApp
GpCommon
GpFontHandler_FreeType2
GpShell
MacRomanConversion
PortabilityLayer
rapidjson
SDL2
stb
zlib

View File

@@ -0,0 +1 @@
include $(call all-subdir-makefiles)

View File

@@ -0,0 +1,10 @@
# Uncomment this if you're using STL in your project
# You can find more information here:
# https://developer.android.com/ndk/guides/cpp-support
APP_STL := c++_shared
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
# Min runtime API level
APP_PLATFORM=android-16

View File

@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.6)
project(GAME)
# armeabi-v7a requires cpufeatures library
# include(AndroidNdkModules)
# android_ndk_import_module_cpufeatures()
# SDL sources are in a subfolder named "SDL"
add_subdirectory(SDL)
# Compilation of companion libraries
#add_subdirectory(SDL_image)
#add_subdirectory(SDL_mixer)
#add_subdirectory(SDL_ttf)
# Your game and its CMakeLists.txt are in a subfolder named "src"
add_subdirectory(src)

View File

@@ -0,0 +1,31 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := main
SDL_PATH := ../SDL
LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include \
$(LOCAL_PATH)/../GpShell \
$(LOCAL_PATH)/../GpCommon \
$(LOCAL_PATH)/../AerofoilPortable \
$(LOCAL_PATH)/../AerofoilSDL \
$(LOCAL_PATH)/../Common \
$(LOCAL_PATH)/../PortabilityLayer
LOCAL_CFLAGS := -DGP_DEBUG_CONFIG=0
# Add your application source files here...
LOCAL_SRC_FILES := \
GpMain_SDL_Android.cpp \
GpSystemServices_Android.cpp \
GpFileSystem_Android.cpp
LOCAL_SHARED_LIBRARIES := SDL2
LOCAL_STATIC_LIBRARIES := GpShell AerofoilPortable AerofoilSDL GpApp
LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -llog
include $(BUILD_SHARED_LIBRARY)

View File

@@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.6)
project(MY_APP)
find_library(SDL2 SDL2)
add_library(main SHARED)
target_sources(main PRIVATE YourSourceHere.c)
target_link_libraries(main SDL2)

View File

@@ -0,0 +1,5 @@
#pragma once
struct GpAndroidGlobals
{
};

View File

@@ -0,0 +1,892 @@
#define _LARGEFILE64_SOURCE
#include "GpFileSystem_Android.h"
#include "GpIOStream.h"
#include "IGpDirectoryCursor.h"
#include "IGpSystemServices.h"
#include "IGpMutex.h"
#include "IGpThreadRelay.h"
#include "VirtualDirectory.h"
#include "PLDrivers.h"
#include "SDL.h"
#include "SDL_rwops.h"
#include <string>
#include <vector>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <jni.h>
#include "UTF8.h"
JNIEXPORT void JNICALL nativePostSourceExportRequest(JNIEnv *env, jclass cls, jboolean cancelled, jint fd, jobject pfd);
static JNINativeMethod GpFileSystemAPI_tab[] =
{
{ "nativePostSourceExportRequest", "(ZILjava/lang/Object;)V", reinterpret_cast<void*>(nativePostSourceExportRequest) },
};
class GpFileStream_PFD final : public GpIOStream
{
public:
GpFileStream_PFD(GpFileSystem_Android *fs, int fd, jobject pfd, bool readOnly, bool writeOnly);
~GpFileStream_PFD();
size_t Read(void *bytesOut, size_t size) override;
size_t Write(const void *bytes, size_t size) override;
bool IsSeekable() const override;
bool IsReadOnly() const override;
bool IsWriteOnly() const override;
bool SeekStart(GpUFilePos_t loc) override;
bool SeekCurrent(GpFilePos_t loc) override;
bool SeekEnd(GpUFilePos_t loc) override;
GpUFilePos_t Size() const override;
GpUFilePos_t Tell() const override;
void Close() override;
void Flush() override;
private:
GpFileSystem_Android *m_fs;
int m_fd;
jobject m_pfd;
bool m_readOnly;
bool m_writeOnly;
};
class GpFileStream_SDLRWops final : public GpIOStream
{
public:
GpFileStream_SDLRWops(SDL_RWops *f, bool readOnly, bool writeOnly);
~GpFileStream_SDLRWops();
size_t Read(void *bytesOut, size_t size) override;
size_t Write(const void *bytes, size_t size) override;
bool IsSeekable() const override;
bool IsReadOnly() const override;
bool IsWriteOnly() const override;
bool SeekStart(GpUFilePos_t loc) override;
bool SeekCurrent(GpFilePos_t loc) override;
bool SeekEnd(GpUFilePos_t loc) override;
GpUFilePos_t Size() const override;
GpUFilePos_t Tell() const override;
void Close() override;
void Flush() override;
private:
SDL_RWops *m_rw;
bool m_isReadOnly;
bool m_isWriteOnly;
};
class GpFileStream_Android_File final : public GpIOStream
{
public:
GpFileStream_Android_File(FILE *f, int fd, bool readOnly, bool writeOnly);
~GpFileStream_Android_File();
size_t Read(void *bytesOut, size_t size) override;
size_t Write(const void *bytes, size_t size) override;
bool IsSeekable() const override;
bool IsReadOnly() const override;
bool IsWriteOnly() const override;
bool SeekStart(GpUFilePos_t loc) override;
bool SeekCurrent(GpFilePos_t loc) override;
bool SeekEnd(GpUFilePos_t loc) override;
GpUFilePos_t Size() const override;
GpUFilePos_t Tell() const override;
void Close() override;
void Flush() override;
private:
FILE *m_f;
int m_fd;
bool m_seekable;
bool m_isReadOnly;
bool m_isWriteOnly;
};
GpFileStream_PFD::GpFileStream_PFD(GpFileSystem_Android *fs, int fd, jobject pfd, bool readOnly, bool writeOnly)
: m_fs(fs)
, m_fd(fd)
, m_readOnly(readOnly)
, m_writeOnly(writeOnly)
, m_pfd(pfd)
{
}
GpFileStream_PFD::~GpFileStream_PFD()
{
m_fs->ClosePFD(m_pfd);
}
size_t GpFileStream_PFD::Read(void *bytesOut, size_t size)
{
if (m_writeOnly)
return 0;
return read(m_fd, bytesOut, size);
}
size_t GpFileStream_PFD::Write(const void *bytes, size_t size)
{
if (m_readOnly)
return 0;
return write(m_fd, bytes, size);
}
bool GpFileStream_PFD::IsSeekable() const
{
return true;
}
bool GpFileStream_PFD::IsReadOnly() const
{
return m_readOnly;
}
bool GpFileStream_PFD::IsWriteOnly() const
{
return m_writeOnly;
}
bool GpFileStream_PFD::SeekStart(GpUFilePos_t loc)
{
return lseek64(m_fd, loc, SEEK_SET) >= 0;
}
bool GpFileStream_PFD::SeekCurrent(GpFilePos_t loc)
{
return lseek64(m_fd, loc, SEEK_CUR) >= 0;
}
bool GpFileStream_PFD::SeekEnd(GpUFilePos_t loc)
{
return lseek64(m_fd, loc, SEEK_END) >= 0;
}
GpUFilePos_t GpFileStream_PFD::Size() const
{
struct stat64 s;
if (fstat64(m_fd, &s) < 0)
return 0;
return static_cast<GpUFilePos_t>(s.st_size);
}
GpUFilePos_t GpFileStream_PFD::Tell() const
{
return lseek64(m_fd, 0, SEEK_CUR);
}
void GpFileStream_PFD::Close()
{
this->~GpFileStream_PFD();
free(this);
}
void GpFileStream_PFD::Flush()
{
}
GpFileStream_SDLRWops::GpFileStream_SDLRWops(SDL_RWops *f, bool readOnly, bool writeOnly)
: m_rw(f)
, m_isReadOnly(readOnly)
, m_isWriteOnly(writeOnly)
{
}
GpFileStream_SDLRWops::~GpFileStream_SDLRWops()
{
m_rw->close(m_rw);
}
size_t GpFileStream_SDLRWops::Read(void *bytesOut, size_t size)
{
return m_rw->read(m_rw, bytesOut, 1, size);
}
size_t GpFileStream_SDLRWops::Write(const void *bytes, size_t size)
{
return m_rw->write(m_rw, bytes, 1, size);
}
bool GpFileStream_SDLRWops::IsSeekable() const
{
return true;
}
bool GpFileStream_SDLRWops::IsReadOnly() const
{
return m_isReadOnly;
}
bool GpFileStream_SDLRWops::IsWriteOnly() const
{
return m_isWriteOnly;
}
bool GpFileStream_SDLRWops::SeekStart(GpUFilePos_t loc)
{
return m_rw->seek(m_rw, static_cast<Sint64>(loc), RW_SEEK_SET) >= 0;
}
bool GpFileStream_SDLRWops::SeekCurrent(GpFilePos_t loc)
{
return m_rw->seek(m_rw, static_cast<Sint64>(loc), RW_SEEK_CUR) >= 0;
}
bool GpFileStream_SDLRWops::SeekEnd(GpUFilePos_t loc)
{
return m_rw->seek(m_rw, -static_cast<Sint64>(loc), RW_SEEK_END) >= 0;
}
GpUFilePos_t GpFileStream_SDLRWops::Size() const
{
return m_rw->size(m_rw);
}
GpUFilePos_t GpFileStream_SDLRWops::GpFileStream_SDLRWops::Tell() const
{
return SDL_RWtell(m_rw);
}
void GpFileStream_SDLRWops::Close()
{
this->~GpFileStream_SDLRWops();
free(this);
}
void GpFileStream_SDLRWops::Flush()
{
}
GpFileStream_Android_File::GpFileStream_Android_File(FILE *f, int fd, bool readOnly, bool writeOnly)
: m_f(f)
, m_fd(fd)
, m_isReadOnly(readOnly)
, m_isWriteOnly(writeOnly)
{
m_seekable = (fseek(m_f, 0, SEEK_CUR) == 0);
}
GpFileStream_Android_File::~GpFileStream_Android_File()
{
fclose(m_f);
}
size_t GpFileStream_Android_File::Read(void *bytesOut, size_t size)
{
if (m_isWriteOnly)
return 0;
return fread(bytesOut, 1, size, m_f);
}
size_t GpFileStream_Android_File::Write(const void *bytes, size_t size)
{
if (m_isReadOnly)
return 0;
return fwrite(bytes, 1, size, m_f);
}
bool GpFileStream_Android_File::IsSeekable() const
{
return m_seekable;
}
bool GpFileStream_Android_File::IsReadOnly() const
{
return m_isReadOnly;
}
bool GpFileStream_Android_File::IsWriteOnly() const
{
return m_isWriteOnly;
}
bool GpFileStream_Android_File::SeekStart(GpUFilePos_t loc)
{
if (!m_seekable)
return false;
fflush(m_f);
return lseek64(m_fd, static_cast<off64_t>(loc), SEEK_SET) >= 0;
}
bool GpFileStream_Android_File::SeekCurrent(GpFilePos_t loc)
{
if (!m_seekable)
return false;
fflush(m_f);
return lseek64(m_fd, static_cast<off64_t>(loc), SEEK_CUR) >= 0;
}
bool GpFileStream_Android_File::SeekEnd(GpUFilePos_t loc)
{
if (!m_seekable)
return false;
fflush(m_f);
return lseek64(m_fd, -static_cast<off64_t>(loc), SEEK_END) >= 0;
}
GpUFilePos_t GpFileStream_Android_File::Size() const
{
fflush(m_f);
struct stat64 s;
if (fstat64(m_fd, &s) < 0)
return 0;
return static_cast<GpUFilePos_t>(s.st_size);
}
GpUFilePos_t GpFileStream_Android_File::Tell() const
{
return static_cast<GpUFilePos_t>(ftell(m_f));
}
void GpFileStream_Android_File::Close()
{
this->~GpFileStream_Android_File();
free(this);
}
void GpFileStream_Android_File::Flush()
{
fflush(m_f);
}
bool GpFileSystem_Android::OpenSourceExportFD(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *const *paths, size_t numPaths, int &fd, jobject &pfd)
{
if (!m_sourceExportMutex)
m_sourceExportMutex = PLDrivers::GetSystemServices()->CreateMutex();
m_sourceExportWaiting = true;
m_sourceExportCancelled = false;
JNIEnv *jni = static_cast<JNIEnv *>(SDL_AndroidGetJNIEnv());
jstring fname = jni->NewStringUTF(paths[0]);
jni->CallVoidMethod(m_activity, this->m_selectSourceExportPathMID, fname);
jni->DeleteLocalRef(fname);
for (;;)
{
m_sourceExportMutex->Lock();
const bool isWaiting = m_sourceExportWaiting;
m_sourceExportMutex->Unlock();
if (!isWaiting)
break;
m_delayCallback(5);
}
fd = m_sourceExportFD;
pfd = m_sourceExportPFD;
return !m_sourceExportCancelled;
}
bool GpFileSystem_Android::ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, std::string &resolution, bool &isAsset)
{
const char *prefsAppend = nullptr;
isAsset = false;
switch (virtualDirectory)
{
case PortabilityLayer::VirtualDirectories::kApplicationData:
resolution = std::string("Packaged") ;
isAsset = true;
break;
case PortabilityLayer::VirtualDirectories::kGameData:
resolution = std::string("Packaged/Houses");
isAsset = true;
break;
case PortabilityLayer::VirtualDirectories::kFonts:
resolution = std::string("Resources");
isAsset = true;
break;
case PortabilityLayer::VirtualDirectories::kHighScores:
prefsAppend = "HighScores";
break;
case PortabilityLayer::VirtualDirectories::kUserData:
prefsAppend = "Houses";
break;
case PortabilityLayer::VirtualDirectories::kUserSaves:
prefsAppend = "SavedGames";
break;
case PortabilityLayer::VirtualDirectories::kPrefs:
prefsAppend = "Prefs";
break;
default:
return false;
};
if (prefsAppend)
{
char *prefsDir = SDL_GetPrefPath("aerofoil", "aerofoil");
resolution = prefsDir;
SDL_free(prefsDir);
resolution += prefsAppend;
}
for (size_t i = 0; i < numPaths; i++)
{
resolution += "/";
resolution += paths[i];
}
return true;
}
GpFileSystem_Android::GpFileSystem_Android()
: m_activity(nullptr)
, m_delayCallback(nullptr)
, m_sourceExportMutex(nullptr)
, m_sourceExportFD(0)
, m_sourceExportWaiting(false)
, m_sourceExportCancelled(false)
, m_sourceExportPFD(nullptr)
{
}
GpFileSystem_Android::~GpFileSystem_Android()
{
}
void GpFileSystem_Android::InitJNI()
{
JNIEnv *jni = static_cast<JNIEnv *>(SDL_AndroidGetJNIEnv());
jclass fileSystemAPIClass = jni->FindClass("org/thecodedeposit/aerofoil/GpFileSystemAPI");
int registerStatus = jni->RegisterNatives(fileSystemAPIClass, GpFileSystemAPI_tab, sizeof(GpFileSystemAPI_tab) / sizeof(GpFileSystemAPI_tab[0]));
jni->DeleteLocalRef(fileSystemAPIClass);
jobject activityLR = static_cast<jobject>(SDL_AndroidGetActivity());
jclass activityClassLR = static_cast<jclass>(jni->GetObjectClass(activityLR));
m_scanAssetDirectoryMID = jni->GetMethodID(activityClassLR, "scanAssetDirectory", "(Ljava/lang/String;)[Ljava/lang/String;");
m_selectSourceExportPathMID = jni->GetMethodID(activityClassLR, "selectSourceExportPath", "(Ljava/lang/String;)V");
m_closeSourceExportPFDMID = jni->GetMethodID(activityClassLR, "closeSourceExportPFD", "(Ljava/lang/Object;)V");
m_activity = jni->NewGlobalRef(activityLR);
jni->DeleteLocalRef(activityLR);
jni->DeleteLocalRef(activityClassLR);
char *prefsDir = SDL_GetPrefPath("aerofoil", "aerofoil");
size_t prefsDirLen = strlen(prefsDir);
for (size_t i = 0; i < prefsDirLen; i++)
{
if (prefsDir[i] == '/')
{
prefsDir[i] = '\0';
int created = mkdir(prefsDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
prefsDir[i] = '/';
}
}
const char *extensions[] = { "HighScores", "Houses", "SavedGames", "Prefs", "FontCache" };
for (size_t i = 0; i < sizeof(extensions) / sizeof(extensions[0]); i++)
{
std::string prefsPath = std::string(prefsDir) + extensions[i];
int created = mkdir(prefsPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
int n = 0;
}
SDL_free(prefsDir);
}
void GpFileSystem_Android::ShutdownJNI()
{
JNIEnv *jni = static_cast<JNIEnv *>(SDL_AndroidGetJNIEnv());
jni->DeleteGlobalRef(m_activity);
m_activity = nullptr;
}
bool GpFileSystem_Android::FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path)
{
std::string resolvedPath;
bool isAsset;
if (!ResolvePath(virtualDirectory, &path, 1, resolvedPath, isAsset))
return false;
if (isAsset)
{
SDL_RWops *rw = SDL_RWFromFile(resolvedPath.c_str(), "rb");
if (!rw)
return false;
SDL_RWclose(rw);
return true;
}
struct stat s;
return stat(resolvedPath.c_str(), &s) == 0;
}
bool GpFileSystem_Android::FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &exists)
{
std::string resolvedPath;
bool isAsset;
if (!ResolvePath(virtualDirectory, &path, 1, resolvedPath, isAsset))
{
if (exists)
exists = false;
return false;
}
if (isAsset)
{
if (exists)
exists = this->FileExists(virtualDirectory, path);
return true;
}
int permissions = access(resolvedPath.c_str(), W_OK | F_OK);
exists = ((permissions & F_OK) != 0);
return ((permissions & W_OK) != 0);
}
GpIOStream *GpFileSystem_Android::OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* subPaths, size_t numSubPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition)
{
const char *mode = nullptr;
bool canWrite = false;
bool needResetPosition = false;
switch (createDisposition)
{
case GpFileCreationDispositions::kCreateOrOverwrite:
mode = "w+b";
break;
case GpFileCreationDispositions::kCreateNew:
mode = "x+b";
break;
case GpFileCreationDispositions::kCreateOrOpen:
mode = "a+b";
needResetPosition = true;
break;
case GpFileCreationDispositions::kOpenExisting:
mode = writeAccess ? "r+b" : "rb";
break;
case GpFileCreationDispositions::kOverwriteExisting:
mode = "r+b";
break;
default:
return nullptr;
};
if (virtualDirectory == PortabilityLayer::VirtualDirectories::kSourceExport)
{
void *objStorage = malloc(sizeof(GpFileStream_PFD));
if (!objStorage)
return nullptr;
int fd = 0;
jobject pfd = nullptr;
const bool resolved = OpenSourceExportFD(virtualDirectory, subPaths, numSubPaths, fd, pfd);
if (!resolved)
{
free(objStorage);
return nullptr;
}
return new (objStorage) GpFileStream_PFD(this, fd, pfd, false, true);
}
std::string resolvedPath;
bool isAsset;
if (!ResolvePath(virtualDirectory, subPaths, numSubPaths, resolvedPath, isAsset))
return nullptr;
if (isAsset)
{
if (createDisposition == GpFileCreationDispositions::kOverwriteExisting || writeAccess)
return nullptr;
void *objStorage = malloc(sizeof(GpFileStream_SDLRWops));
if (!objStorage)
return nullptr;
SDL_RWops *rw = SDL_RWFromFile(resolvedPath.c_str(), mode);
if (!rw)
{
free(objStorage);
return nullptr;
}
return new (objStorage) GpFileStream_SDLRWops(rw, true, false);
}
else
{
void *objStorage = malloc(sizeof(GpFileStream_Android_File));
if (!objStorage)
return nullptr;
FILE *f = fopen(resolvedPath.c_str(), mode);
if (!f)
{
free(objStorage);
return nullptr;
}
if (needResetPosition)
fseek(f, 0, SEEK_SET);
int fd = fileno(f);
if (createDisposition == GpFileCreationDispositions::kOverwriteExisting)
{
if (ftruncate64(fd, 0) < 0)
{
free(objStorage);
fclose(f);
return nullptr;
}
}
return new (objStorage) GpFileStream_Android_File(f, fd, !writeAccess, false);
}
}
bool GpFileSystem_Android::DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed)
{
std::string resolvedPath;
bool isAsset;
if (!ResolvePath(virtualDirectory, &path, 1, resolvedPath, isAsset))
{
existed = false;
return false;
}
if (isAsset)
return false;
if (unlink(resolvedPath.c_str()) < 0)
{
existed = (errno != ENOENT);
return false;
}
existed = true;
return true;
}
IGpDirectoryCursor *GpFileSystem_Android::ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *const *paths, size_t numPaths)
{
if (virtualDirectory == PortabilityLayer::VirtualDirectories::kGameData || virtualDirectory == PortabilityLayer::VirtualDirectories::kApplicationData)
return ScanAssetDirectory(virtualDirectory, paths, numPaths);
return ScanStorageDirectory(virtualDirectory, paths, numPaths);
}
bool GpFileSystem_Android::ValidateFilePath(const char *path, size_t length) const
{
for (size_t i = 0; i < length; i++)
{
const char c = path[i];
if (c >= '0' && c <= '9')
continue;
if (c == '_' || c == '.' || c == '\'' || c == '!')
continue;
if (c == ' ' && i != 0 && i != length - 1)
continue;
if (c >= 'a' && c <= 'z')
continue;
if (c >= 'A' && c <= 'Z')
continue;
return false;
}
return true;
}
bool GpFileSystem_Android::ValidateFilePathUnicodeChar(uint32_t c) const
{
if (c >= '0' && c <= '9')
return true;
if (c == '_' || c == '\'')
return true;
if (c == ' ')
return true;
if (c >= 'a' && c <= 'z')
return true;
if (c >= 'A' && c <= 'Z')
return true;
return false;
}
void GpFileSystem_Android::SetDelayCallback(DelayCallback_t delayCallback)
{
m_delayCallback = delayCallback;
}
void GpFileSystem_Android::PostSourceExportRequest(bool cancelled, int fd, jobject pfd)
{
JNIEnv *jni = static_cast<JNIEnv *>(SDL_AndroidGetJNIEnv());
jobject globalRef = jni->NewGlobalRef(pfd);
m_sourceExportMutex->Lock();
m_sourceExportWaiting = false;
m_sourceExportCancelled = cancelled;
m_sourceExportFD = fd;
m_sourceExportPFD = globalRef;
m_sourceExportMutex->Unlock();
}
void GpFileSystem_Android::ClosePFD(jobject pfd)
{
JNIEnv *jni = static_cast<JNIEnv *>(SDL_AndroidGetJNIEnv());
jni->CallVoidMethod(m_activity, m_closeSourceExportPFDMID, pfd);
jni->DeleteGlobalRef(pfd);
}
GpFileSystem_Android *GpFileSystem_Android::GetInstance()
{
return &ms_instance;
}
class GpDirectoryCursor_StringList final : public IGpDirectoryCursor
{
public:
explicit GpDirectoryCursor_StringList(std::vector<std::string> &paths);
~GpDirectoryCursor_StringList();
bool GetNext(const char *&outFileName) override;
void Destroy() override;
private:
std::vector<std::string> m_paths;
size_t m_index;
};
GpDirectoryCursor_StringList::GpDirectoryCursor_StringList(std::vector<std::string> &paths)
: m_index(0)
{
std::swap(paths, m_paths);
}
GpDirectoryCursor_StringList::~GpDirectoryCursor_StringList()
{
}
bool GpDirectoryCursor_StringList::GetNext(const char *&outFileName)
{
if (m_index == m_paths.size())
return false;
outFileName = m_paths[m_index].c_str();
m_index++;
return true;
}
void GpDirectoryCursor_StringList::Destroy()
{
delete this;
}
class GpDirectoryCursor_POSIX final : public IGpDirectoryCursor
{
public:
explicit GpDirectoryCursor_POSIX(DIR *dir);
~GpDirectoryCursor_POSIX();
bool GetNext(const char *&outFileName) override;
void Destroy() override;
private:
DIR *m_dir;
};
GpDirectoryCursor_POSIX::GpDirectoryCursor_POSIX(DIR *dir)
: m_dir(dir)
{
}
GpDirectoryCursor_POSIX::~GpDirectoryCursor_POSIX()
{
closedir(m_dir);
}
bool GpDirectoryCursor_POSIX::GetNext(const char *&outFileName)
{
struct dirent *dir = readdir(m_dir);
if (!dir)
return false;
outFileName = dir->d_name;
return true;
}
void GpDirectoryCursor_POSIX::Destroy()
{
delete this;
}
IGpDirectoryCursor *GpFileSystem_Android::ScanAssetDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths)
{
std::string resolvedPath;
std::vector<std::string> subPaths;
bool isAsset = true;
if (!ResolvePath(virtualDirectory, paths, numPaths, resolvedPath, isAsset))
return nullptr;
JNIEnv *jni = static_cast<JNIEnv *>(SDL_AndroidGetJNIEnv());
jstring directory = jni->NewStringUTF(resolvedPath.c_str());
jobjectArray resultArray = static_cast<jobjectArray>(jni->CallObjectMethod(m_activity, m_scanAssetDirectoryMID, directory));
jni->DeleteLocalRef(directory);
size_t arrayLength = jni->GetArrayLength(resultArray);
subPaths.reserve(arrayLength);
for (size_t i = 0; i < arrayLength; i++)
{
jstring pathJStr = static_cast<jstring>(jni->GetObjectArrayElement(resultArray, i));
const char *pathStrChars = jni->GetStringUTFChars(pathJStr, nullptr);
subPaths.push_back(std::string(pathStrChars, static_cast<size_t>(jni->GetStringUTFLength(pathJStr))));
jni->ReleaseStringUTFChars(pathJStr, pathStrChars);
jni->DeleteLocalRef(pathJStr);
}
jni->DeleteLocalRef(resultArray);
return new GpDirectoryCursor_StringList(subPaths);
}
IGpDirectoryCursor *GpFileSystem_Android::ScanStorageDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths)
{
std::string resolvedPath;
std::vector<std::string> subPaths;
bool isAsset = true;
if (!ResolvePath(virtualDirectory, paths, numPaths, resolvedPath, isAsset))
return nullptr;
DIR *d = opendir(resolvedPath.c_str());
if (!d)
return nullptr;
return new GpDirectoryCursor_POSIX(d);
}
JNIEXPORT void JNICALL nativePostSourceExportRequest(JNIEnv *env, jclass cls, jboolean cancelled, jint fd, jobject pfd)
{
GpFileSystem_Android::GetInstance()->PostSourceExportRequest(cancelled != JNI_FALSE, fd, pfd);
}
GpFileSystem_Android GpFileSystem_Android::ms_instance;

View File

@@ -0,0 +1,58 @@
#pragma once
#include "IGpFileSystem.h"
#include "GpCoreDefs.h"
#include <jni.h>
#include <string>
struct IGpMutex;
class GpFileSystem_Android final : public IGpFileSystem
{
public:
GpFileSystem_Android();
~GpFileSystem_Android();
void InitJNI();
void ShutdownJNI();
bool FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path) override;
bool FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &exists) override;
GpIOStream *OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* subPaths, size_t numSubPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition) override;
bool DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed) override;
IGpDirectoryCursor *ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) override;
bool ValidateFilePath(const char *path, size_t pathLen) const override;
bool ValidateFilePathUnicodeChar(uint32_t ch) const override;
void SetDelayCallback(DelayCallback_t delayCallback) override;
void PostSourceExportRequest(bool cancelled, int fd, jobject pfd);
void ClosePFD(jobject pfd);
static GpFileSystem_Android *GetInstance();
private:
IGpDirectoryCursor *ScanAssetDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths);
IGpDirectoryCursor *ScanStorageDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths);
bool OpenSourceExportFD(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, int &fd, jobject &pfd);
bool ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, std::string &resolution, bool &isAsset);
DelayCallback_t m_delayCallback;
jobject m_activity;
jmethodID m_scanAssetDirectoryMID;
jmethodID m_selectSourceExportPathMID;
jmethodID m_closeSourceExportPFDMID;
IGpMutex *m_sourceExportMutex;
int m_sourceExportFD;
bool m_sourceExportWaiting;
bool m_sourceExportCancelled;
jobject m_sourceExportPFD;
static GpFileSystem_Android ms_instance;
};

View File

@@ -0,0 +1,127 @@
#include "SDL.h"
#include "GpMain.h"
#include "GpAllocator_C.h"
#include "GpAudioDriverFactory.h"
#include "GpDisplayDriverFactory.h"
#include "GpGlobalConfig.h"
#include "GpFileSystem_Android.h"
#include "GpFontHandlerFactory.h"
#include "GpInputDriverFactory.h"
#include "GpInputDriver_SDL_Gamepad.h"
#include "GpAppInterface.h"
#include "GpSystemServices_Android.h"
#include "GpVOSEvent.h"
#include "IGpVOSEventQueue.h"
#include "IGpLogDriver.h"
#include "IGpFileSystem.h"
#include "IGpThreadEvent.h"
#include "GpAndroid.h"
#include <exception>
GpAndroidGlobals g_gpAndroidGlobals;
IGpDisplayDriver *GpDriver_CreateDisplayDriver_SDL_GL2(const GpDisplayDriverProperties &properties);
IGpAudioDriver *GpDriver_CreateAudioDriver_SDL(const GpAudioDriverProperties &properties);
IGpInputDriver *GpDriver_CreateInputDriver_SDL2_Gamepad(const GpInputDriverProperties &properties);
class GpLogDriver_Android final : public IGpLogDriver
{
public:
void VPrintf(Category category, const char *fmt, va_list args) override;
void Shutdown() override;
static GpLogDriver_Android *GetInstance();
private:
static GpLogDriver_Android ms_instance;
};
void GpLogDriver_Android::VPrintf(IGpLogDriver::Category category, const char *fmt, va_list args)
{
switch (category)
{
case IGpLogDriver::Category_Error:
SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, fmt, args);
break;
case IGpLogDriver::Category_Warning:
SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_WARN, fmt, args);
break;
case IGpLogDriver::Category_Information:
SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, args);
break;
default:
SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_VERBOSE, fmt, args);
break;
};
}
void GpLogDriver_Android::Shutdown()
{
}
GpLogDriver_Android *GpLogDriver_Android::GetInstance()
{
return &ms_instance;
}
GpLogDriver_Android GpLogDriver_Android::ms_instance;
int main(int argc, char* argv[])
{
IGpAllocator *alloc = GpAllocator_C::GetInstance();
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_INFO);
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0)
return -1;
SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight");
GpFileSystem_Android::GetInstance()->InitJNI();
GpDriverCollection *drivers = GpAppInterface_Get()->PL_GetDriverCollection();
drivers->SetDriver<GpDriverIDs::kFileSystem>(GpFileSystem_Android::GetInstance());
drivers->SetDriver<GpDriverIDs::kSystemServices>(GpSystemServices_Android::GetInstance());
drivers->SetDriver<GpDriverIDs::kLog>(GpLogDriver_Android::GetInstance());
drivers->SetDriver<GpDriverIDs::kAlloc>(alloc);
g_gpGlobalConfig.m_displayDriverType = EGpDisplayDriverType_SDL_GL2;
g_gpGlobalConfig.m_audioDriverType = EGpAudioDriverType_SDL2;
g_gpGlobalConfig.m_fontHandlerType = EGpFontHandlerType_None;
EGpInputDriverType inputDrivers[] =
{
EGpInputDriverType_SDL2_Gamepad
};
g_gpGlobalConfig.m_inputDriverTypes = inputDrivers;
g_gpGlobalConfig.m_numInputDrivers = sizeof(inputDrivers) / sizeof(inputDrivers[0]);
g_gpGlobalConfig.m_osGlobals = &g_gpAndroidGlobals;
g_gpGlobalConfig.m_logger = GpLogDriver_Android::GetInstance();
g_gpGlobalConfig.m_systemServices = GpSystemServices_Android::GetInstance();
g_gpGlobalConfig.m_allocator = alloc;
GpDisplayDriverFactory::RegisterDisplayDriverFactory(EGpDisplayDriverType_SDL_GL2, GpDriver_CreateDisplayDriver_SDL_GL2);
GpAudioDriverFactory::RegisterAudioDriverFactory(EGpAudioDriverType_SDL2, GpDriver_CreateAudioDriver_SDL);
GpInputDriverFactory::RegisterInputDriverFactory(EGpInputDriverType_SDL2_Gamepad, GpDriver_CreateInputDriver_SDL2_Gamepad);
int returnCode = GpMain::Run();
GpFileSystem_Android::GetInstance()->ShutdownJNI();
SDL_Quit();
// This doesn't even actually exit, but it does stop this stupid brain-damaged OS from
// just calling "main" again with no cleanup...
// That'll have to do until proper cleanup code is added to everything.
exit(returnCode);
return returnCode;
}

View File

@@ -0,0 +1,137 @@
#include "GpSystemServices_Android.h"
#include "IGpThreadEvent.h"
#include "SDL.h"
#include <time.h>
#include <unistd.h>
struct GpSystemServices_Android_ThreadStartParams
{
GpSystemServices_Android::ThreadFunc_t m_threadFunc;
void *m_threadContext;
IGpThreadEvent *m_threadStartEvent;
};
static int SDLCALL StaticStartThread(void *lpThreadParameter)
{
const GpSystemServices_Android_ThreadStartParams *threadParams = static_cast<const GpSystemServices_Android_ThreadStartParams*>(lpThreadParameter);
GpSystemServices_Android::ThreadFunc_t threadFunc = threadParams->m_threadFunc;
void *threadContext = threadParams->m_threadContext;
IGpThreadEvent *threadStartEvent = threadParams->m_threadStartEvent;
threadStartEvent->Signal();
return threadFunc(threadContext);
}
GpSystemServices_Android::GpSystemServices_Android()
: m_textInputEnabled(false)
{
}
void *GpSystemServices_Android::CreateThread(ThreadFunc_t threadFunc, void *context)
{
IGpThreadEvent *evt = CreateThreadEvent(true, false);
if (!evt)
return nullptr;
GpSystemServices_Android_ThreadStartParams startParams;
startParams.m_threadContext = context;
startParams.m_threadFunc = threadFunc;
startParams.m_threadStartEvent = evt;
SDL_Thread *thread = SDL_CreateThread(StaticStartThread, "WorkerThread", &startParams);
if (thread == nullptr)
{
evt->Destroy();
return nullptr;
}
evt->Wait();
evt->Destroy();
return thread;
}
bool GpSystemServices_Android::Beep() const
{
return false;
}
bool GpSystemServices_Android::IsTouchscreen() const
{
return true;
}
bool GpSystemServices_Android::IsUsingMouseAsTouch() const
{
return false;
}
bool GpSystemServices_Android::IsTextInputObstructive() const
{
return true;
}
bool GpSystemServices_Android::IsFullscreenPreferred() const
{
return true;
}
bool GpSystemServices_Android::IsFullscreenOnStartup() const
{
return true;
}
bool GpSystemServices_Android::HasNativeFileManager() const
{
return false;
}
GpOperatingSystem_t GpSystemServices_Android::GetOperatingSystem() const
{
return GpOperatingSystems::kAndroid;
}
GpOperatingSystemFlavor_t GpSystemServices_Android::GetOperatingSystemFlavor() const
{
return GpOperatingSystemFlavors::kGeneric;
}
unsigned int GpSystemServices_Android::GetCPUCount() const
{
return SDL_GetCPUCount();
}
void GpSystemServices_Android::SetTextInputEnabled(bool isEnabled)
{
m_textInputEnabled = isEnabled;
}
bool GpSystemServices_Android::IsTextInputEnabled() const
{
return m_textInputEnabled;
}
bool GpSystemServices_Android::AreFontResourcesSeekable() const
{
return false;
}
IGpClipboardContents *GpSystemServices_Android::GetClipboardContents() const
{
return nullptr;
}
void GpSystemServices_Android::SetClipboardContents(IGpClipboardContents *contents)
{
}
GpSystemServices_Android *GpSystemServices_Android::GetInstance()
{
return &ms_instance;
}
GpSystemServices_Android GpSystemServices_Android::ms_instance;

View File

@@ -0,0 +1,36 @@
#pragma once
#include "GpSystemServices_POSIX.h"
#include "GpCoreDefs.h"
class GpSystemServices_Android final : public GpSystemServices_POSIX
{
public:
GpSystemServices_Android();
void *CreateThread(ThreadFunc_t threadFunc, void *context) override;
bool Beep() const override;
bool IsTouchscreen() const override;
bool IsUsingMouseAsTouch() const override;
bool IsTextInputObstructive() const override;
bool IsFullscreenPreferred() const override;
bool IsFullscreenOnStartup() const override;
bool HasNativeFileManager() const override;
GpOperatingSystem_t GetOperatingSystem() const override;
GpOperatingSystemFlavor_t GetOperatingSystemFlavor() const override;
unsigned int GetCPUCount() const override;
void SetTextInputEnabled(bool isEnabled) override;
bool IsTextInputEnabled() const override;
bool AreFontResourcesSeekable() const override;
IGpClipboardContents *GetClipboardContents() const override;
void SetClipboardContents(IGpClipboardContents *contents) override;
void FlushTextInputEnabled();
static GpSystemServices_Android *GetInstance();
private:
static GpSystemServices_Android ms_instance;
bool m_textInputEnabled;
};

17
AerofoilAndroid/app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in [sdk]/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Replace com.test.game with the identifier of your game below, e.g.
com.gamemaker.game
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.thecodedeposit.aerofoil"
android:versionCode="1"
android:versionName="1.0"
android:installLocation="auto">
<!-- OpenGL ES 2.0 -->
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<!-- Touchscreen support -->
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<!-- Game controller support -->
<uses-feature
android:name="android.hardware.bluetooth"
android:required="false" />
<uses-feature
android:name="android.hardware.gamepad"
android:required="false" />
<uses-feature
android:name="android.hardware.usb.host"
android:required="false" />
<!-- External mouse input events -->
<uses-feature
android:name="android.hardware.type.pc"
android:required="false" />
<!-- Audio recording support -->
<!-- if you want to capture audio, uncomment this. -->
<!-- <uses-feature
android:name="android.hardware.microphone"
android:required="false" /> -->
<!-- Allow writing to external storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Allow access to Bluetooth devices -->
<!--
<uses-permission android:name="android.permission.BLUETOOTH" />
-->
<!-- Allow access to the vibrator -->
<!--
<uses-permission android:name="android.permission.VIBRATE" />
-->
<!-- if you want to capture audio, uncomment this. -->
<!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->
<!-- Create a Java class extending SDLActivity and place it in a
directory under app/src/main/java matching the package, e.g. app/src/main/java/com/gamemaker/game/MyGame.java
then replace "SDLActivity" with the name of your class (e.g. "MyGame")
in the XML below.
An example Java class can be found in README-android.md
-->
<application android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:allowBackup="true"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:hardwareAccelerated="true" >
<!-- Example of setting SDL hints from AndroidManifest.xml:
<meta-data android:name="SDL_ENV.SDL_ACCELEROMETER_AS_JOYSTICK" android:value="0"/>
-->
<activity android:name="GpActivity"
android:label="@string/app_name"
android:alwaysRetainTaskState="true"
android:launchMode="singleInstance"
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- Drop file event -->
<!--
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
-->
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,3 @@
Packaged
Resources
SourceCode.pkg

View File

@@ -0,0 +1,22 @@
package org.libsdl.app;
import android.hardware.usb.UsbDevice;
interface HIDDevice
{
public int getId();
public int getVendorId();
public int getProductId();
public String getSerialNumber();
public int getVersion();
public String getManufacturerName();
public String getProductName();
public UsbDevice getDevice();
public boolean open();
public int sendFeatureReport(byte[] report);
public int sendOutputReport(byte[] report);
public boolean getFeatureReport(byte[] report);
public void setFrozen(boolean frozen);
public void close();
public void shutdown();
}

View File

@@ -0,0 +1,650 @@
package org.libsdl.app;
import android.content.Context;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothGattService;
import android.hardware.usb.UsbDevice;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.os.*;
//import com.android.internal.util.HexDump;
import java.lang.Runnable;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.UUID;
class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
private static final String TAG = "hidapi";
private HIDDeviceManager mManager;
private BluetoothDevice mDevice;
private int mDeviceId;
private BluetoothGatt mGatt;
private boolean mIsRegistered = false;
private boolean mIsConnected = false;
private boolean mIsChromebook = false;
private boolean mIsReconnecting = false;
private boolean mFrozen = false;
private LinkedList<GattOperation> mOperations;
GattOperation mCurrentOperation = null;
private Handler mHandler;
private static final int TRANSPORT_AUTO = 0;
private static final int TRANSPORT_BREDR = 1;
private static final int TRANSPORT_LE = 2;
private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
static class GattOperation {
private enum Operation {
CHR_READ,
CHR_WRITE,
ENABLE_NOTIFICATION
}
Operation mOp;
UUID mUuid;
byte[] mValue;
BluetoothGatt mGatt;
boolean mResult = true;
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
mGatt = gatt;
mOp = operation;
mUuid = uuid;
}
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
mGatt = gatt;
mOp = operation;
mUuid = uuid;
mValue = value;
}
public void run() {
// This is executed in main thread
BluetoothGattCharacteristic chr;
switch (mOp) {
case CHR_READ:
chr = getCharacteristic(mUuid);
//Log.v(TAG, "Reading characteristic " + chr.getUuid());
if (!mGatt.readCharacteristic(chr)) {
Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
mResult = false;
break;
}
mResult = true;
break;
case CHR_WRITE:
chr = getCharacteristic(mUuid);
//Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
chr.setValue(mValue);
if (!mGatt.writeCharacteristic(chr)) {
Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
mResult = false;
break;
}
mResult = true;
break;
case ENABLE_NOTIFICATION:
chr = getCharacteristic(mUuid);
//Log.v(TAG, "Writing descriptor of " + chr.getUuid());
if (chr != null) {
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (cccd != null) {
int properties = chr.getProperties();
byte[] value;
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
} else {
Log.e(TAG, "Unable to start notifications on input characteristic");
mResult = false;
return;
}
mGatt.setCharacteristicNotification(chr, true);
cccd.setValue(value);
if (!mGatt.writeDescriptor(cccd)) {
Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
mResult = false;
return;
}
mResult = true;
}
}
}
}
public boolean finish() {
return mResult;
}
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
BluetoothGattService valveService = mGatt.getService(steamControllerService);
if (valveService == null)
return null;
return valveService.getCharacteristic(uuid);
}
static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
return new GattOperation(gatt, Operation.CHR_READ, uuid);
}
static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
}
static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
}
}
public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
mManager = manager;
mDevice = device;
mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
mIsRegistered = false;
mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
mOperations = new LinkedList<GattOperation>();
mHandler = new Handler(Looper.getMainLooper());
mGatt = connectGatt();
// final HIDDeviceBLESteamController finalThis = this;
// mHandler.postDelayed(new Runnable() {
// @Override
// public void run() {
// finalThis.checkConnectionForChromebookIssue();
// }
// }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
}
public String getIdentifier() {
return String.format("SteamController.%s", mDevice.getAddress());
}
public BluetoothGatt getGatt() {
return mGatt;
}
// Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
// of TRANSPORT_LE. Let's force ourselves to connect low energy.
private BluetoothGatt connectGatt(boolean managed) {
if (Build.VERSION.SDK_INT >= 23) {
try {
return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE);
} catch (Exception e) {
return mDevice.connectGatt(mManager.getContext(), managed, this);
}
} else {
return mDevice.connectGatt(mManager.getContext(), managed, this);
}
}
private BluetoothGatt connectGatt() {
return connectGatt(false);
}
protected int getConnectionState() {
Context context = mManager.getContext();
if (context == null) {
// We are lacking any context to get our Bluetooth information. We'll just assume disconnected.
return BluetoothProfile.STATE_DISCONNECTED;
}
BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
if (btManager == null) {
// This device doesn't support Bluetooth. We should never be here, because how did
// we instantiate a device to start with?
return BluetoothProfile.STATE_DISCONNECTED;
}
return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
}
public void reconnect() {
if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
mGatt.disconnect();
mGatt = connectGatt();
}
}
protected void checkConnectionForChromebookIssue() {
if (!mIsChromebook) {
// We only do this on Chromebooks, because otherwise it's really annoying to just attempt
// over and over.
return;
}
int connectionState = getConnectionState();
switch (connectionState) {
case BluetoothProfile.STATE_CONNECTED:
if (!mIsConnected) {
// We are in the Bad Chromebook Place. We can force a disconnect
// to try to recover.
Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback. Forcing a reconnect.");
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
break;
}
else if (!isRegistered()) {
if (mGatt.getServices().size() > 0) {
Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover.");
probeService(this);
}
else {
Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover.");
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
break;
}
}
else {
Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!");
return;
}
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us. Attempting a disconnect/reconnect, but we may not be able to recover.");
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
break;
case BluetoothProfile.STATE_CONNECTING:
Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer.");
break;
}
final HIDDeviceBLESteamController finalThis = this;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
finalThis.checkConnectionForChromebookIssue();
}
}, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
}
private boolean isRegistered() {
return mIsRegistered;
}
private void setRegistered() {
mIsRegistered = true;
}
private boolean probeService(HIDDeviceBLESteamController controller) {
if (isRegistered()) {
return true;
}
if (!mIsConnected) {
return false;
}
Log.v(TAG, "probeService controller=" + controller);
for (BluetoothGattService service : mGatt.getServices()) {
if (service.getUuid().equals(steamControllerService)) {
Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
if (chr.getUuid().equals(inputCharacteristic)) {
Log.v(TAG, "Found input characteristic");
// Start notifications
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (cccd != null) {
enableNotification(chr.getUuid());
}
}
}
return true;
}
}
if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
mIsConnected = false;
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
}
return false;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
private void finishCurrentGattOperation() {
GattOperation op = null;
synchronized (mOperations) {
if (mCurrentOperation != null) {
op = mCurrentOperation;
mCurrentOperation = null;
}
}
if (op != null) {
boolean result = op.finish(); // TODO: Maybe in main thread as well?
// Our operation failed, let's add it back to the beginning of our queue.
if (!result) {
mOperations.addFirst(op);
}
}
executeNextGattOperation();
}
private void executeNextGattOperation() {
synchronized (mOperations) {
if (mCurrentOperation != null)
return;
if (mOperations.isEmpty())
return;
mCurrentOperation = mOperations.removeFirst();
}
// Run in main thread
mHandler.post(new Runnable() {
@Override
public void run() {
synchronized (mOperations) {
if (mCurrentOperation == null) {
Log.e(TAG, "Current operation null in executor?");
return;
}
mCurrentOperation.run();
// now wait for the GATT callback and when it comes, finish this operation
}
}
});
}
private void queueGattOperation(GattOperation op) {
synchronized (mOperations) {
mOperations.add(op);
}
executeNextGattOperation();
}
private void enableNotification(UUID chrUuid) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
queueGattOperation(op);
}
public void writeCharacteristic(UUID uuid, byte[] value) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
queueGattOperation(op);
}
public void readCharacteristic(UUID uuid) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
queueGattOperation(op);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
////////////// BluetoothGattCallback overridden methods
//////////////////////////////////////////////////////////////////////////////////////////////////////
public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
//Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
mIsReconnecting = false;
if (newState == 2) {
mIsConnected = true;
// Run directly, without GattOperation
if (!isRegistered()) {
mHandler.post(new Runnable() {
@Override
public void run() {
mGatt.discoverServices();
}
});
}
}
else if (newState == 0) {
mIsConnected = false;
}
// Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
}
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
//Log.v(TAG, "onServicesDiscovered status=" + status);
if (status == 0) {
if (gatt.getServices().size() == 0) {
Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
mIsReconnecting = true;
mIsConnected = false;
gatt.disconnect();
mGatt = connectGatt(false);
}
else {
probeService(this);
}
}
}
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue());
}
finishCurrentGattOperation();
}
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
if (characteristic.getUuid().equals(reportCharacteristic)) {
// Only register controller with the native side once it has been fully configured
if (!isRegistered()) {
Log.v(TAG, "Registering Steam Controller with ID: " + getId());
mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0);
setRegistered();
}
}
finishCurrentGattOperation();
}
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
// Enable this for verbose logging of controller input reports
//Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
}
}
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
//Log.v(TAG, "onDescriptorRead status=" + status);
}
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
//Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
if (chr.getUuid().equals(inputCharacteristic)) {
boolean hasWrittenInputDescriptor = true;
BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
if (reportChr != null) {
Log.v(TAG, "Writing report characteristic to enter valve mode");
reportChr.setValue(enterValveMode);
gatt.writeCharacteristic(reportChr);
}
}
finishCurrentGattOperation();
}
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
//Log.v(TAG, "onReliableWriteCompleted status=" + status);
}
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
//Log.v(TAG, "onReadRemoteRssi status=" + status);
}
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
//Log.v(TAG, "onMtuChanged status=" + status);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////// Public API
//////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
public int getId() {
return mDeviceId;
}
@Override
public int getVendorId() {
// Valve Corporation
final int VALVE_USB_VID = 0x28DE;
return VALVE_USB_VID;
}
@Override
public int getProductId() {
// We don't have an easy way to query from the Bluetooth device, but we know what it is
final int D0G_BLE2_PID = 0x1106;
return D0G_BLE2_PID;
}
@Override
public String getSerialNumber() {
// This will be read later via feature report by Steam
return "12345";
}
@Override
public int getVersion() {
return 0;
}
@Override
public String getManufacturerName() {
return "Valve Corporation";
}
@Override
public String getProductName() {
return "Steam Controller";
}
@Override
public UsbDevice getDevice() {
return null;
}
@Override
public boolean open() {
return true;
}
@Override
public int sendFeatureReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return -1;
}
// We need to skip the first byte, as that doesn't go over the air
byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report));
writeCharacteristic(reportCharacteristic, actual_report);
return report.length;
}
@Override
public int sendOutputReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return -1;
}
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report));
writeCharacteristic(reportCharacteristic, report);
return report.length;
}
@Override
public boolean getFeatureReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return false;
}
//Log.v(TAG, "getFeatureReport");
readCharacteristic(reportCharacteristic);
return true;
}
@Override
public void close() {
}
@Override
public void setFrozen(boolean frozen) {
mFrozen = frozen;
}
@Override
public void shutdown() {
close();
BluetoothGatt g = mGatt;
if (g != null) {
g.disconnect();
g.close();
mGatt = null;
}
mManager = null;
mIsRegistered = false;
mIsConnected = false;
mOperations.clear();
}
}

View File

@@ -0,0 +1,669 @@
package org.libsdl.app;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.util.Log;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.hardware.usb.*;
import android.os.Handler;
import android.os.Looper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
public class HIDDeviceManager {
private static final String TAG = "hidapi";
private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
private static HIDDeviceManager sManager;
private static int sManagerRefCount = 0;
public static HIDDeviceManager acquire(Context context) {
if (sManagerRefCount == 0) {
sManager = new HIDDeviceManager(context);
}
++sManagerRefCount;
return sManager;
}
public static void release(HIDDeviceManager manager) {
if (manager == sManager) {
--sManagerRefCount;
if (sManagerRefCount == 0) {
sManager.close();
sManager = null;
}
}
}
private Context mContext;
private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
private int mNextDeviceId = 0;
private SharedPreferences mSharedPreferences = null;
private boolean mIsChromebook = false;
private UsbManager mUsbManager;
private Handler mHandler;
private BluetoothManager mBluetoothManager;
private List<BluetoothDevice> mLastBluetoothDevices;
private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
handleUsbDeviceAttached(usbDevice);
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
handleUsbDeviceDetached(usbDevice);
} else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
}
}
};
private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// Bluetooth device was connected. If it was a Steam Controller, handle it
if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d(TAG, "Bluetooth device connected: " + device);
if (isSteamController(device)) {
connectBluetoothDevice(device);
}
}
// Bluetooth device was disconnected, remove from controller manager (if any)
if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d(TAG, "Bluetooth device disconnected: " + device);
disconnectBluetoothDevice(device);
}
}
};
private HIDDeviceManager(final Context context) {
mContext = context;
// Make sure we have the HIDAPI library loaded with the native functions
try {
SDL.loadLibrary("hidapi");
} catch (Throwable e) {
Log.w(TAG, "Couldn't load hidapi: " + e.toString());
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setCancelable(false);
builder.setTitle("SDL HIDAPI Error");
builder.setMessage("Please report the following error to the SDL maintainers: " + e.getMessage());
builder.setNegativeButton("Quit", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
try {
// If our context is an activity, exit rather than crashing when we can't
// call our native functions.
Activity activity = (Activity)context;
activity.finish();
}
catch (ClassCastException cce) {
// Context wasn't an activity, there's nothing we can do. Give up and return.
}
}
});
builder.show();
return;
}
HIDDeviceRegisterCallback();
mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
// if (shouldClear) {
// SharedPreferences.Editor spedit = mSharedPreferences.edit();
// spedit.clear();
// spedit.commit();
// }
// else
{
mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
}
initializeUSB();
initializeBluetooth();
}
public Context getContext() {
return mContext;
}
public int getDeviceIDForIdentifier(String identifier) {
SharedPreferences.Editor spedit = mSharedPreferences.edit();
int result = mSharedPreferences.getInt(identifier, 0);
if (result == 0) {
result = mNextDeviceId++;
spedit.putInt("next_device_id", mNextDeviceId);
}
spedit.putInt(identifier, result);
spedit.commit();
return result;
}
private void initializeUSB() {
mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
/*
// Logging
for (UsbDevice device : mUsbManager.getDeviceList().values()) {
Log.i(TAG,"Path: " + device.getDeviceName());
Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
Log.i(TAG,"Product: " + device.getProductName());
Log.i(TAG,"ID: " + device.getDeviceId());
Log.i(TAG,"Class: " + device.getDeviceClass());
Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
Log.i(TAG,"Vendor ID " + device.getVendorId());
Log.i(TAG,"Product ID: " + device.getProductId());
Log.i(TAG,"Interface count: " + device.getInterfaceCount());
Log.i(TAG,"---------------------------------------");
// Get interface details
for (int index = 0; index < device.getInterfaceCount(); index++) {
UsbInterface mUsbInterface = device.getInterface(index);
Log.i(TAG," ***** *****");
Log.i(TAG," Interface index: " + index);
Log.i(TAG," Interface ID: " + mUsbInterface.getId());
Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass());
Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass());
Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
// Get endpoint details
for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
{
UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
Log.i(TAG," ++++ ++++ ++++");
Log.i(TAG," Endpoint index: " + epi);
Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
Log.i(TAG," Direction: " + mEndpoint.getDirection());
Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
Log.i(TAG," Interval: " + mEndpoint.getInterval());
Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
Log.i(TAG," Type: " + mEndpoint.getType());
}
}
}
Log.i(TAG," No more devices connected.");
*/
// Register for USB broadcasts and permission completions
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
mContext.registerReceiver(mUsbBroadcast, filter);
for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
handleUsbDeviceAttached(usbDevice);
}
}
UsbManager getUSBManager() {
return mUsbManager;
}
private void shutdownUSB() {
try {
mContext.unregisterReceiver(mUsbBroadcast);
} catch (Exception e) {
// We may not have registered, that's okay
}
}
private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) {
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
return true;
}
if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
return true;
}
return false;
}
private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
final int XB360_IFACE_SUBCLASS = 93;
final int XB360_IFACE_PROTOCOL = 1; // Wired
final int XB360W_IFACE_PROTOCOL = 129; // Wireless
final int[] SUPPORTED_VENDORS = {
0x0079, // GPD Win 2
0x044f, // Thrustmaster
0x045e, // Microsoft
0x046d, // Logitech
0x056e, // Elecom
0x06a3, // Saitek
0x0738, // Mad Catz
0x07ff, // Mad Catz
0x0e6f, // PDP
0x0f0d, // Hori
0x1038, // SteelSeries
0x11c9, // Nacon
0x12ab, // Unknown
0x1430, // RedOctane
0x146b, // BigBen
0x1532, // Razer Sabertooth
0x15e4, // Numark
0x162e, // Joytech
0x1689, // Razer Onza
0x1bad, // Harmonix
0x24c6, // PowerA
};
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
(usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL ||
usbInterface.getInterfaceProtocol() == XB360W_IFACE_PROTOCOL)) {
int vendor_id = usbDevice.getVendorId();
for (int supportedVid : SUPPORTED_VENDORS) {
if (vendor_id == supportedVid) {
return true;
}
}
}
return false;
}
private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
final int XB1_IFACE_SUBCLASS = 71;
final int XB1_IFACE_PROTOCOL = 208;
final int[] SUPPORTED_VENDORS = {
0x045e, // Microsoft
0x0738, // Mad Catz
0x0e6f, // PDP
0x0f0d, // Hori
0x1532, // Razer Wildcat
0x24c6, // PowerA
0x2e24, // Hyperkin
};
if (usbInterface.getId() == 0 &&
usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
int vendor_id = usbDevice.getVendorId();
for (int supportedVid : SUPPORTED_VENDORS) {
if (vendor_id == supportedVid) {
return true;
}
}
}
return false;
}
private void handleUsbDeviceAttached(UsbDevice usbDevice) {
connectHIDDeviceUSB(usbDevice);
}
private void handleUsbDeviceDetached(UsbDevice usbDevice) {
List<Integer> devices = new ArrayList<Integer>();
for (HIDDevice device : mDevicesById.values()) {
if (usbDevice.equals(device.getDevice())) {
devices.add(device.getId());
}
}
for (int id : devices) {
HIDDevice device = mDevicesById.get(id);
mDevicesById.remove(id);
device.shutdown();
HIDDeviceDisconnected(id);
}
}
private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
for (HIDDevice device : mDevicesById.values()) {
if (usbDevice.equals(device.getDevice())) {
boolean opened = false;
if (permission_granted) {
opened = device.open();
}
HIDDeviceOpenResult(device.getId(), opened);
}
}
}
private void connectHIDDeviceUSB(UsbDevice usbDevice) {
synchronized (this) {
for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) {
UsbInterface usbInterface = usbDevice.getInterface(interface_index);
if (isHIDDeviceInterface(usbDevice, usbInterface)) {
HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index);
int id = device.getId();
mDevicesById.put(id, device);
HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol());
}
}
}
}
private void initializeBluetooth() {
Log.d(TAG, "Initializing Bluetooth");
if (mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
return;
}
// Find bonded bluetooth controllers and create SteamControllers for them
mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
// This device doesn't support Bluetooth.
return;
}
BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
if (btAdapter == null) {
// This device has Bluetooth support in the codebase, but has no available adapters.
return;
}
// Get our bonded devices.
for (BluetoothDevice device : btAdapter.getBondedDevices()) {
Log.d(TAG, "Bluetooth device available: " + device);
if (isSteamController(device)) {
connectBluetoothDevice(device);
}
}
// NOTE: These don't work on Chromebooks, to my undying dismay.
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
mContext.registerReceiver(mBluetoothBroadcast, filter);
if (mIsChromebook) {
mHandler = new Handler(Looper.getMainLooper());
mLastBluetoothDevices = new ArrayList<BluetoothDevice>();
// final HIDDeviceManager finalThis = this;
// mHandler.postDelayed(new Runnable() {
// @Override
// public void run() {
// finalThis.chromebookConnectionHandler();
// }
// }, 5000);
}
}
private void shutdownBluetooth() {
try {
mContext.unregisterReceiver(mBluetoothBroadcast);
} catch (Exception e) {
// We may not have registered, that's okay
}
}
// Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
// This function provides a sort of dummy version of that, watching for changes in the
// connected devices and attempting to add controllers as things change.
public void chromebookConnectionHandler() {
if (!mIsChromebook) {
return;
}
ArrayList<BluetoothDevice> disconnected = new ArrayList<BluetoothDevice>();
ArrayList<BluetoothDevice> connected = new ArrayList<BluetoothDevice>();
List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
for (BluetoothDevice bluetoothDevice : currentConnected) {
if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
connected.add(bluetoothDevice);
}
}
for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
if (!currentConnected.contains(bluetoothDevice)) {
disconnected.add(bluetoothDevice);
}
}
mLastBluetoothDevices = currentConnected;
for (BluetoothDevice bluetoothDevice : disconnected) {
disconnectBluetoothDevice(bluetoothDevice);
}
for (BluetoothDevice bluetoothDevice : connected) {
connectBluetoothDevice(bluetoothDevice);
}
final HIDDeviceManager finalThis = this;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
finalThis.chromebookConnectionHandler();
}
}, 10000);
}
public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
synchronized (this) {
if (mBluetoothDevices.containsKey(bluetoothDevice)) {
Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
device.reconnect();
return false;
}
HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
int id = device.getId();
mBluetoothDevices.put(bluetoothDevice, device);
mDevicesById.put(id, device);
// The Steam Controller will mark itself connected once initialization is complete
}
return true;
}
public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
synchronized (this) {
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
if (device == null)
return;
int id = device.getId();
mBluetoothDevices.remove(bluetoothDevice);
mDevicesById.remove(id);
device.shutdown();
HIDDeviceDisconnected(id);
}
}
public boolean isSteamController(BluetoothDevice bluetoothDevice) {
// Sanity check. If you pass in a null device, by definition it is never a Steam Controller.
if (bluetoothDevice == null) {
return false;
}
// If the device has no local name, we really don't want to try an equality check against it.
if (bluetoothDevice.getName() == null) {
return false;
}
return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
}
private void close() {
shutdownUSB();
shutdownBluetooth();
synchronized (this) {
for (HIDDevice device : mDevicesById.values()) {
device.shutdown();
}
mDevicesById.clear();
mBluetoothDevices.clear();
HIDDeviceReleaseCallback();
}
}
public void setFrozen(boolean frozen) {
synchronized (this) {
for (HIDDevice device : mDevicesById.values()) {
device.setFrozen(frozen);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
private HIDDevice getDevice(int id) {
synchronized (this) {
HIDDevice result = mDevicesById.get(id);
if (result == null) {
Log.v(TAG, "No device for id: " + id);
Log.v(TAG, "Available devices: " + mDevicesById.keySet());
}
return result;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
////////// JNI interface functions
//////////////////////////////////////////////////////////////////////////////////////////////////////
public boolean openDevice(int deviceID) {
Log.v(TAG, "openDevice deviceID=" + deviceID);
HIDDevice device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return false;
}
// Look to see if this is a USB device and we have permission to access it
UsbDevice usbDevice = device.getDevice();
if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) {
HIDDeviceOpenPending(deviceID);
try {
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), 0));
} catch (Exception e) {
Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
HIDDeviceOpenResult(deviceID, false);
}
return false;
}
try {
return device.open();
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return false;
}
public int sendOutputReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return -1;
}
return device.sendOutputReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return -1;
}
public int sendFeatureReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return -1;
}
return device.sendFeatureReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return -1;
}
public boolean getFeatureReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return false;
}
return device.getFeatureReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return false;
}
public void closeDevice(int deviceID) {
try {
Log.v(TAG, "closeDevice deviceID=" + deviceID);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return;
}
device.close();
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////// Native methods
//////////////////////////////////////////////////////////////////////////////////////////////////////
private native void HIDDeviceRegisterCallback();
private native void HIDDeviceReleaseCallback();
native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol);
native void HIDDeviceOpenPending(int deviceID);
native void HIDDeviceOpenResult(int deviceID, boolean opened);
native void HIDDeviceDisconnected(int deviceID);
native void HIDDeviceInputReport(int deviceID, byte[] report);
native void HIDDeviceFeatureReport(int deviceID, byte[] report);
}

View File

@@ -0,0 +1,304 @@
package org.libsdl.app;
import android.hardware.usb.*;
import android.os.Build;
import android.util.Log;
import java.util.Arrays;
class HIDDeviceUSB implements HIDDevice {
private static final String TAG = "hidapi";
protected HIDDeviceManager mManager;
protected UsbDevice mDevice;
protected int mInterfaceIndex;
protected int mInterface;
protected int mDeviceId;
protected UsbDeviceConnection mConnection;
protected UsbEndpoint mInputEndpoint;
protected UsbEndpoint mOutputEndpoint;
protected InputThread mInputThread;
protected boolean mRunning;
protected boolean mFrozen;
public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) {
mManager = manager;
mDevice = usbDevice;
mInterfaceIndex = interface_index;
mInterface = mDevice.getInterface(mInterfaceIndex).getId();
mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
mRunning = false;
}
public String getIdentifier() {
return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex);
}
@Override
public int getId() {
return mDeviceId;
}
@Override
public int getVendorId() {
return mDevice.getVendorId();
}
@Override
public int getProductId() {
return mDevice.getProductId();
}
@Override
public String getSerialNumber() {
String result = null;
if (Build.VERSION.SDK_INT >= 21) {
result = mDevice.getSerialNumber();
}
if (result == null) {
result = "";
}
return result;
}
@Override
public int getVersion() {
return 0;
}
@Override
public String getManufacturerName() {
String result = null;
if (Build.VERSION.SDK_INT >= 21) {
result = mDevice.getManufacturerName();
}
if (result == null) {
result = String.format("%x", getVendorId());
}
return result;
}
@Override
public String getProductName() {
String result = null;
if (Build.VERSION.SDK_INT >= 21) {
result = mDevice.getProductName();
}
if (result == null) {
result = String.format("%x", getProductId());
}
return result;
}
@Override
public UsbDevice getDevice() {
return mDevice;
}
public String getDeviceName() {
return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
}
@Override
public boolean open() {
mConnection = mManager.getUSBManager().openDevice(mDevice);
if (mConnection == null) {
Log.w(TAG, "Unable to open USB device " + getDeviceName());
return false;
}
// Force claim our interface
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
if (!mConnection.claimInterface(iface, true)) {
Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
close();
return false;
}
// Find the endpoints
for (int j = 0; j < iface.getEndpointCount(); j++) {
UsbEndpoint endpt = iface.getEndpoint(j);
switch (endpt.getDirection()) {
case UsbConstants.USB_DIR_IN:
if (mInputEndpoint == null) {
mInputEndpoint = endpt;
}
break;
case UsbConstants.USB_DIR_OUT:
if (mOutputEndpoint == null) {
mOutputEndpoint = endpt;
}
break;
}
}
// Make sure the required endpoints were present
if (mInputEndpoint == null || mOutputEndpoint == null) {
Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
close();
return false;
}
// Start listening for input
mRunning = true;
mInputThread = new InputThread();
mInputThread.start();
return true;
}
@Override
public int sendFeatureReport(byte[] report) {
int res = -1;
int offset = 0;
int length = report.length;
boolean skipped_report_id = false;
byte report_number = report[0];
if (report_number == 0x0) {
++offset;
--length;
skipped_report_id = true;
}
res = mConnection.controlTransfer(
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
0x09/*HID set_report*/,
(3/*HID feature*/ << 8) | report_number,
mInterface,
report, offset, length,
1000/*timeout millis*/);
if (res < 0) {
Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
return -1;
}
if (skipped_report_id) {
++length;
}
return length;
}
@Override
public int sendOutputReport(byte[] report) {
int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
if (r != report.length) {
Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
}
return r;
}
@Override
public boolean getFeatureReport(byte[] report) {
int res = -1;
int offset = 0;
int length = report.length;
boolean skipped_report_id = false;
byte report_number = report[0];
if (report_number == 0x0) {
/* Offset the return buffer by 1, so that the report ID
will remain in byte 0. */
++offset;
--length;
skipped_report_id = true;
}
res = mConnection.controlTransfer(
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
0x01/*HID get_report*/,
(3/*HID feature*/ << 8) | report_number,
mInterface,
report, offset, length,
1000/*timeout millis*/);
if (res < 0) {
Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
return false;
}
if (skipped_report_id) {
++res;
++length;
}
byte[] data;
if (res == length) {
data = report;
} else {
data = Arrays.copyOfRange(report, 0, res);
}
mManager.HIDDeviceFeatureReport(mDeviceId, data);
return true;
}
@Override
public void close() {
mRunning = false;
if (mInputThread != null) {
while (mInputThread.isAlive()) {
mInputThread.interrupt();
try {
mInputThread.join();
} catch (InterruptedException e) {
// Keep trying until we're done
}
}
mInputThread = null;
}
if (mConnection != null) {
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
mConnection.releaseInterface(iface);
mConnection.close();
mConnection = null;
}
}
@Override
public void shutdown() {
close();
mManager = null;
}
@Override
public void setFrozen(boolean frozen) {
mFrozen = frozen;
}
protected class InputThread extends Thread {
@Override
public void run() {
int packetSize = mInputEndpoint.getMaxPacketSize();
byte[] packet = new byte[packetSize];
while (mRunning) {
int r;
try
{
r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
}
catch (Exception e)
{
Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
break;
}
if (r < 0) {
// Could be a timeout or an I/O error
}
if (r > 0) {
byte[] data;
if (r == packetSize) {
data = packet;
} else {
data = Arrays.copyOfRange(packet, 0, r);
}
if (!mFrozen) {
mManager.HIDDeviceInputReport(mDeviceId, data);
}
}
}
}
}
}

View File

@@ -0,0 +1,84 @@
package org.libsdl.app;
import android.content.Context;
import java.lang.reflect.*;
/**
SDL library initialization
*/
public class SDL {
// This function should be called first and sets up the native code
// so it can call into the Java classes
public static void setupJNI() {
SDLActivity.nativeSetupJNI();
SDLAudioManager.nativeSetupJNI();
SDLControllerManager.nativeSetupJNI();
}
// This function should be called each time the activity is started
public static void initialize() {
setContext(null);
SDLActivity.initialize();
SDLAudioManager.initialize();
SDLControllerManager.initialize();
}
// This function stores the current activity (SDL or not)
public static void setContext(Context context) {
mContext = context;
}
public static Context getContext() {
return mContext;
}
public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
if (libraryName == null) {
throw new NullPointerException("No library name provided.");
}
try {
// Let's see if we have ReLinker available in the project. This is necessary for
// some projects that have huge numbers of local libraries bundled, and thus may
// trip a bug in Android's native library loader which ReLinker works around. (If
// loadLibrary works properly, ReLinker will simply use the normal Android method
// internally.)
//
// To use ReLinker, just add it as a dependency. For more information, see
// https://github.com/KeepSafe/ReLinker for ReLinker's repository.
//
Class relinkClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");
Class relinkListenerClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener");
Class contextClass = mContext.getClassLoader().loadClass("android.content.Context");
Class stringClass = mContext.getClassLoader().loadClass("java.lang.String");
// Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if
// they've changed during updates.
Method forceMethod = relinkClass.getDeclaredMethod("force");
Object relinkInstance = forceMethod.invoke(null);
Class relinkInstanceClass = relinkInstance.getClass();
// Actually load the library!
Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass);
loadMethod.invoke(relinkInstance, mContext, libraryName, null, null);
}
catch (final Throwable e) {
// Fall back
try {
System.loadLibrary(libraryName);
}
catch (final UnsatisfiedLinkError ule) {
throw ule;
}
catch (final SecurityException se) {
throw se;
}
}
}
protected static Context mContext;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,387 @@
package org.libsdl.app;
import android.media.*;
import android.os.Build;
import android.util.Log;
public class SDLAudioManager
{
protected static final String TAG = "SDLAudio";
protected static AudioTrack mAudioTrack;
protected static AudioRecord mAudioRecord;
public static void initialize() {
mAudioTrack = null;
mAudioRecord = null;
}
// Audio
protected static String getAudioFormatString(int audioFormat) {
switch (audioFormat) {
case AudioFormat.ENCODING_PCM_8BIT:
return "8-bit";
case AudioFormat.ENCODING_PCM_16BIT:
return "16-bit";
case AudioFormat.ENCODING_PCM_FLOAT:
return "float";
default:
return Integer.toString(audioFormat);
}
}
protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
int channelConfig;
int sampleSize;
int frameSize;
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz");
/* On older devices let's use known good settings */
if (Build.VERSION.SDK_INT < 21) {
if (desiredChannels > 2) {
desiredChannels = 2;
}
if (sampleRate < 8000) {
sampleRate = 8000;
} else if (sampleRate > 48000) {
sampleRate = 48000;
}
}
if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
int minSDKVersion = (isCapture ? 23 : 21);
if (Build.VERSION.SDK_INT < minSDKVersion) {
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
}
}
switch (audioFormat)
{
case AudioFormat.ENCODING_PCM_8BIT:
sampleSize = 1;
break;
case AudioFormat.ENCODING_PCM_16BIT:
sampleSize = 2;
break;
case AudioFormat.ENCODING_PCM_FLOAT:
sampleSize = 4;
break;
default:
Log.v(TAG, "Requested format " + audioFormat + ", getting ENCODING_PCM_16BIT");
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
sampleSize = 2;
break;
}
if (isCapture) {
switch (desiredChannels) {
case 1:
channelConfig = AudioFormat.CHANNEL_IN_MONO;
break;
case 2:
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
break;
default:
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
desiredChannels = 2;
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
break;
}
} else {
switch (desiredChannels) {
case 1:
channelConfig = AudioFormat.CHANNEL_OUT_MONO;
break;
case 2:
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
break;
case 3:
channelConfig = AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
break;
case 4:
channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
break;
case 5:
channelConfig = AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
break;
case 6:
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
break;
case 7:
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
break;
case 8:
if (Build.VERSION.SDK_INT >= 23) {
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
} else {
Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround");
desiredChannels = 6;
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
}
break;
default:
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
desiredChannels = 2;
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
break;
}
/*
Log.v(TAG, "Speaker configuration (and order of channels):");
if ((channelConfig & 0x00000004) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT");
}
if ((channelConfig & 0x00000008) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT");
}
if ((channelConfig & 0x00000010) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_CENTER");
}
if ((channelConfig & 0x00000020) != 0) {
Log.v(TAG, " CHANNEL_OUT_LOW_FREQUENCY");
}
if ((channelConfig & 0x00000040) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_LEFT");
}
if ((channelConfig & 0x00000080) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_RIGHT");
}
if ((channelConfig & 0x00000100) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT_OF_CENTER");
}
if ((channelConfig & 0x00000200) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT_OF_CENTER");
}
if ((channelConfig & 0x00000400) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_CENTER");
}
if ((channelConfig & 0x00000800) != 0) {
Log.v(TAG, " CHANNEL_OUT_SIDE_LEFT");
}
if ((channelConfig & 0x00001000) != 0) {
Log.v(TAG, " CHANNEL_OUT_SIDE_RIGHT");
}
*/
}
frameSize = (sampleSize * desiredChannels);
// Let the user pick a larger buffer if they really want -- but ye
// gods they probably shouldn't, the minimums are horrifyingly high
// latency already
int minBufferSize;
if (isCapture) {
minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
} else {
minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
}
desiredFrames = Math.max(desiredFrames, (minBufferSize + frameSize - 1) / frameSize);
int[] results = new int[4];
if (isCapture) {
if (mAudioRecord == null) {
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
channelConfig, audioFormat, desiredFrames * frameSize);
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "Failed during initialization of AudioRecord");
mAudioRecord.release();
mAudioRecord = null;
return null;
}
mAudioRecord.startRecording();
}
results[0] = mAudioRecord.getSampleRate();
results[1] = mAudioRecord.getAudioFormat();
results[2] = mAudioRecord.getChannelCount();
results[3] = desiredFrames;
} else {
if (mAudioTrack == null) {
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
/* Try again, with safer values */
Log.e(TAG, "Failed during initialization of Audio Track");
mAudioTrack.release();
mAudioTrack = null;
return null;
}
mAudioTrack.play();
}
results[0] = mAudioTrack.getSampleRate();
results[1] = mAudioTrack.getAudioFormat();
results[2] = mAudioTrack.getChannelCount();
results[3] = desiredFrames;
}
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz");
return results;
}
/**
* This method is called by SDL using JNI.
*/
public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames);
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteFloatBuffer(float[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length;) {
int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(float)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteShortBuffer(short[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length;) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(short)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteByteBuffer(byte[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length; ) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(byte)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames);
}
/** This method is called by SDL using JNI. */
public static int captureReadFloatBuffer(float[] buffer, boolean blocking) {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
/** This method is called by SDL using JNI. */
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23) {
return mAudioRecord.read(buffer, 0, buffer.length);
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23) {
return mAudioRecord.read(buffer, 0, buffer.length);
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static void audioClose() {
if (mAudioTrack != null) {
mAudioTrack.stop();
mAudioTrack.release();
mAudioTrack = null;
}
}
/** This method is called by SDL using JNI. */
public static void captureClose() {
if (mAudioRecord != null) {
mAudioRecord.stop();
mAudioRecord.release();
mAudioRecord = null;
}
}
/** This method is called by SDL using JNI. */
public static void audioSetThreadPriority(boolean iscapture, int device_id) {
try {
/* Set thread name */
if (iscapture) {
Thread.currentThread().setName("SDLAudioC" + device_id);
} else {
Thread.currentThread().setName("SDLAudioP" + device_id);
}
/* Set thread priority */
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
} catch (Exception e) {
Log.v(TAG, "modify thread properties failed " + e.toString());
}
}
public static native int nativeSetupJNI();
}

View File

@@ -0,0 +1,788 @@
package org.libsdl.app;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import android.content.Context;
import android.os.*;
import android.view.*;
import android.util.Log;
public class SDLControllerManager
{
public static native int nativeSetupJNI();
public static native int nativeAddJoystick(int device_id, String name, String desc,
int vendor_id, int product_id,
boolean is_accelerometer, int button_mask,
int naxes, int nhats, int nballs);
public static native int nativeRemoveJoystick(int device_id);
public static native int nativeAddHaptic(int device_id, String name);
public static native int nativeRemoveHaptic(int device_id);
public static native int onNativePadDown(int device_id, int keycode);
public static native int onNativePadUp(int device_id, int keycode);
public static native void onNativeJoy(int device_id, int axis,
float value);
public static native void onNativeHat(int device_id, int hat_id,
int x, int y);
protected static SDLJoystickHandler mJoystickHandler;
protected static SDLHapticHandler mHapticHandler;
private static final String TAG = "SDLControllerManager";
public static void initialize() {
if (mJoystickHandler == null) {
if (Build.VERSION.SDK_INT >= 19) {
mJoystickHandler = new SDLJoystickHandler_API19();
} else {
mJoystickHandler = new SDLJoystickHandler_API16();
}
}
if (mHapticHandler == null) {
if (Build.VERSION.SDK_INT >= 26) {
mHapticHandler = new SDLHapticHandler_API26();
} else {
mHapticHandler = new SDLHapticHandler();
}
}
}
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
public static boolean handleJoystickMotionEvent(MotionEvent event) {
return mJoystickHandler.handleMotionEvent(event);
}
/**
* This method is called by SDL using JNI.
*/
public static void pollInputDevices() {
mJoystickHandler.pollInputDevices();
}
/**
* This method is called by SDL using JNI.
*/
public static void pollHapticDevices() {
mHapticHandler.pollHapticDevices();
}
/**
* This method is called by SDL using JNI.
*/
public static void hapticRun(int device_id, float intensity, int length) {
mHapticHandler.run(device_id, intensity, length);
}
/**
* This method is called by SDL using JNI.
*/
public static void hapticStop(int device_id)
{
mHapticHandler.stop(device_id);
}
// Check if a given device is considered a possible SDL joystick
public static boolean isDeviceSDLJoystick(int deviceId) {
InputDevice device = InputDevice.getDevice(deviceId);
// We cannot use InputDevice.isVirtual before API 16, so let's accept
// only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
if ((device == null) || (deviceId < 0)) {
return false;
}
int sources = device.getSources();
/* This is called for every button press, so let's not spam the logs */
/**
if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
Log.v(TAG, "Input device " + device.getName() + " has class joystick.");
}
if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
}
if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
}
**/
return ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ||
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
);
}
}
class SDLJoystickHandler {
/**
* Handles given MotionEvent.
* @param event the event to be handled.
* @return if given event was processed.
*/
public boolean handleMotionEvent(MotionEvent event) {
return false;
}
/**
* Handles adding and removing of input devices.
*/
public void pollInputDevices() {
}
}
/* Actual joystick functionality available for API >= 12 devices */
class SDLJoystickHandler_API16 extends SDLJoystickHandler {
static class SDLJoystick {
public int device_id;
public String name;
public String desc;
public ArrayList<InputDevice.MotionRange> axes;
public ArrayList<InputDevice.MotionRange> hats;
}
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
@Override
public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
// Some controllers, like the Moga Pro 2, return AXIS_GAS (22) for right trigger and AXIS_BRAKE (23) for left trigger - swap them so they're sorted in the right order for SDL
int arg0Axis = arg0.getAxis();
int arg1Axis = arg1.getAxis();
if (arg0Axis == MotionEvent.AXIS_GAS) {
arg0Axis = MotionEvent.AXIS_BRAKE;
} else if (arg0Axis == MotionEvent.AXIS_BRAKE) {
arg0Axis = MotionEvent.AXIS_GAS;
}
if (arg1Axis == MotionEvent.AXIS_GAS) {
arg1Axis = MotionEvent.AXIS_BRAKE;
} else if (arg1Axis == MotionEvent.AXIS_BRAKE) {
arg1Axis = MotionEvent.AXIS_GAS;
}
return arg0Axis - arg1Axis;
}
}
private ArrayList<SDLJoystick> mJoysticks;
public SDLJoystickHandler_API16() {
mJoysticks = new ArrayList<SDLJoystick>();
}
@Override
public void pollInputDevices() {
int[] deviceIds = InputDevice.getDeviceIds();
for(int i=0; i < deviceIds.length; ++i) {
SDLJoystick joystick = getJoystick(deviceIds[i]);
if (joystick == null) {
joystick = new SDLJoystick();
InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
if (SDLControllerManager.isDeviceSDLJoystick(deviceIds[i])) {
joystick.device_id = deviceIds[i];
joystick.name = joystickDevice.getName();
joystick.desc = getJoystickDescriptor(joystickDevice);
joystick.axes = new ArrayList<InputDevice.MotionRange>();
joystick.hats = new ArrayList<InputDevice.MotionRange>();
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
Collections.sort(ranges, new RangeComparator());
for (InputDevice.MotionRange range : ranges ) {
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
range.getAxis() == MotionEvent.AXIS_HAT_Y) {
joystick.hats.add(range);
}
else {
joystick.axes.add(range);
}
}
}
mJoysticks.add(joystick);
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, getVendorId(joystickDevice), getProductId(joystickDevice), false, getButtonMask(joystickDevice), joystick.axes.size(), joystick.hats.size()/2, 0);
}
}
}
/* Check removed devices */
ArrayList<Integer> removedDevices = new ArrayList<Integer>();
for(int i=0; i < mJoysticks.size(); i++) {
int device_id = mJoysticks.get(i).device_id;
int j;
for (j=0; j < deviceIds.length; j++) {
if (device_id == deviceIds[j]) break;
}
if (j == deviceIds.length) {
removedDevices.add(Integer.valueOf(device_id));
}
}
for(int i=0; i < removedDevices.size(); i++) {
int device_id = removedDevices.get(i).intValue();
SDLControllerManager.nativeRemoveJoystick(device_id);
for (int j=0; j < mJoysticks.size(); j++) {
if (mJoysticks.get(j).device_id == device_id) {
mJoysticks.remove(j);
break;
}
}
}
}
protected SDLJoystick getJoystick(int device_id) {
for(int i=0; i < mJoysticks.size(); i++) {
if (mJoysticks.get(i).device_id == device_id) {
return mJoysticks.get(i);
}
}
return null;
}
@Override
public boolean handleMotionEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
int actionPointerIndex = event.getActionIndex();
int action = event.getActionMasked();
switch(action) {
case MotionEvent.ACTION_MOVE:
SDLJoystick joystick = getJoystick(event.getDeviceId());
if ( joystick != null ) {
for (int i = 0; i < joystick.axes.size(); i++) {
InputDevice.MotionRange range = joystick.axes.get(i);
/* Normalize the value to -1...1 */
float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
SDLControllerManager.onNativeJoy(joystick.device_id, i, value );
}
for (int i = 0; i < joystick.hats.size(); i+=2) {
int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) );
int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) );
SDLControllerManager.onNativeHat(joystick.device_id, i/2, hatX, hatY );
}
}
break;
default:
break;
}
}
return true;
}
public String getJoystickDescriptor(InputDevice joystickDevice) {
String desc = joystickDevice.getDescriptor();
if (desc != null && !desc.isEmpty()) {
return desc;
}
return joystickDevice.getName();
}
public int getProductId(InputDevice joystickDevice) {
return 0;
}
public int getVendorId(InputDevice joystickDevice) {
return 0;
}
public int getButtonMask(InputDevice joystickDevice) {
return -1;
}
}
class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
@Override
public int getProductId(InputDevice joystickDevice) {
return joystickDevice.getProductId();
}
@Override
public int getVendorId(InputDevice joystickDevice) {
return joystickDevice.getVendorId();
}
@Override
public int getButtonMask(InputDevice joystickDevice) {
int button_mask = 0;
int[] keys = new int[] {
KeyEvent.KEYCODE_BUTTON_A,
KeyEvent.KEYCODE_BUTTON_B,
KeyEvent.KEYCODE_BUTTON_X,
KeyEvent.KEYCODE_BUTTON_Y,
KeyEvent.KEYCODE_BACK,
KeyEvent.KEYCODE_BUTTON_MODE,
KeyEvent.KEYCODE_BUTTON_START,
KeyEvent.KEYCODE_BUTTON_THUMBL,
KeyEvent.KEYCODE_BUTTON_THUMBR,
KeyEvent.KEYCODE_BUTTON_L1,
KeyEvent.KEYCODE_BUTTON_R1,
KeyEvent.KEYCODE_DPAD_UP,
KeyEvent.KEYCODE_DPAD_DOWN,
KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_RIGHT,
KeyEvent.KEYCODE_BUTTON_SELECT,
KeyEvent.KEYCODE_DPAD_CENTER,
// These don't map into any SDL controller buttons directly
KeyEvent.KEYCODE_BUTTON_L2,
KeyEvent.KEYCODE_BUTTON_R2,
KeyEvent.KEYCODE_BUTTON_C,
KeyEvent.KEYCODE_BUTTON_Z,
KeyEvent.KEYCODE_BUTTON_1,
KeyEvent.KEYCODE_BUTTON_2,
KeyEvent.KEYCODE_BUTTON_3,
KeyEvent.KEYCODE_BUTTON_4,
KeyEvent.KEYCODE_BUTTON_5,
KeyEvent.KEYCODE_BUTTON_6,
KeyEvent.KEYCODE_BUTTON_7,
KeyEvent.KEYCODE_BUTTON_8,
KeyEvent.KEYCODE_BUTTON_9,
KeyEvent.KEYCODE_BUTTON_10,
KeyEvent.KEYCODE_BUTTON_11,
KeyEvent.KEYCODE_BUTTON_12,
KeyEvent.KEYCODE_BUTTON_13,
KeyEvent.KEYCODE_BUTTON_14,
KeyEvent.KEYCODE_BUTTON_15,
KeyEvent.KEYCODE_BUTTON_16,
};
int[] masks = new int[] {
(1 << 0), // A -> A
(1 << 1), // B -> B
(1 << 2), // X -> X
(1 << 3), // Y -> Y
(1 << 4), // BACK -> BACK
(1 << 5), // MODE -> GUIDE
(1 << 6), // START -> START
(1 << 7), // THUMBL -> LEFTSTICK
(1 << 8), // THUMBR -> RIGHTSTICK
(1 << 9), // L1 -> LEFTSHOULDER
(1 << 10), // R1 -> RIGHTSHOULDER
(1 << 11), // DPAD_UP -> DPAD_UP
(1 << 12), // DPAD_DOWN -> DPAD_DOWN
(1 << 13), // DPAD_LEFT -> DPAD_LEFT
(1 << 14), // DPAD_RIGHT -> DPAD_RIGHT
(1 << 4), // SELECT -> BACK
(1 << 0), // DPAD_CENTER -> A
(1 << 15), // L2 -> ??
(1 << 16), // R2 -> ??
(1 << 17), // C -> ??
(1 << 18), // Z -> ??
(1 << 20), // 1 -> ??
(1 << 21), // 2 -> ??
(1 << 22), // 3 -> ??
(1 << 23), // 4 -> ??
(1 << 24), // 5 -> ??
(1 << 25), // 6 -> ??
(1 << 26), // 7 -> ??
(1 << 27), // 8 -> ??
(1 << 28), // 9 -> ??
(1 << 29), // 10 -> ??
(1 << 30), // 11 -> ??
(1 << 31), // 12 -> ??
// We're out of room...
0xFFFFFFFF, // 13 -> ??
0xFFFFFFFF, // 14 -> ??
0xFFFFFFFF, // 15 -> ??
0xFFFFFFFF, // 16 -> ??
};
boolean[] has_keys = joystickDevice.hasKeys(keys);
for (int i = 0; i < keys.length; ++i) {
if (has_keys[i]) {
button_mask |= masks[i];
}
}
return button_mask;
}
}
class SDLHapticHandler_API26 extends SDLHapticHandler {
@Override
public void run(int device_id, float intensity, int length) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
Log.d("SDL", "Rtest: Vibe with intensity " + intensity + " for " + length);
if (intensity == 0.0f) {
stop(device_id);
return;
}
int vibeValue = Math.round(intensity * 255);
if (vibeValue > 255) {
vibeValue = 255;
}
if (vibeValue < 1) {
stop(device_id);
return;
}
try {
haptic.vib.vibrate(VibrationEffect.createOneShot(length, vibeValue));
}
catch (Exception e) {
// Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
// something went horribly wrong with the Android 8.0 APIs.
haptic.vib.vibrate(length);
}
}
}
}
class SDLHapticHandler {
class SDLHaptic {
public int device_id;
public String name;
public Vibrator vib;
}
private ArrayList<SDLHaptic> mHaptics;
public SDLHapticHandler() {
mHaptics = new ArrayList<SDLHaptic>();
}
public void run(int device_id, float intensity, int length) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
haptic.vib.vibrate(length);
}
}
public void stop(int device_id) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
haptic.vib.cancel();
}
}
public void pollHapticDevices() {
final int deviceId_VIBRATOR_SERVICE = 999999;
boolean hasVibratorService = false;
int[] deviceIds = InputDevice.getDeviceIds();
// It helps processing the device ids in reverse order
// For example, in the case of the XBox 360 wireless dongle,
// so the first controller seen by SDL matches what the receiver
// considers to be the first controller
for (int i = deviceIds.length - 1; i > -1; i--) {
SDLHaptic haptic = getHaptic(deviceIds[i]);
if (haptic == null) {
InputDevice device = InputDevice.getDevice(deviceIds[i]);
Vibrator vib = device.getVibrator();
if (vib.hasVibrator()) {
haptic = new SDLHaptic();
haptic.device_id = deviceIds[i];
haptic.name = device.getName();
haptic.vib = vib;
mHaptics.add(haptic);
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
}
}
}
/* Check VIBRATOR_SERVICE */
Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (vib != null) {
hasVibratorService = vib.hasVibrator();
if (hasVibratorService) {
SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
if (haptic == null) {
haptic = new SDLHaptic();
haptic.device_id = deviceId_VIBRATOR_SERVICE;
haptic.name = "VIBRATOR_SERVICE";
haptic.vib = vib;
mHaptics.add(haptic);
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
}
}
}
/* Check removed devices */
ArrayList<Integer> removedDevices = new ArrayList<Integer>();
for(int i=0; i < mHaptics.size(); i++) {
int device_id = mHaptics.get(i).device_id;
int j;
for (j=0; j < deviceIds.length; j++) {
if (device_id == deviceIds[j]) break;
}
if (device_id == deviceId_VIBRATOR_SERVICE && hasVibratorService) {
// don't remove the vibrator if it is still present
} else if (j == deviceIds.length) {
removedDevices.add(device_id);
}
}
for(int i=0; i < removedDevices.size(); i++) {
int device_id = removedDevices.get(i);
SDLControllerManager.nativeRemoveHaptic(device_id);
for (int j=0; j < mHaptics.size(); j++) {
if (mHaptics.get(j).device_id == device_id) {
mHaptics.remove(j);
break;
}
}
}
}
protected SDLHaptic getHaptic(int device_id) {
for(int i=0; i < mHaptics.size(); i++) {
if (mHaptics.get(i).device_id == device_id) {
return mHaptics.get(i);
}
}
return null;
}
}
class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
// Generic Motion (mouse hover, joystick...) events go here
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
float x, y;
int action;
switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK:
case InputDevice.SOURCE_GAMEPAD:
case InputDevice.SOURCE_DPAD:
return SDLControllerManager.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE:
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
default:
break;
}
break;
default:
break;
}
// Event was not managed
return false;
}
public boolean supportsRelativeMouse() {
return false;
}
public boolean inRelativeMode() {
return false;
}
public boolean setRelativeMouseEnabled(boolean enabled) {
return false;
}
public void reclaimRelativeMouseModeIfNeeded()
{
}
public float getEventX(MotionEvent event) {
return event.getX(0);
}
public float getEventY(MotionEvent event) {
return event.getY(0);
}
}
class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 {
// Generic Motion (mouse hover, joystick...) events go here
private boolean mRelativeModeEnabled;
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
// Handle relative mouse mode
if (mRelativeModeEnabled) {
if (event.getSource() == InputDevice.SOURCE_MOUSE) {
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_HOVER_MOVE) {
float x = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
float y = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
}
}
}
// Event was not managed, call SDLGenericMotionListener_API12 method
return super.onGenericMotion(v, event);
}
@Override
public boolean supportsRelativeMouse() {
return true;
}
@Override
public boolean inRelativeMode() {
return mRelativeModeEnabled;
}
@Override
public boolean setRelativeMouseEnabled(boolean enabled) {
mRelativeModeEnabled = enabled;
return true;
}
@Override
public float getEventX(MotionEvent event) {
if (mRelativeModeEnabled) {
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
}
else {
return event.getX(0);
}
}
@Override
public float getEventY(MotionEvent event) {
if (mRelativeModeEnabled) {
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
}
else {
return event.getY(0);
}
}
}
class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
// Generic Motion (mouse hover, joystick...) events go here
private boolean mRelativeModeEnabled;
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
float x, y;
int action;
switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK:
case InputDevice.SOURCE_GAMEPAD:
case InputDevice.SOURCE_DPAD:
return SDLControllerManager.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE:
// DeX desktop mouse cursor is a separate non-standard input type.
case InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN:
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
default:
break;
}
break;
case InputDevice.SOURCE_MOUSE_RELATIVE:
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
default:
break;
}
break;
default:
break;
}
// Event was not managed
return false;
}
@Override
public boolean supportsRelativeMouse() {
return (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27));
}
@Override
public boolean inRelativeMode() {
return mRelativeModeEnabled;
}
@Override
public boolean setRelativeMouseEnabled(boolean enabled) {
if (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27)) {
if (enabled) {
SDLActivity.getContentView().requestPointerCapture();
}
else {
SDLActivity.getContentView().releasePointerCapture();
}
mRelativeModeEnabled = enabled;
return true;
}
else
{
return false;
}
}
@Override
public void reclaimRelativeMouseModeIfNeeded()
{
if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
SDLActivity.getContentView().requestPointerCapture();
}
}
@Override
public float getEventX(MotionEvent event) {
// Relative mouse in capture mode will only have relative for X/Y
return event.getX(0);
}
@Override
public float getEventY(MotionEvent event) {
// Relative mouse in capture mode will only have relative for X/Y
return event.getY(0);
}
}

View File

@@ -0,0 +1,105 @@
package org.thecodedeposit.aerofoil;
import org.libsdl.app.SDLActivity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.res.AssetManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
public class GpActivity extends SDLActivity
{
private static final int SOURCE_EXPORT_REQUEST_ID = 20;
private AssetManager assetManager;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
assetManager = getAssets();
}
public String[] scanAssetDirectory(String directory)
{
try
{
return this.assetManager.list(directory);
}
catch (java.io.IOException ex)
{
return new String[0];
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent)
{
if (requestCode == SOURCE_EXPORT_REQUEST_ID)
{
if (resultCode == RESULT_OK)
{
Uri uri = intent.getData();
Context context = getContext();
ContentResolver contentResolver = context.getContentResolver();
try
{
ParcelFileDescriptor fd = contentResolver.openFileDescriptor(uri, "w");
GpFileSystemAPI.nativePostSourceExportRequest(false, fd.getFd(), fd);
}
catch (FileNotFoundException e)
{
GpFileSystemAPI.nativePostSourceExportRequest(true, 0, null);
return;
}
catch (IOException e)
{
GpFileSystemAPI.nativePostSourceExportRequest(true, 0, null);
return;
}
catch (Exception e)
{
GpFileSystemAPI.nativePostSourceExportRequest(true, 0, null);
return;
}
}
else
GpFileSystemAPI.nativePostSourceExportRequest(true, 0, null);
}
else
{
super.onActivityResult(requestCode, resultCode, intent);
}
}
public void selectSourceExportPath(String fname)
{
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT)
.setType("application/zip")
.addCategory(Intent.CATEGORY_OPENABLE)
.putExtra(Intent.EXTRA_TITLE, fname);
startActivityForResult(intent, SOURCE_EXPORT_REQUEST_ID);
}
public void closeSourceExportPFD(Object obj)
{
try
{
((ParcelFileDescriptor) obj).close();
}
catch (IOException e)
{
}
}
}

View File

@@ -0,0 +1,6 @@
package org.thecodedeposit.aerofoil;
public class GpFileSystemAPI
{
public static native void nativePostSourceExportRequest(boolean cancelled, int fd, Object pfd);
}

View File

@@ -0,0 +1,5 @@
package org.thecodedeposit.aerofoil;
public class GpSystemServices
{
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 746 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 966 B

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Aerofoil</string>
</resources>

View File

@@ -0,0 +1,8 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -0,0 +1,25 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
google()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@@ -0,0 +1,12 @@
cd app
cd src
cd main
cd assets
rmdir /S /Q Packaged
mkdir Packaged
cd Packaged
rmdir /S /Q Houses
mkdir Houses
copy ..\..\..\..\..\..\Packaged\*.gpf .\
copy ..\..\..\..\..\..\Packaged\Houses\* Houses\
cd ..

View File

@@ -0,0 +1,9 @@
cd ..
copy /Y DefaultTimestamp.timestamp AerofoilAndroid\app\src\main\assets\Packaged\DefaultTimestamp.timestamp
del AerofoilAndroid\app\src\main\assets\Packaged\SourceCode.zip
del AerofoilAndroid\app\src\main\assets\Packaged\SourceCode.pkg
git archive -0 --format zip -o AerofoilAndroid\app\src\main\assets\Packaged\SourceCode.zip HEAD
tools\7z.exe d AerofoilAndroid\app\src\main\assets\Packaged\SourceCode.zip GliderProData\
cd AerofoilAndroid\app\src\main\assets\Packaged
rename SourceCode.zip SourceCode.pkg
pause

View File

@@ -0,0 +1,17 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#Sat Oct 17 02:06:49 EDT 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip

160
AerofoilAndroid/gradlew vendored Normal file
View File

@@ -0,0 +1,160 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

90
AerofoilAndroid/gradlew.bat vendored Normal file
View File

@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,19 @@
@setlocal enableextensions
@cd /d "%~dp0"
call remove_symlinks.bat
mklink /D app\jni\AerofoilSDL ..\..\..\AerofoilSDL
mklink /D app\jni\AerofoilPortable ..\..\..\AerofoilPortable
mklink /D app\jni\Common ..\..\..\Common
mklink /D app\jni\SDL2 ..\..\..\SDL2-2.0.12
mklink /D app\jni\GpApp ..\..\..\GpApp
mklink /D app\jni\GpShell ..\..\..\GpShell
mklink /D app\jni\GpCommon ..\..\..\GpCommon
mklink /D app\jni\PortabilityLayer ..\..\..\PortabilityLayer
mklink /D app\jni\rapidjson ..\..\..\rapidjson
mklink /D app\jni\MacRomanConversion ..\..\..\MacRomanConversion
mklink /D app\jni\stb ..\..\..\stb
pause

View File

@@ -0,0 +1,14 @@
@setlocal enableextensions
@cd /d "%~dp0"
rmdir app\jni\AerofoilSDL
rmdir app\jni\AerofoilPortable
rmdir app\jni\Common
rmdir app\jni\SDL2
rmdir app\jni\GpShell
rmdir app\jni\GpCommon
rmdir app\jni\GpApp
rmdir app\jni\PortabilityLayer
rmdir app\jni\rapidjson
rmdir app\jni\MacRomanConversion
rmdir app\jni\stb

View File

@@ -0,0 +1 @@
include ':app'

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:">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1240"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5C54D0952629B42100AB55E0"
BuildableName = "Aerofoil.app"
BlueprintName = "Aerofoil"
ReferencedContainer = "container:AerofoilMac.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5C54D0952629B42100AB55E0"
BuildableName = "Aerofoil.app"
BlueprintName = "Aerofoil"
ReferencedContainer = "container:AerofoilMac.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "-diagnostics"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5C54D0952629B42100AB55E0"
BuildableName = "Aerofoil.app"
BlueprintName = "Aerofoil"
ReferencedContainer = "container:AerofoilMac.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,9 @@
#import <Cocoa/Cocoa.h>
NS_ASSUME_NONNULL_BEGIN
@interface AerofoilAppDelegate : NSObject <NSApplicationDelegate>
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,43 @@
#import "AerofoilAppDelegate.h"
#import "AerofoilApplication.h"
#include "WindowManager.h"
#include "GliderDefines.h" // kPlayMode
extern short theMode;
@interface AerofoilAppDelegate ()
@property (weak) IBOutlet NSMenuItem *aboutAerofoilMenuItem;
@property (weak) IBOutlet NSMenuItem *aboutGliderPROMenuItem;
@property (weak) IBOutlet NSMenuItem *preferencesMenuItem;
@end
@implementation AerofoilAppDelegate
- (IBAction)showAboutAerofoil:(id)sender {
[NSApp sendMenuItemEvent:GpMenuItemSelectionEvents::kAboutAerofoil];
}
- (IBAction)showAboutGliderPRO:(id)sender {
[NSApp sendMenuItemEvent:GpMenuItemSelectionEvents::kAboutGliderPRO];
}
- (IBAction)showPreferences:(id)sender {
[NSApp sendMenuItemEvent:GpMenuItemSelectionEvents::kPreferences];
}
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
if (menuItem == _aboutAerofoilMenuItem || menuItem == _aboutGliderPROMenuItem) {
return ![self menuItemsDisabled];
} else if (menuItem == _preferencesMenuItem) {
return ![self menuItemsDisabled];
} else {
return NO;
}
}
- (BOOL)menuItemsDisabled {
PortabilityLayer::WindowManager* wm = PortabilityLayer::WindowManager::GetInstance();
return theMode == kPlayMode || wm->IsExclusiveWindowVisible();
}
@end

View File

@@ -0,0 +1,12 @@
#import <Cocoa/Cocoa.h>
#include "GpVOSEvent.h"
NS_ASSUME_NONNULL_BEGIN
@interface AerofoilApplication : NSApplication
- (void)sendMenuItemEvent:(GpMenuItemSelectionEvent_t)itemEvent;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,28 @@
#import "AerofoilApplication.h"
#include "IGpVOSEventQueue.h"
#include "GpAppInterface.h"
#include "SDL.h"
@implementation AerofoilApplication
- (void)terminate:(id)sender {
SDL_Event event;
event.quit.type = SDL_QUIT;
event.quit.timestamp = SDL_GetTicks();
SDL_PushEvent(&event);
}
- (void)sendMenuItemEvent:(GpMenuItemSelectionEvent_t)itemEvent {
GpVOSEvent event;
event.m_eventType = GpVOSEventTypes::kMenuItemSelected;
event.m_event.m_menuItemSelectionEvent = itemEvent;
[self sendVOSEvent:event];
}
- (void)sendVOSEvent:(GpVOSEvent)event {
IGpVOSEventQueue* queue = GpAppInterface_Get()->PL_GetDriverCollection()->GetDriver<GpDriverIDs::kEventQueue>();
if (GpVOSEvent *evt = queue->QueueEvent())
*evt = event;
}
@end

View File

@@ -0,0 +1,12 @@
<?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>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

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