Compare commits
183 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1fe94e4f06 | ||
|
5f2f73e176 | ||
|
7c5864d59b | ||
|
d698ff23db | ||
|
92cb961208 | ||
|
545798600e | ||
|
9ba15c6d78 | ||
|
0706640bc9 | ||
|
bdb7ddbdbb | ||
|
c5dee3ce9e | ||
|
7e3569500a | ||
|
376fdf16c4 | ||
|
40b38046b9 | ||
|
5b5fb15780 | ||
|
744b06796d | ||
|
7ba11df286 | ||
|
9f5699a61e | ||
|
5ab966ea98 | ||
|
42ec9e3646 | ||
|
83978d0397 | ||
|
82fe38dfc7 | ||
|
2c90110668 | ||
|
eac270670d | ||
|
c04aeeb962 | ||
|
f9109850a6 | ||
|
71955ac729 | ||
|
f36a8da95f | ||
|
0e3534d902 | ||
|
90943d945b | ||
|
00488c6fea | ||
|
f16ffa0c4c | ||
|
e098370249 | ||
|
c3eb23af4b | ||
|
a32b33ef1b | ||
|
b83fd1b28f | ||
|
ab6c447a2f | ||
|
e02919f1fc | ||
|
ccceec8b3c | ||
|
40c1a39c40 | ||
|
c27d78d329 | ||
|
66cce6bcd4 | ||
|
b616c6bf6e | ||
|
2e9954677d | ||
|
3d5265258e | ||
|
6103007ed2 | ||
|
240ba88f53 | ||
|
71115b2366 | ||
|
2bde3bc60b | ||
|
0524c312d0 | ||
|
8a69d7b921 | ||
|
20a3918535 | ||
|
bd61b23bde | ||
|
b1bebc2afd | ||
|
5b16ed826f | ||
|
567b82eb23 | ||
|
e8565a122d | ||
|
32ff2f6fe8 | ||
|
95260f8d8a | ||
|
e7b02f37a5 | ||
|
781c6ce610 | ||
|
98c7a1eea6 | ||
|
0a9b5ded9b | ||
|
09485b7cf4 | ||
|
6fd1edd2c8 | ||
|
6858230dde | ||
|
a6486c137b | ||
|
318a5868d7 | ||
|
295db5f064 | ||
|
0def879db1 | ||
|
ac56badffa | ||
|
75923ad7b0 | ||
|
32d96798cc | ||
|
c354d49e9f | ||
|
24169507bd | ||
|
d54ad576bc | ||
|
222927d56f | ||
|
d74c4b70f3 | ||
|
fb7b9b02d9 | ||
|
1ac8032411 | ||
|
a012c6d3e7 | ||
|
08c6fd5108 | ||
|
a7cf9d48e3 | ||
|
d6ce07685e | ||
|
5e060279af | ||
|
a2ca1725fa | ||
|
f32866510e | ||
|
ac15c26bef | ||
|
9510aaee32 | ||
|
c6aa82db35 | ||
|
6d12b6ff1a | ||
|
c3b2a7e8af | ||
|
b0d5673d95 | ||
|
14ef73a8ab | ||
|
456a134041 | ||
|
27ae449353 | ||
|
0de26b5e71 | ||
|
65d4784618 | ||
|
5187ef5dc6 | ||
|
b96acad0ca | ||
|
2277d1340c | ||
|
57798c05c6 | ||
|
f24b0a0e77 | ||
|
7442b92dd3 | ||
|
0725828a5f | ||
|
c5a1303b71 | ||
|
a2d374f650 | ||
|
472462c535 | ||
|
80683464af | ||
|
2cd4e4f178 | ||
|
07df94fb00 | ||
|
d6b206195d | ||
|
65e68f790a | ||
|
56d999f400 | ||
|
2991a7490e | ||
|
0c43370353 | ||
|
9ba5b14db6 | ||
|
5e1f5655a0 | ||
|
67b594fe68 | ||
|
0385c28002 | ||
|
19b1a307e7 | ||
|
9341c4c378 | ||
|
5d9dde6589 | ||
|
f9d3b91f72 | ||
|
1dc86a703e | ||
|
82aaefe342 | ||
|
d77cf6f5db | ||
|
a9b6d1dc4f | ||
|
08349fc6db | ||
|
77e02927c6 | ||
|
c473271e4e | ||
|
84a4d16aed | ||
|
f15a87041a | ||
|
d826a908e9 | ||
|
3d3f839801 | ||
|
141f840888 | ||
|
22ae442f87 | ||
|
5dd8bf28f5 | ||
|
bd422d0eb3 | ||
|
4e8e76f8fc | ||
|
bf1dad34c3 | ||
|
c49bab04d5 | ||
|
e6df94dac0 | ||
|
71f27f0a05 | ||
|
b9fe553ff8 | ||
|
7ae31bc7fc | ||
|
2234190f24 | ||
|
d62de80ff1 | ||
|
03f07d7c4f | ||
|
b90d0c303c | ||
|
7842e721e5 | ||
|
93195207dc | ||
|
59741dd966 | ||
|
1ac69c2c75 | ||
|
69f59f769d | ||
|
bb2af5abb3 | ||
|
3692a966bf | ||
|
7916c72fc4 | ||
|
7951f579e0 | ||
|
b64c904558 | ||
|
a6f6ffcdcc | ||
|
6fb45f480b | ||
|
9ba0e9f13d | ||
|
0df94405f8 | ||
|
ae69696cbd | ||
|
50f420d2b1 | ||
|
c87f238563 | ||
|
48fe83bb33 | ||
|
b2b27ef335 | ||
|
89831f0ff6 | ||
|
99fd71196d | ||
|
7060676b73 | ||
|
184f867f79 | ||
|
cbd759c754 | ||
|
2ada8029d0 | ||
|
8e0d8e261e | ||
|
20b9eef64d | ||
|
c147e1100e | ||
|
0a2e730d26 | ||
|
922cd0fd06 | ||
|
b8bf6be44f | ||
|
2897c4ffab | ||
|
35c174984b | ||
|
da16edea8d |
29
.gitignore
vendored
@@ -20,6 +20,9 @@
|
||||
*.idb
|
||||
*.aps
|
||||
*.res
|
||||
*.a
|
||||
*.recipe
|
||||
*.FileListAbsolute.txt
|
||||
.vs/*
|
||||
Packaged/*
|
||||
DebugData/*
|
||||
@@ -30,9 +33,35 @@ InstallerPackages/*
|
||||
*.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
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
322
ASADTool/ASADTool.cpp
Normal 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
@@ -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>
|
22
ASADTool/ASADTool.vcxproj.filters
Normal 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>
|
26
Aerofoil.sln
@@ -48,6 +48,8 @@ Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ReleasePackageInstaller", "
|
||||
{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
|
||||
@@ -63,6 +65,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AerofoilSDL", "AerofoilSDL\
|
||||
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
|
||||
@@ -175,6 +185,22 @@ Global
|
||||
{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
|
||||
|
@@ -14,19 +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|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>
|
||||
@@ -43,6 +43,7 @@
|
||||
<Import Project="..\GpMainApp.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" />
|
||||
@@ -52,6 +53,7 @@
|
||||
<Import Project="..\GpMainApp.props" />
|
||||
<Import Project="..\Release.props" />
|
||||
<Import Project="..\GpShell.props" />
|
||||
<Import Project="..\AerofoilPortable.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
@@ -82,16 +84,15 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\AerofoilPortable\GpAllocator_C.cpp" />
|
||||
<ClCompile Include="GpBWCursor_Win32.cpp" />
|
||||
<ClCompile Include="GpColorCursor_Win32.cpp" />
|
||||
<ClCompile Include="GpFiber_Win32.cpp" />
|
||||
<ClCompile Include="GpFileStream_Win32.cpp" />
|
||||
<ClCompile Include="GpFileSystem_Win32.cpp" />
|
||||
<ClCompile Include="GpLogDriver_Win32.cpp" />
|
||||
<ClCompile Include="GpMain_Win32.cpp" />
|
||||
<ClCompile Include="GpMutex_Win32.cpp" />
|
||||
<ClCompile Include="GpSystemServices_Win32.cpp" />
|
||||
<ClCompile Include="GpFiberStarter_Win32.cpp" />
|
||||
<ClCompile Include="GpThreadEvent_Win32.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -102,13 +103,14 @@
|
||||
<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\IGpDisplayDriverSurface.h" />
|
||||
<ClInclude Include="..\GpCommon\IGpLogDriver.h" />
|
||||
<ClInclude Include="..\GpCommon\IGpPrefsHandler.h" />
|
||||
<ClInclude Include="GpBWCursor_Win32.h" />
|
||||
<ClInclude Include="GpFiber_Win32.h" />
|
||||
<ClInclude Include="GpFileStream_Win32.h" />
|
||||
<ClInclude Include="GpFileSystem_Win32.h" />
|
||||
<ClInclude Include="GpLogDriver_Win32.h" />
|
||||
@@ -127,9 +129,6 @@
|
||||
<ProjectReference Include="..\GpDisplayDriver_D3D11\GpDisplayDriver_D3D11.vcxproj">
|
||||
<Project>{ffc961ac-55b4-4a38-a83e-06ae98f59acc}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GpFontHandler_FreeType2\GpFontHandler_FreeType2.vcxproj">
|
||||
<Project>{4b564030-8985-4975-91e1-e1b2c16ae2a1}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GpInputDriver_XInput\GpInputDriver_XInput.vcxproj">
|
||||
<Project>{17b96f07-ef92-47cd-95a5-8e6ee38ab564}</Project>
|
||||
</ProjectReference>
|
||||
|
@@ -4,12 +4,6 @@
|
||||
<ClCompile Include="GpColorCursor_Win32.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GpFiber_Win32.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GpFiberStarter_Win32.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GpFileStream_Win32.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -34,6 +28,9 @@
|
||||
<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">
|
||||
@@ -48,9 +45,6 @@
|
||||
<ClInclude Include="..\GpCommon\GpDisplayDriverTickStatus.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GpFiber_Win32.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\GpCommon\GpFileCreationDisposition.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -99,6 +93,12 @@
|
||||
<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">
|
||||
|
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "GpBWCursor_Win32.h"
|
||||
#include "GpWindows.h"
|
||||
#include "IGpAllocator.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@@ -34,19 +35,19 @@ void GpBWCursor_Win32::Destroy()
|
||||
this->DecRef();
|
||||
}
|
||||
|
||||
IGpCursor_Win32 *GpBWCursor_Win32::Create(size_t width, size_t height, const void *pixelData, const void *maskData, size_t hotSpotX, size_t hotSpotY)
|
||||
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 = (width * height + 7) / 8;
|
||||
uint8_t *convertedAndData = static_cast<uint8_t*>(malloc(numBytes));
|
||||
uint8_t *convertedXorData = static_cast<uint8_t*>(malloc(numBytes));
|
||||
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)
|
||||
free(convertedAndData);
|
||||
alloc->Release(convertedAndData);
|
||||
if (convertedXorData)
|
||||
free(convertedXorData);
|
||||
alloc->Release(convertedXorData);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@@ -62,25 +63,26 @@ IGpCursor_Win32 *GpBWCursor_Win32::Create(size_t width, size_t height, const voi
|
||||
|
||||
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);
|
||||
|
||||
free(convertedAndData);
|
||||
free(convertedXorData);
|
||||
alloc->Release(convertedAndData);
|
||||
alloc->Release(convertedXorData);
|
||||
|
||||
if (!hcursor)
|
||||
return nullptr;
|
||||
|
||||
void *storage = malloc(sizeof(GpBWCursor_Win32));
|
||||
void *storage = alloc->Alloc(sizeof(GpBWCursor_Win32));
|
||||
if (!storage)
|
||||
{
|
||||
DestroyCursor(hcursor);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new (storage) GpBWCursor_Win32(hcursor);
|
||||
return new (storage) GpBWCursor_Win32(hcursor, alloc);
|
||||
}
|
||||
|
||||
GpBWCursor_Win32::GpBWCursor_Win32(HCURSOR cursor)
|
||||
GpBWCursor_Win32::GpBWCursor_Win32(HCURSOR cursor, IGpAllocator *alloc)
|
||||
: m_cursor(cursor)
|
||||
, m_refCount(1)
|
||||
, m_alloc(alloc)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -104,7 +106,8 @@ void GpBWCursor_Win32::DecRef()
|
||||
m_refCount--;
|
||||
if (m_refCount == 0)
|
||||
{
|
||||
IGpAllocator *alloc = m_alloc;
|
||||
this->~GpBWCursor_Win32();
|
||||
free(this);
|
||||
alloc->Release(this);
|
||||
}
|
||||
}
|
||||
|
@@ -3,6 +3,8 @@
|
||||
#include "IGpCursor_Win32.h"
|
||||
#include "GpWindows.h"
|
||||
|
||||
struct IGpAllocator;
|
||||
|
||||
class GpBWCursor_Win32 final : public IGpCursor_Win32
|
||||
{
|
||||
public:
|
||||
@@ -13,12 +15,13 @@ public:
|
||||
void IncRef() override;
|
||||
void DecRef() override;
|
||||
|
||||
static IGpCursor_Win32 *Create(size_t width, size_t height, const void *pixelData, const void *maskData, size_t hotSpotX, size_t hotSpotY);
|
||||
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);
|
||||
GpBWCursor_Win32(HCURSOR cursor, IGpAllocator *alloc);
|
||||
~GpBWCursor_Win32();
|
||||
|
||||
HCURSOR m_cursor;
|
||||
int m_refCount;
|
||||
IGpAllocator *m_alloc;
|
||||
};
|
||||
|
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include "GpColorCursor_Win32.h"
|
||||
#include "IGpAllocator.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@@ -31,7 +32,7 @@ void GpColorCursor_Win32::Destroy()
|
||||
this->DecRef();
|
||||
}
|
||||
|
||||
IGpCursor_Win32 *GpColorCursor_Win32::Create(size_t width, size_t height, const void *pixelDataRGBA, size_t hotSpotX, size_t hotSpotY)
|
||||
IGpCursor_Win32 *GpColorCursor_Win32::Create(IGpAllocator *alloc, size_t width, size_t height, const void *pixelDataRGBA, size_t hotSpotX, size_t hotSpotY)
|
||||
{
|
||||
const size_t paddingBits = (sizeof(void*) * 8);
|
||||
|
||||
@@ -52,7 +53,7 @@ IGpCursor_Win32 *GpColorCursor_Win32::Create(size_t width, size_t height, const
|
||||
size_t maskPitch = width + paddingBits - 1;
|
||||
maskPitch -= maskPitch % paddingBits;
|
||||
|
||||
LPVOID maskBits = malloc(maskPitch * height);
|
||||
LPVOID maskBits = alloc->Alloc(maskPitch * height);
|
||||
if (!maskBits)
|
||||
return nullptr;
|
||||
|
||||
@@ -71,6 +72,8 @@ IGpCursor_Win32 *GpColorCursor_Win32::Create(size_t width, size_t height, const
|
||||
ii.hbmMask = CreateBitmap(width, height, 1, 1, maskBits);
|
||||
ReleaseDC(NULL, hdc);
|
||||
|
||||
alloc->Release(maskBits);
|
||||
|
||||
size_t cursorPitch = width * 4;
|
||||
|
||||
size_t numPixels = width * height;
|
||||
@@ -90,19 +93,20 @@ IGpCursor_Win32 *GpColorCursor_Win32::Create(size_t width, size_t height, const
|
||||
if (!hicon)
|
||||
return nullptr;
|
||||
|
||||
void *storage = malloc(sizeof(GpColorCursor_Win32));
|
||||
void *storage = alloc->Alloc(sizeof(GpColorCursor_Win32));
|
||||
if (!storage)
|
||||
{
|
||||
DestroyIcon(hicon);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new (storage) GpColorCursor_Win32(reinterpret_cast<HCURSOR>(hicon));
|
||||
return new (storage) GpColorCursor_Win32(alloc, reinterpret_cast<HCURSOR>(hicon));
|
||||
}
|
||||
|
||||
GpColorCursor_Win32::GpColorCursor_Win32(HCURSOR cursor)
|
||||
GpColorCursor_Win32::GpColorCursor_Win32(IGpAllocator *alloc, HCURSOR cursor)
|
||||
: m_cursor(cursor)
|
||||
, m_refCount(1)
|
||||
, m_alloc(alloc)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -126,7 +130,8 @@ void GpColorCursor_Win32::DecRef()
|
||||
m_refCount--;
|
||||
if (m_refCount == 0)
|
||||
{
|
||||
IGpAllocator *alloc = m_alloc;
|
||||
this->~GpColorCursor_Win32();
|
||||
free(this);
|
||||
alloc->Release(this);
|
||||
}
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@
|
||||
#include "IGpCursor_Win32.h"
|
||||
#include "GpWindows.h"
|
||||
|
||||
struct IGpAllocator;
|
||||
|
||||
class GpColorCursor_Win32 final : public IGpCursor_Win32
|
||||
{
|
||||
@@ -14,12 +15,13 @@ public:
|
||||
void IncRef() override;
|
||||
void DecRef() override;
|
||||
|
||||
static IGpCursor_Win32 *Create(size_t width, size_t height, const void *pixelDataRGBA, size_t hotSpotX, size_t hotSpotY);
|
||||
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;
|
||||
};
|
||||
|
@@ -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;
|
||||
SwitchToFiber(static_cast<GpFiber_Win32*>(creatingFiber)->GetFiber());
|
||||
|
||||
threadFunc(context);
|
||||
|
||||
assert(!"Fiber function exited");
|
||||
}
|
||||
}
|
||||
|
||||
IGpFiber *GpFiberStarter::StartFiber(IGpSystemServices *systemServices, 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);
|
||||
}
|
@@ -1,37 +0,0 @@
|
||||
#include "GpFiber_Win32.h"
|
||||
#include <new>
|
||||
|
||||
GpFiber_Win32::GpFiber_Win32(LPVOID fiber)
|
||||
: m_fiber(fiber)
|
||||
{
|
||||
}
|
||||
|
||||
void GpFiber_Win32::YieldTo(IGpFiber *toFiber)
|
||||
{
|
||||
SwitchToFiber(static_cast<GpFiber_Win32*>(toFiber)->m_fiber);
|
||||
}
|
||||
|
||||
void GpFiber_Win32::YieldToTerminal(IGpFiber *toFiber)
|
||||
{
|
||||
YieldTo(toFiber);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
#include "GpWindows.h"
|
||||
#include "IGpFiber.h"
|
||||
|
||||
class GpFiber_Win32 final : public IGpFiber
|
||||
{
|
||||
public:
|
||||
void YieldTo(IGpFiber *toFiber) override;
|
||||
void YieldToTerminal(IGpFiber *toFiber) override;
|
||||
void Destroy() override;
|
||||
|
||||
static IGpFiber *Create(LPVOID fiber);
|
||||
|
||||
LPVOID GetFiber() const;
|
||||
|
||||
private:
|
||||
explicit GpFiber_Win32(LPVOID fiber);
|
||||
~GpFiber_Win32();
|
||||
|
||||
LPVOID m_fiber;
|
||||
};
|
||||
|
||||
inline LPVOID GpFiber_Win32::GetFiber() const
|
||||
{
|
||||
return m_fiber;
|
||||
}
|
@@ -1,13 +1,32 @@
|
||||
#include "GpFileStream_Win32.h"
|
||||
#include "IGpAllocator.h"
|
||||
|
||||
GpFileStream_Win32::GpFileStream_Win32(HANDLE handle, bool readable, bool writeable, bool seekable)
|
||||
: m_handle(handle)
|
||||
#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)
|
||||
@@ -122,7 +141,10 @@ GpUFilePos_t GpFileStream_Win32::Tell() const
|
||||
|
||||
void GpFileStream_Win32::Close()
|
||||
{
|
||||
CloseHandle(m_handle);
|
||||
IGpAllocator *alloc = m_alloc;
|
||||
this->~GpFileStream_Win32();
|
||||
|
||||
alloc->Release(this);
|
||||
}
|
||||
|
||||
void GpFileStream_Win32::Flush()
|
||||
|
@@ -7,7 +7,7 @@
|
||||
class GpFileStream_Win32 final : public GpIOStream
|
||||
{
|
||||
public:
|
||||
explicit GpFileStream_Win32(HANDLE handle, bool readable, bool writeable, bool seekable);
|
||||
~GpFileStream_Win32();
|
||||
|
||||
size_t Read(void *bytesOut, size_t size) override;
|
||||
size_t Write(const void *bytes, size_t size) override;
|
||||
@@ -19,10 +19,19 @@ public:
|
||||
bool SeekEnd(GpUFilePos_t loc) override;
|
||||
GpUFilePos_t Size() const override;
|
||||
GpUFilePos_t Tell() const override;
|
||||
void Close() 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;
|
||||
|
@@ -1,44 +1,49 @@
|
||||
#include "GpFileSystem_Win32.h"
|
||||
|
||||
#include "GpAllocator_C.h"
|
||||
#include "GpApplicationName.h"
|
||||
#include "GpFileStream_Win32.h"
|
||||
#include "GpWindows.h"
|
||||
#include "IGpAllocator.h"
|
||||
#include "IGpDirectoryCursor.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <Shlwapi.h>
|
||||
#include <ShlObj.h>
|
||||
#include <commdlg.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
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)
|
||||
@@ -82,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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -98,47 +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";
|
||||
m_logsDir = m_prefsDir + L"\\Logs";
|
||||
m_fontCacheDir = m_prefsDir + L"\\FontCache";
|
||||
|
||||
CreateDirectoryW(m_prefsDir.c_str(), nullptr);
|
||||
CreateDirectoryW(m_scoresDir.c_str(), nullptr);
|
||||
CreateDirectoryW(m_userHousesDir.c_str(), nullptr);
|
||||
CreateDirectoryW(m_userSavesDir.c_str(), nullptr);
|
||||
CreateDirectoryW(m_logsDir.c_str(), nullptr);
|
||||
CreateDirectoryW(m_fontCacheDir.c_str(), nullptr);
|
||||
|
||||
m_prefsDir.append(L"\\");
|
||||
m_scoresDir.append(L"\\");
|
||||
m_userHousesDir.append(L"\\");
|
||||
m_userSavesDir.append(L"\\");
|
||||
m_logsDir.append(L"\\");
|
||||
m_fontCacheDir.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;
|
||||
@@ -158,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;
|
||||
@@ -173,12 +209,19 @@ 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)
|
||||
@@ -217,7 +260,7 @@ GpIOStream *GpFileSystem_Win32::OpenFileNested(PortabilityLayer::VirtualDirector
|
||||
wchar_t winPath[MAX_PATH + 1];
|
||||
|
||||
if (!ResolvePath(virtualDirectory, paths, numPaths, winPath))
|
||||
return false;
|
||||
return nullptr;
|
||||
|
||||
const DWORD desiredAccess = writeAccess ? (GENERIC_WRITE | GENERIC_READ) : GENERIC_READ;
|
||||
DWORD winCreationDisposition = 0;
|
||||
@@ -240,14 +283,14 @@ GpIOStream *GpFileSystem_Win32::OpenFileNested(PortabilityLayer::VirtualDirector
|
||||
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)
|
||||
@@ -276,7 +319,7 @@ IGpDirectoryCursor *GpFileSystem_Win32::ScanDirectoryNested(PortabilityLayer::Vi
|
||||
{
|
||||
wchar_t winPath[MAX_PATH + 2];
|
||||
|
||||
const char **expandedPaths = static_cast<const char**>(malloc(sizeof(const char*) * (numPaths + 1)));
|
||||
const char **expandedPaths = static_cast<const char**>(m_alloc->Alloc(sizeof(const char*) * (numPaths + 1)));
|
||||
if (!expandedPaths)
|
||||
return nullptr;
|
||||
|
||||
@@ -285,7 +328,7 @@ IGpDirectoryCursor *GpFileSystem_Win32::ScanDirectoryNested(PortabilityLayer::Vi
|
||||
expandedPaths[numPaths] = "*";
|
||||
|
||||
const bool isPathResolved = ResolvePath(virtualDirectory, expandedPaths, numPaths + 1, winPath);
|
||||
free(expandedPaths);
|
||||
m_alloc->Release(expandedPaths);
|
||||
|
||||
if (!isPathResolved)
|
||||
return nullptr;
|
||||
@@ -296,7 +339,7 @@ IGpDirectoryCursor *GpFileSystem_Win32::ScanDirectoryNested(PortabilityLayer::Vi
|
||||
if (ff == INVALID_HANDLE_VALUE)
|
||||
return nullptr;
|
||||
|
||||
return GpDirectoryCursor_Win32::Create(ff, findData);
|
||||
return GpDirectoryCursor_Win32::Create(m_alloc, ff, findData);
|
||||
}
|
||||
|
||||
bool GpFileSystem_Win32::ValidateFilePathUnicodeChar(uint32_t c) const
|
||||
@@ -319,11 +362,6 @@ bool GpFileSystem_Win32::ValidateFilePathUnicodeChar(uint32_t c) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void GpFileSystem_Win32::SetMainThreadRelay(IGpThreadRelay *relay)
|
||||
{
|
||||
(void)relay;
|
||||
}
|
||||
|
||||
void GpFileSystem_Win32::SetDelayCallback(GpFileSystem_Win32::DelayCallback_t delayCallback)
|
||||
{
|
||||
}
|
||||
@@ -424,9 +462,27 @@ const wchar_t *GpFileSystem_Win32::GetBasePath() const
|
||||
return m_executablePath;
|
||||
}
|
||||
|
||||
GpFileSystem_Win32 *GpFileSystem_Win32::CreateInstance(IGpAllocator *alloc)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
GpFileSystem_Win32 *GpFileSystem_Win32::GetInstance()
|
||||
{
|
||||
return &ms_instance;
|
||||
return ms_instance;
|
||||
}
|
||||
|
||||
bool GpFileSystem_Win32::ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, wchar_t *outPath)
|
||||
@@ -436,31 +492,31 @@ bool GpFileSystem_Win32::ResolvePath(PortabilityLayer::VirtualDirectory_t virtua
|
||||
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.c_str();
|
||||
baseDir = m_logsDir.Buffer();
|
||||
break;
|
||||
case PortabilityLayer::VirtualDirectories::kFontCache:
|
||||
baseDir = m_fontCacheDir.c_str();
|
||||
case PortabilityLayer::VirtualDirectories::kSourceExport:
|
||||
baseDir = m_exportDir.Buffer();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
@@ -506,4 +562,4 @@ bool GpFileSystem_Win32::ResolvePath(PortabilityLayer::VirtualDirectory_t virtua
|
||||
return true;
|
||||
}
|
||||
|
||||
GpFileSystem_Win32 GpFileSystem_Win32::ms_instance;
|
||||
GpFileSystem_Win32 *GpFileSystem_Win32::ms_instance;
|
||||
|
@@ -4,13 +4,14 @@
|
||||
|
||||
#include "GpCoreDefs.h"
|
||||
#include "GpWindows.h"
|
||||
|
||||
#include <string>
|
||||
#include "GpString.h"
|
||||
|
||||
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;
|
||||
@@ -21,26 +22,30 @@ public:
|
||||
bool ValidateFilePath(const char *path, size_t sz) const override;
|
||||
bool ValidateFilePathUnicodeChar(uint32_t ch) const override;
|
||||
|
||||
void SetMainThreadRelay(IGpThreadRelay *relay) override;
|
||||
void SetDelayCallback(DelayCallback_t delayCallback) override;
|
||||
|
||||
const wchar_t *GetBasePath() const;
|
||||
|
||||
static GpFileSystem_Win32 *CreateInstance(IGpAllocator *alloc);
|
||||
static GpFileSystem_Win32 *GetInstance();
|
||||
|
||||
private:
|
||||
bool Init();
|
||||
|
||||
bool ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, wchar_t *outPath);
|
||||
|
||||
std::wstring m_prefsDir;
|
||||
std::wstring m_scoresDir;
|
||||
std::wstring m_packagedDir;
|
||||
std::wstring m_housesDir;
|
||||
std::wstring m_logsDir;
|
||||
std::wstring m_userHousesDir;
|
||||
std::wstring m_userSavesDir;
|
||||
std::wstring m_resourcesDir;
|
||||
std::wstring m_fontCacheDir;
|
||||
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;
|
||||
};
|
||||
|
@@ -1,3 +1,4 @@
|
||||
#include "GpAllocator_C.h"
|
||||
#include "GpLogDriver_Win32.h"
|
||||
#include "GpFileSystem_Win32.h"
|
||||
|
||||
@@ -7,6 +8,7 @@
|
||||
GpLogDriver_Win32::GpLogDriver_Win32()
|
||||
: m_stream(nullptr)
|
||||
, m_isInitialized(false)
|
||||
, m_alloc(GpAllocator_C::GetInstance())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -58,14 +60,14 @@ void GpLogDriver_Win32::VPrintf(Category category, const char *fmt, va_list args
|
||||
if (formattedSize <= 0)
|
||||
return;
|
||||
|
||||
char *charBuff = static_cast<char*>(malloc(formattedSize + 1));
|
||||
char *charBuff = static_cast<char*>(m_alloc->Alloc(formattedSize + 1));
|
||||
if (!charBuff)
|
||||
return;
|
||||
|
||||
vsnprintf(charBuff, formattedSize + 1, fmt, args);
|
||||
|
||||
m_stream->Write(charBuff, formattedSize);
|
||||
free(charBuff);
|
||||
m_alloc->Release(charBuff);
|
||||
}
|
||||
|
||||
m_stream->Write("\n", 1);
|
||||
|
@@ -3,6 +3,7 @@
|
||||
#include "IGpLogDriver.h"
|
||||
|
||||
class GpIOStream;
|
||||
struct IGpAllocator;
|
||||
|
||||
class GpLogDriver_Win32 : public IGpLogDriver
|
||||
{
|
||||
@@ -20,6 +21,7 @@ private:
|
||||
void InitInternal();
|
||||
|
||||
GpIOStream *m_stream;
|
||||
IGpAllocator *m_alloc;
|
||||
bool m_isInitialized;
|
||||
|
||||
static GpLogDriver_Win32 ms_instance;
|
||||
|
@@ -1,10 +1,10 @@
|
||||
#include "GpMain.h"
|
||||
#include "GpAllocator_C.h"
|
||||
#include "GpAudioDriverFactory.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"
|
||||
@@ -28,7 +28,6 @@ GpWindowsGlobals g_gpWindowsGlobals;
|
||||
extern "C" __declspec(dllimport) IGpAudioDriver *GpDriver_CreateAudioDriver_XAudio2(const GpAudioDriverProperties &properties);
|
||||
extern "C" __declspec(dllimport) IGpDisplayDriver *GpDriver_CreateDisplayDriver_D3D11(const GpDisplayDriverProperties &properties);
|
||||
extern "C" __declspec(dllimport) IGpInputDriver *GpDriver_CreateInputDriver_XInput(const GpInputDriverProperties &properties);
|
||||
extern "C" __declspec(dllimport) IGpFontHandler *GpDriver_CreateFontHandler_FreeType2(const GpFontHandlerProperties &properties);
|
||||
|
||||
static void PostMouseEvent(IGpVOSEventQueue *eventQueue, GpMouseEventType_t eventType, GpMouseButton_t button, int32_t x, int32_t y, float pixelScaleX, float pixelScaleY)
|
||||
{
|
||||
@@ -405,6 +404,13 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
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"))
|
||||
@@ -412,12 +418,14 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
}
|
||||
|
||||
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>(GpSystemServices_Win32::GetInstance());
|
||||
drivers->SetDriver<GpDriverIDs::kLog>(GpLogDriver_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;
|
||||
@@ -430,7 +438,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
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_createBWCursorFunc = GpBWCursor_Win32::Create;
|
||||
g_gpWindowsGlobals.m_createColorCursorFunc = GpColorCursor_Win32::Create;
|
||||
g_gpWindowsGlobals.m_translateWindowsMessageFunc = TranslateWindowsMessage;
|
||||
@@ -439,7 +446,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
|
||||
g_gpGlobalConfig.m_audioDriverType = EGpAudioDriverType_XAudio2;
|
||||
|
||||
g_gpGlobalConfig.m_fontHandlerType = EGpFontHandlerType_FreeType2;
|
||||
g_gpGlobalConfig.m_fontHandlerType = EGpFontHandlerType_None;
|
||||
|
||||
EGpInputDriverType inputDrivers[] =
|
||||
{
|
||||
@@ -451,12 +458,12 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
|
||||
g_gpGlobalConfig.m_osGlobals = &g_gpWindowsGlobals;
|
||||
g_gpGlobalConfig.m_logger = logger;
|
||||
g_gpGlobalConfig.m_systemServices = GpSystemServices_Win32::GetInstance();
|
||||
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);
|
||||
GpFontHandlerFactory::RegisterFontHandlerFactory(EGpFontHandlerType_FreeType2, GpDriver_CreateFontHandler_FreeType2);
|
||||
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Windows environment configured, starting up");
|
||||
@@ -468,5 +475,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
|
||||
LocalFree(cmdLineArgs);
|
||||
|
||||
fs->Destroy();
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "GpWindows.h"
|
||||
|
||||
struct IGpAllocator;
|
||||
|
||||
class GpMutex_Win32 final : public IGpMutex
|
||||
{
|
||||
public:
|
||||
@@ -11,11 +13,12 @@ public:
|
||||
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;
|
||||
};
|
||||
|
@@ -2,11 +2,11 @@
|
||||
#include "GpMutex_Win32.h"
|
||||
#include "GpThreadEvent_Win32.h"
|
||||
#include "GpWindows.h"
|
||||
#include "GpAllocator_C.h"
|
||||
|
||||
#include "IGpClipboardContents.h"
|
||||
|
||||
#include "UTF16.h"
|
||||
#include "UTF8.h"
|
||||
#include "GpUnicode.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
@@ -134,6 +134,7 @@ static DWORD WINAPI StaticStartThread(LPVOID lpThreadParameter)
|
||||
|
||||
GpSystemServices_Win32::GpSystemServices_Win32()
|
||||
: m_isTouchscreenSimulation(false)
|
||||
, m_alloc(GpAllocator_C::GetInstance())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -181,17 +182,17 @@ void GpSystemServices_Win32::GetLocalDateTime(unsigned int &year, unsigned int &
|
||||
|
||||
IGpMutex *GpSystemServices_Win32::CreateMutex()
|
||||
{
|
||||
return GpMutex_Win32::Create();
|
||||
return GpMutex_Win32::Create(m_alloc);
|
||||
}
|
||||
|
||||
IGpMutex *GpSystemServices_Win32::CreateRecursiveMutex()
|
||||
{
|
||||
return GpMutex_Win32::Create();
|
||||
return GpMutex_Win32::Create(m_alloc);
|
||||
}
|
||||
|
||||
IGpThreadEvent *GpSystemServices_Win32::CreateThreadEvent(bool autoReset, bool startSignaled)
|
||||
{
|
||||
return GpThreadEvent_Win32::Create(autoReset, startSignaled);
|
||||
return GpThreadEvent_Win32::Create(m_alloc, autoReset, startSignaled);
|
||||
}
|
||||
|
||||
void *GpSystemServices_Win32::CreateThread(ThreadFunc_t threadFunc, void *context)
|
||||
@@ -231,9 +232,10 @@ 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
|
||||
@@ -261,6 +263,21 @@ 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;
|
||||
|
@@ -28,12 +28,15 @@ public:
|
||||
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;
|
||||
@@ -48,6 +51,8 @@ public:
|
||||
private:
|
||||
bool m_isTouchscreenSimulation;
|
||||
|
||||
IGpAllocator *m_alloc;
|
||||
|
||||
static GpSystemServices_Win32 ms_instance;
|
||||
};
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#include "GpThreadEvent_Win32.h"
|
||||
#include "IGpAllocator.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <new>
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@@ -12,11 +12,12 @@ public:
|
||||
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;
|
||||
};
|
||||
|
@@ -8,15 +8,15 @@ else {
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 30
|
||||
defaultConfig {
|
||||
if (buildAsApplication) {
|
||||
applicationId "org.thecodedeposit.aerofoil"
|
||||
}
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
versionCode 11
|
||||
versionName "1.0.15"
|
||||
targetSdkVersion 30
|
||||
versionCode 16
|
||||
versionName "1.1.2"
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
arguments "APP_PLATFORM=android-16"
|
||||
|
1
AerofoilAndroid/app/jni/.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
AerofoilSDL
|
||||
AerofoilPortable
|
||||
Common
|
||||
FreeType
|
||||
GpApp
|
||||
|
@@ -1 +0,0 @@
|
||||
../../../AerofoilPortable
|
@@ -24,7 +24,7 @@ LOCAL_SRC_FILES := \
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := SDL2
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := GpShell GpFontHandler_FreeType2 AerofoilPortable AerofoilSDL GpApp
|
||||
LOCAL_STATIC_LIBRARIES := GpShell AerofoilPortable AerofoilSDL GpApp
|
||||
|
||||
LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -llog
|
||||
|
||||
|
@@ -424,9 +424,6 @@ bool GpFileSystem_Android::ResolvePath(PortabilityLayer::VirtualDirectory_t virt
|
||||
case PortabilityLayer::VirtualDirectories::kPrefs:
|
||||
prefsAppend = "Prefs";
|
||||
break;
|
||||
case PortabilityLayer::VirtualDirectories::kFontCache:
|
||||
prefsAppend = "FontCache";
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
@@ -450,7 +447,6 @@ bool GpFileSystem_Android::ResolvePath(PortabilityLayer::VirtualDirectory_t virt
|
||||
|
||||
GpFileSystem_Android::GpFileSystem_Android()
|
||||
: m_activity(nullptr)
|
||||
, m_relay(nullptr)
|
||||
, m_delayCallback(nullptr)
|
||||
, m_sourceExportMutex(nullptr)
|
||||
, m_sourceExportFD(0)
|
||||
@@ -680,25 +676,6 @@ bool GpFileSystem_Android::DeleteFile(PortabilityLayer::VirtualDirectory_t virtu
|
||||
}
|
||||
|
||||
IGpDirectoryCursor *GpFileSystem_Android::ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *const *paths, size_t numPaths)
|
||||
{
|
||||
ScanDirectoryNestedContext ctx;
|
||||
ctx.m_this = this;
|
||||
ctx.m_returnValue = nullptr;
|
||||
ctx.m_virtualDirectory = virtualDirectory;
|
||||
ctx.m_paths = paths;
|
||||
ctx.m_numPaths = numPaths;
|
||||
m_relay->Invoke(ScanDirectoryNestedThunk, &ctx);
|
||||
|
||||
return ctx.m_returnValue;
|
||||
}
|
||||
|
||||
void GpFileSystem_Android::ScanDirectoryNestedThunk(void *context)
|
||||
{
|
||||
ScanDirectoryNestedContext *ctx = static_cast<ScanDirectoryNestedContext*>(context);
|
||||
ctx->m_returnValue = ctx->m_this->ScanDirectoryNestedInternal(ctx->m_virtualDirectory, ctx->m_paths, ctx->m_numPaths);
|
||||
}
|
||||
|
||||
IGpDirectoryCursor *GpFileSystem_Android::ScanDirectoryNestedInternal(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);
|
||||
@@ -752,11 +729,6 @@ bool GpFileSystem_Android::ValidateFilePathUnicodeChar(uint32_t c) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void GpFileSystem_Android::SetMainThreadRelay(IGpThreadRelay *relay)
|
||||
{
|
||||
m_relay = relay;
|
||||
}
|
||||
|
||||
void GpFileSystem_Android::SetDelayCallback(DelayCallback_t delayCallback)
|
||||
{
|
||||
m_delayCallback = delayCallback;
|
||||
|
@@ -27,7 +27,6 @@ public:
|
||||
bool ValidateFilePath(const char *path, size_t pathLen) const override;
|
||||
bool ValidateFilePathUnicodeChar(uint32_t ch) const override;
|
||||
|
||||
void SetMainThreadRelay(IGpThreadRelay *relay) override;
|
||||
void SetDelayCallback(DelayCallback_t delayCallback) override;
|
||||
|
||||
void PostSourceExportRequest(bool cancelled, int fd, jobject pfd);
|
||||
@@ -36,26 +35,12 @@ public:
|
||||
static GpFileSystem_Android *GetInstance();
|
||||
|
||||
private:
|
||||
struct ScanDirectoryNestedContext
|
||||
{
|
||||
GpFileSystem_Android *m_this;
|
||||
|
||||
IGpDirectoryCursor *m_returnValue;
|
||||
PortabilityLayer::VirtualDirectory_t m_virtualDirectory;
|
||||
char const *const *m_paths;
|
||||
size_t m_numPaths;
|
||||
};
|
||||
|
||||
static void ScanDirectoryNestedThunk(void *context);
|
||||
IGpDirectoryCursor *ScanDirectoryNestedInternal(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths);
|
||||
|
||||
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);
|
||||
|
||||
IGpThreadRelay *m_relay;
|
||||
DelayCallback_t m_delayCallback;
|
||||
|
||||
jobject m_activity;
|
||||
|
@@ -1,10 +1,10 @@
|
||||
#include "SDL.h"
|
||||
|
||||
#include "GpMain.h"
|
||||
#include "GpAllocator_C.h"
|
||||
#include "GpAudioDriverFactory.h"
|
||||
#include "GpDisplayDriverFactory.h"
|
||||
#include "GpGlobalConfig.h"
|
||||
#include "GpFiber_SDL.h"
|
||||
#include "GpFileSystem_Android.h"
|
||||
#include "GpFontHandlerFactory.h"
|
||||
#include "GpInputDriverFactory.h"
|
||||
@@ -24,10 +24,9 @@
|
||||
|
||||
GpAndroidGlobals g_gpAndroidGlobals;
|
||||
|
||||
extern "C" IGpFontHandler *GpDriver_CreateFontHandler_FreeType2(const GpFontHandlerProperties &properties);
|
||||
|
||||
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
|
||||
{
|
||||
@@ -73,9 +72,11 @@ 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) < 0)
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0)
|
||||
return -1;
|
||||
|
||||
SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight");
|
||||
@@ -86,23 +87,30 @@ int main(int argc, char* argv[])
|
||||
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_FreeType2;
|
||||
g_gpGlobalConfig.m_fontHandlerType = EGpFontHandlerType_None;
|
||||
|
||||
g_gpGlobalConfig.m_inputDriverTypes = nullptr;
|
||||
g_gpGlobalConfig.m_numInputDrivers = 0;
|
||||
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);
|
||||
GpFontHandlerFactory::RegisterFontHandlerFactory(EGpFontHandlerType_FreeType2, GpDriver_CreateFontHandler_FreeType2);
|
||||
GpInputDriverFactory::RegisterInputDriverFactory(EGpInputDriverType_SDL2_Gamepad, GpDriver_CreateInputDriver_SDL2_Gamepad);
|
||||
|
||||
int returnCode = GpMain::Run();
|
||||
|
||||
@@ -117,8 +125,3 @@ int main(int argc, char* argv[])
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
IGpInputDriverSDLGamepad *IGpInputDriverSDLGamepad::GetInstance()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
@@ -55,8 +55,9 @@ void *GpSystemServices_Android::CreateThread(ThreadFunc_t threadFunc, void *cont
|
||||
return thread;
|
||||
}
|
||||
|
||||
void GpSystemServices_Android::Beep() const
|
||||
bool GpSystemServices_Android::Beep() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GpSystemServices_Android::IsTouchscreen() const
|
||||
@@ -84,6 +85,21 @@ 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();
|
||||
|
@@ -9,12 +9,15 @@ public:
|
||||
GpSystemServices_Android();
|
||||
|
||||
void *CreateThread(ThreadFunc_t threadFunc, void *context) 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;
|
||||
|
@@ -11,12 +11,9 @@ 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\GpFontHandler_FreeType2 ..\..\..\GpFontHandler_FreeType2
|
||||
mklink /D app\jni\PortabilityLayer ..\..\..\PortabilityLayer
|
||||
mklink /D app\jni\FreeType ..\..\..\FreeType
|
||||
mklink /D app\jni\rapidjson ..\..\..\rapidjson
|
||||
mklink /D app\jni\MacRomanConversion ..\..\..\MacRomanConversion
|
||||
mklink /D app\jni\stb ..\..\..\stb
|
||||
mklink /D app\src\main\assets\Resources ..\..\..\..\..\Resources
|
||||
|
||||
pause
|
||||
|
@@ -8,11 +8,7 @@ rmdir app\jni\SDL2
|
||||
rmdir app\jni\GpShell
|
||||
rmdir app\jni\GpCommon
|
||||
rmdir app\jni\GpApp
|
||||
rmdir app\jni\GpFontHandler_FreeType2
|
||||
rmdir app\jni\PortabilityLayer
|
||||
rmdir app\jni\FreeType
|
||||
rmdir app\jni\zlib
|
||||
rmdir app\jni\rapidjson
|
||||
rmdir app\jni\MacRomanConversion
|
||||
rmdir app\jni\stb
|
||||
rmdir app\src\main\assets\Resources
|
||||
|
2497
AerofoilMac/AerofoilMac.xcodeproj/project.pbxproj
Normal file
7
AerofoilMac/AerofoilMac.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
@@ -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>
|
@@ -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>
|
9
AerofoilMac/AerofoilMac/AerofoilAppDelegate.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AerofoilAppDelegate : NSObject <NSApplicationDelegate>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
43
AerofoilMac/AerofoilMac/AerofoilAppDelegate.mm
Normal 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
|
12
AerofoilMac/AerofoilMac/AerofoilApplication.h
Normal 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
|
28
AerofoilMac/AerofoilMac/AerofoilApplication.mm
Normal 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
|
12
AerofoilMac/AerofoilMac/AerofoilMac.entitlements
Normal 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>
|
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 1.2 MiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 343 KiB |
After Width: | Height: | Size: 9.3 KiB |
@@ -0,0 +1 @@
|
||||
{"images":[{"size":"128x128","expected-size":"128","filename":"128.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"256x256","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"128x128","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"256x256","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"512x512","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"16","filename":"16.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"64","filename":"64.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"512x512","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"}]}
|
6
AerofoilMac/AerofoilMac/Assets.xcassets/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
126
AerofoilMac/AerofoilMac/Base.lproj/MainMenu.xib
Normal file
@@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19115.3" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19115.3"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
<connections>
|
||||
<outlet property="delegate" destination="Rej-MO-9Vm" id="Gxm-Yh-n2y"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="SDLApplication"/>
|
||||
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||
<items>
|
||||
<menuItem title="Aerofoil" id="1Xt-HY-uBw">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Aerofoil" systemMenu="apple" id="uQy-DD-JDr">
|
||||
<items>
|
||||
<menuItem title="About Aerofoil" id="5kV-Vb-QxS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="showAboutAerofoil:" target="Rej-MO-9Vm" id="bhL-jn-Mnf"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="About Glider PRO" id="s5u-0T-8m0">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="showAboutGliderPRO:" target="Rej-MO-9Vm" id="GmM-Kv-7Zh"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
|
||||
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW">
|
||||
<connections>
|
||||
<action selector="showPreferences:" target="Rej-MO-9Vm" id="eLk-ZW-NLG"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
|
||||
<menuItem title="Services" id="NMo-om-nkz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
|
||||
<menuItem title="Hide Aerofoil" id="Olw-nP-bQN">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Show All" id="Kd2-mp-pUS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
|
||||
<menuItem title="Quit Aerofoil" keyEquivalent="q" id="4sb-4s-VLi">
|
||||
<connections>
|
||||
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Window" id="aUF-d1-5bR">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
|
||||
<items>
|
||||
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
|
||||
<connections>
|
||||
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Zoom" id="R4o-n2-Eq4">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Enter Full Screen" keyEquivalent="f" id="Kyh-0n-SY2">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleFullScreen:" target="-1" id="wEJ-jN-ABr"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
|
||||
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Help" id="wpr-3q-Mcd">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
|
||||
<items>
|
||||
<menuItem title="Aerofoil Help" keyEquivalent="?" id="FKE-Sm-Kum">
|
||||
<connections>
|
||||
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
<point key="canvasLocation" x="139" y="154"/>
|
||||
</menu>
|
||||
<customObject id="Rej-MO-9Vm" customClass="AerofoilAppDelegate">
|
||||
<connections>
|
||||
<outlet property="aboutAerofoilMenuItem" destination="5kV-Vb-QxS" id="Cd2-zP-auq"/>
|
||||
<outlet property="aboutGliderPROMenuItem" destination="s5u-0T-8m0" id="gQV-eb-cs2"/>
|
||||
<outlet property="preferencesMenuItem" destination="BOF-NM-1cW" id="oG4-1W-R0t"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
</objects>
|
||||
</document>
|
32
AerofoilMac/AerofoilMac/Info.plist
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.arcade-games</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
3
AerofoilMac/AerofoilMac/MacInit.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
int MacInit(void);
|
22
AerofoilMac/AerofoilMac/MacInit.mm
Normal file
@@ -0,0 +1,22 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "AerofoilApplication.h"
|
||||
#import "AerofoilAppDelegate.h"
|
||||
#import "MacInit.h"
|
||||
#include "SDL.h"
|
||||
|
||||
int MacInit(void) {
|
||||
// Instantiate NSApp and its delegate first, so SDL chooses those over its own implementation.
|
||||
[AerofoilApplication sharedApplication];
|
||||
[[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:NSApp topLevelObjects:nil];
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0)
|
||||
return -1;
|
||||
|
||||
// Gracefully activate app.
|
||||
//
|
||||
// (SDL forcefully does this via [NSApp activateIgnoringOtherApps:YES],
|
||||
// which isn't consistent with normal Mac apps).
|
||||
[NSApp finishLaunching];
|
||||
|
||||
return 0;
|
||||
}
|
6
AerofoilMac/Resources/README.TXT
Normal file
@@ -0,0 +1,6 @@
|
||||
Extract resources from the Windows build and place here to create the Mac build.
|
||||
Files are included in the Xcode project, but not in the Git repo.
|
||||
|
||||
- ApplicationResources.gpf
|
||||
- Fonts.gpf
|
||||
- Houses/*.gpf
|
10
AerofoilPortable.props
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<IncludePath>$(SolutionDir)AerofoilPortable;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup />
|
||||
<ItemGroup />
|
||||
</Project>
|
@@ -14,7 +14,9 @@ LOCAL_CFLAGS := -DGP_DEBUG_CONFIG=0
|
||||
|
||||
# Add your application source files here...
|
||||
LOCAL_SRC_FILES := \
|
||||
GpAllocator_C.cpp \
|
||||
GpThreadEvent_Cpp11.cpp \
|
||||
GpSystemServices_POSIX.cpp
|
||||
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
115
AerofoilPortable/GpAllocator_C.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "GpAllocator_C.h"
|
||||
#include "CoreDefs.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct GpAllocator_C_MMBlock
|
||||
{
|
||||
uint8_t m_offsetFromAllocLocation;
|
||||
|
||||
static size_t AlignedSize();
|
||||
};
|
||||
|
||||
size_t GpAllocator_C_MMBlock::AlignedSize()
|
||||
{
|
||||
const size_t paddedSize = sizeof(GpAllocator_C_MMBlock) + GP_SYSTEM_MEMORY_ALIGNMENT - 1;
|
||||
const size_t paddedSizeTruncated = paddedSize - (paddedSize % GP_SYSTEM_MEMORY_ALIGNMENT);
|
||||
|
||||
return paddedSizeTruncated;
|
||||
}
|
||||
|
||||
void *GpAllocator_C::Realloc(void *buf, size_t newSize)
|
||||
{
|
||||
if (buf == nullptr)
|
||||
{
|
||||
if (newSize == 0)
|
||||
return nullptr;
|
||||
|
||||
return this->Alloc(newSize);
|
||||
}
|
||||
|
||||
if (newSize == 0)
|
||||
{
|
||||
this->Free(buf);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
assert(buf != nullptr);
|
||||
|
||||
const size_t mmBlockSize = GpAllocator_C_MMBlock::AlignedSize();
|
||||
uint8_t *oldBufBytes = static_cast<uint8_t*>(buf);
|
||||
const GpAllocator_C_MMBlock *oldBufMMBlock = reinterpret_cast<const GpAllocator_C_MMBlock*>(oldBufBytes - GpAllocator_C_MMBlock::AlignedSize());
|
||||
|
||||
const size_t oldBufOffsetFromAlignLoc = oldBufMMBlock->m_offsetFromAllocLocation;
|
||||
uint8_t *oldBufBase = oldBufBytes - GpAllocator_C_MMBlock::AlignedSize() - oldBufOffsetFromAlignLoc;
|
||||
|
||||
const size_t mmBlockSizeWithMaxPadding = GpAllocator_C_MMBlock::AlignedSize() + GP_SYSTEM_MEMORY_ALIGNMENT - 1;
|
||||
if (SIZE_MAX - newSize < mmBlockSizeWithMaxPadding)
|
||||
return nullptr;
|
||||
|
||||
const size_t newBufferSize = newSize + mmBlockSizeWithMaxPadding;
|
||||
uint8_t *newBuffer = static_cast<uint8_t*>(realloc(oldBufBase, newSize + mmBlockSizeWithMaxPadding));
|
||||
if (!newBuffer)
|
||||
return nullptr;
|
||||
|
||||
const intptr_t offsetFromAlignPoint = reinterpret_cast<intptr_t>(newBuffer) & static_cast<intptr_t>(GP_SYSTEM_MEMORY_ALIGNMENT - 1);
|
||||
intptr_t alignPadding = 0;
|
||||
if (offsetFromAlignPoint != 0)
|
||||
alignPadding = static_cast<intptr_t>(GP_SYSTEM_MEMORY_ALIGNMENT) - offsetFromAlignPoint;
|
||||
|
||||
// Check if the alignment changed, if so relocate
|
||||
if (static_cast<size_t>(alignPadding) != oldBufOffsetFromAlignLoc)
|
||||
memmove(newBuffer + alignPadding, newBuffer + oldBufOffsetFromAlignLoc, GpAllocator_C_MMBlock::AlignedSize() + newSize);
|
||||
|
||||
GpAllocator_C_MMBlock *newMMBlock = reinterpret_cast<GpAllocator_C_MMBlock*>(newBuffer + alignPadding);
|
||||
newMMBlock->m_offsetFromAllocLocation = static_cast<uint8_t>(alignPadding);
|
||||
|
||||
return newBuffer + alignPadding + GpAllocator_C_MMBlock::AlignedSize();
|
||||
}
|
||||
|
||||
void *GpAllocator_C::Alloc(size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return nullptr;
|
||||
|
||||
const size_t mmBlockSizeWithMaxPadding = GpAllocator_C_MMBlock::AlignedSize() + GP_SYSTEM_MEMORY_ALIGNMENT - 1;
|
||||
if (SIZE_MAX - size < mmBlockSizeWithMaxPadding)
|
||||
return nullptr;
|
||||
|
||||
uint8_t *buffer = static_cast<uint8_t*>(realloc(nullptr, size + mmBlockSizeWithMaxPadding));
|
||||
if (!buffer)
|
||||
return nullptr;
|
||||
|
||||
const intptr_t offsetFromAlignPoint = reinterpret_cast<intptr_t>(buffer) & static_cast<intptr_t>(GP_SYSTEM_MEMORY_ALIGNMENT - 1);
|
||||
intptr_t alignPadding = 0;
|
||||
if (offsetFromAlignPoint != 0)
|
||||
alignPadding = static_cast<intptr_t>(GP_SYSTEM_MEMORY_ALIGNMENT) - offsetFromAlignPoint;
|
||||
|
||||
GpAllocator_C_MMBlock *mmBlock = reinterpret_cast<GpAllocator_C_MMBlock*>(buffer + alignPadding);
|
||||
mmBlock->m_offsetFromAllocLocation = static_cast<uint8_t>(alignPadding);
|
||||
|
||||
return buffer + alignPadding + GpAllocator_C_MMBlock::AlignedSize();
|
||||
}
|
||||
|
||||
void GpAllocator_C::Free(void *buf)
|
||||
{
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
const size_t mmBlockSize = GpAllocator_C_MMBlock::AlignedSize();
|
||||
|
||||
uint8_t *bytes = static_cast<uint8_t*>(buf);
|
||||
const GpAllocator_C_MMBlock *mmBlock = reinterpret_cast<const GpAllocator_C_MMBlock*>(bytes - GpAllocator_C_MMBlock::AlignedSize());
|
||||
|
||||
void *freeLoc = bytes - GpAllocator_C_MMBlock::AlignedSize() - mmBlock->m_offsetFromAllocLocation;
|
||||
(void)realloc(freeLoc, 0);
|
||||
}
|
||||
|
||||
GpAllocator_C *GpAllocator_C::GetInstance()
|
||||
{
|
||||
return &ms_instance;
|
||||
}
|
||||
|
||||
GpAllocator_C GpAllocator_C::ms_instance;
|
17
AerofoilPortable/GpAllocator_C.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "IGpAllocator.h"
|
||||
|
||||
class GpAllocator_C final : public IGpAllocator
|
||||
{
|
||||
public:
|
||||
void *Realloc(void *buf, size_t newSize) override;
|
||||
|
||||
static GpAllocator_C *GetInstance();
|
||||
|
||||
private:
|
||||
void *Alloc(size_t size);
|
||||
void Free(void *ptr);
|
||||
|
||||
static GpAllocator_C ms_instance;
|
||||
};
|
@@ -6,6 +6,10 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __MACOS__
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
GpSystemServices_POSIX::GpSystemServices_POSIX()
|
||||
{
|
||||
}
|
||||
@@ -44,7 +48,21 @@ IGpThreadEvent *GpSystemServices_POSIX::CreateThreadEvent(bool autoReset, bool s
|
||||
|
||||
uint64_t GpSystemServices_POSIX::GetFreeMemoryCosmetic() const
|
||||
{
|
||||
#ifdef __MACOS__
|
||||
{ /* This works on *bsd and darwin. */
|
||||
unsigned int usermem;
|
||||
size_t len = sizeof usermem;
|
||||
static int mib[2] = { CTL_HW, HW_USERMEM };
|
||||
|
||||
if (sysctl (mib, 2, &usermem, &len, NULL, 0) == 0
|
||||
&& len == sizeof (usermem))
|
||||
return (long) usermem;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
long pages = sysconf(_SC_AVPHYS_PAGES);
|
||||
long pageSize = sysconf(_SC_PAGE_SIZE);
|
||||
return pages * pageSize;
|
||||
#endif
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<IncludePath>$(SolutionDir)SDL2-2.0.12\include;$(IncludePath)</IncludePath>
|
||||
<IncludePath>$(SolutionDir)SDL2-2.0.12\include;$(SolutionDir)AerofoilPortable;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(SolutionDir)SDL2-2.0.12\lib\x64;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
|
@@ -14,19 +14,19 @@
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{33542FF0-0473-4802-BC79-3B8261790F65}</ProjectGuid>
|
||||
<RootNamespace>AerofoilSDL</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
<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>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>
|
||||
@@ -84,6 +84,7 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\AerofoilPortable\GpAllocator_C.cpp" />
|
||||
<ClCompile Include="..\Aerofoil\GpColorCursor_Win32.cpp" />
|
||||
<ClCompile Include="..\Aerofoil\GpFileStream_Win32.cpp" />
|
||||
<ClCompile Include="..\Aerofoil\GpFileSystem_Win32.cpp" />
|
||||
@@ -93,8 +94,6 @@
|
||||
<ClCompile Include="..\Aerofoil\GpThreadEvent_Win32.cpp" />
|
||||
<ClCompile Include="GpAudioDriver_SDL2.cpp" />
|
||||
<ClCompile Include="GpDisplayDriver_SDL_GL2.cpp" />
|
||||
<ClCompile Include="GpFiberStarter_SDL.cpp" />
|
||||
<ClCompile Include="GpFiber_SDL.cpp" />
|
||||
<ClCompile Include="GpInputDriver_SDL_Gamepad.cpp" />
|
||||
<ClCompile Include="GpMain_SDL_Win32.cpp" />
|
||||
<ClCompile Include="ShaderCode\CopyQuadP.cpp" />
|
||||
|
@@ -48,12 +48,6 @@
|
||||
<ClCompile Include="ShaderCode\ScaleQuadP.cpp">
|
||||
<Filter>Source Files\ShaderCode</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GpFiber_SDL.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GpFiberStarter_SDL.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GpDisplayDriver_SDL_GL2.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -72,6 +66,9 @@
|
||||
<ClCompile Include="GpInputDriver_SDL_Gamepad.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\AerofoilPortable\GpAllocator_C.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ShaderCode\Functions.h">
|
||||
|
8
AerofoilSDL/AerofoilSDL_Combined.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "GpAudioDriver_SDL2.cpp"
|
||||
#include "GpDisplayDriver_SDL_GL2.cpp"
|
||||
#include "GpInputDriver_SDL_Gamepad.cpp"
|
||||
#include "ShaderCode/CopyQuadP.cpp"
|
||||
#include "ShaderCode/DrawQuadPaletteP.cpp"
|
||||
#include "ShaderCode/DrawQuad32P.cpp"
|
||||
#include "ShaderCode/DrawQuadV.cpp"
|
||||
#include "ShaderCode/ScaleQuadP.cpp"
|
@@ -11,6 +11,7 @@ LOCAL_C_INCLUDES := \
|
||||
$(LOCAL_PATH)/../GpShell \
|
||||
$(LOCAL_PATH)/../Common \
|
||||
$(LOCAL_PATH)/../PortabilityLayer \
|
||||
$(LOCAL_PATH)/../AerofoilPortable \
|
||||
$(LOCAL_PATH)/$(SDL_PATH)/include
|
||||
|
||||
LOCAL_CFLAGS := -DGP_DEBUG_CONFIG=0
|
||||
@@ -19,8 +20,7 @@ LOCAL_CFLAGS := -DGP_DEBUG_CONFIG=0
|
||||
LOCAL_SRC_FILES := \
|
||||
GpAudioDriver_SDL2.cpp \
|
||||
GpDisplayDriver_SDL_GL2.cpp \
|
||||
GpFiber_SDL.cpp \
|
||||
GpFiberStarter_SDL.cpp \
|
||||
GpInputDriver_SDL_Gamepad.cpp \
|
||||
ShaderCode/CopyQuadP.cpp \
|
||||
ShaderCode/DrawQuadPaletteP.cpp \
|
||||
ShaderCode/DrawQuad32P.cpp \
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#include "CoreDefs.h"
|
||||
#include "IGpAudioBuffer.h"
|
||||
#include "IGpAudioDriver.h"
|
||||
#include "IGpAudioChannel.h"
|
||||
#include "IGpAudioChannelCallbacks.h"
|
||||
@@ -5,21 +7,28 @@
|
||||
#include "IGpPrefsHandler.h"
|
||||
#include "IGpSystemServices.h"
|
||||
#include "GpAudioDriverProperties.h"
|
||||
#include "CoreDefs.h"
|
||||
|
||||
#include "GpSDL.h"
|
||||
|
||||
#include "SDL_audio.h"
|
||||
#include "GpRingBuffer.h"
|
||||
|
||||
#include "SDL_atomic.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <chrono>
|
||||
|
||||
class GpAudioDriver_SDL2;
|
||||
|
||||
typedef std::chrono::high_resolution_clock::duration GpAudioDriver_SDL2_Duration_t;
|
||||
typedef std::chrono::high_resolution_clock::time_point GpAudioDriver_SDL2_TimePoint_t;
|
||||
typedef std::chrono::high_resolution_clock::period GpAudioDriver_SDL2_Period_t;
|
||||
typedef std::chrono::high_resolution_clock::rep GpAudioDriver_SDL2_Rep_t;
|
||||
|
||||
static void *AlignedAlloc(size_t size, size_t alignment)
|
||||
{
|
||||
void *storage = malloc(size + alignment);
|
||||
@@ -46,72 +55,150 @@ static void AlignedFree(void *ptr)
|
||||
free(storageLoc);
|
||||
}
|
||||
|
||||
struct GpAudioChannelBufferChain_SDL2 final
|
||||
{
|
||||
GpAudioChannelBufferChain_SDL2();
|
||||
|
||||
static GpAudioChannelBufferChain_SDL2 *Alloc();
|
||||
void Release();
|
||||
|
||||
static const size_t kMaxCapacity = 65536;
|
||||
|
||||
size_t m_consumed;
|
||||
size_t m_used;
|
||||
uint8_t m_data[kMaxCapacity];
|
||||
GpAudioChannelBufferChain_SDL2 *m_next;
|
||||
bool m_hasTrigger;
|
||||
};
|
||||
|
||||
class GP_ALIGNED(GP_SYSTEM_MEMORY_ALIGNMENT) GpAudioChannel_SDL2 final : public IGpAudioChannel
|
||||
class GpAudioBuffer_SDL2 final : public IGpAudioBuffer
|
||||
{
|
||||
public:
|
||||
enum ChannelState
|
||||
{
|
||||
ChannelState_Idle,
|
||||
ChannelState_Playing,
|
||||
ChannelState_Stopped,
|
||||
};
|
||||
typedef int16_t AudioSample_t;
|
||||
|
||||
static GpAudioBuffer_SDL2 *Create(const void *data, size_t size);
|
||||
|
||||
void AddRef() override;
|
||||
void Release() override;
|
||||
|
||||
const AudioSample_t *GetData() const;
|
||||
size_t GetSize() const;
|
||||
|
||||
private:
|
||||
GpAudioBuffer_SDL2(const AudioSample_t *data, size_t size);
|
||||
~GpAudioBuffer_SDL2();
|
||||
|
||||
void Destroy();
|
||||
|
||||
const AudioSample_t *m_data;
|
||||
size_t m_size;
|
||||
SDL_atomic_t m_count;
|
||||
};
|
||||
|
||||
GpAudioBuffer_SDL2 *GpAudioBuffer_SDL2::Create(const void *data, size_t size)
|
||||
{
|
||||
size_t baseSize = sizeof(GpAudioBuffer_SDL2) + GP_SYSTEM_MEMORY_ALIGNMENT + 1;
|
||||
baseSize -= baseSize % GP_SYSTEM_MEMORY_ALIGNMENT;
|
||||
void *storage = malloc(size * sizeof(AudioSample_t) + baseSize);
|
||||
if (!storage)
|
||||
return nullptr;
|
||||
|
||||
AudioSample_t *dataPos = reinterpret_cast<AudioSample_t*>(static_cast<uint8_t*>(storage) + baseSize);
|
||||
|
||||
// Convert from u8 to s16
|
||||
const uint8_t *srcData = static_cast<const uint8_t*>(data);
|
||||
for (size_t i = 0; i < size; i++)
|
||||
dataPos[i] = srcData[i] - 0x80;
|
||||
|
||||
return new (storage) GpAudioBuffer_SDL2(dataPos, size);
|
||||
}
|
||||
|
||||
|
||||
void GpAudioBuffer_SDL2::AddRef()
|
||||
{
|
||||
SDL_AtomicAdd(&m_count, 1);
|
||||
}
|
||||
|
||||
void GpAudioBuffer_SDL2::Release()
|
||||
{
|
||||
int prevCount = SDL_AtomicAdd(&m_count, -1);
|
||||
if (prevCount == 1)
|
||||
this->Destroy();
|
||||
}
|
||||
|
||||
const int16_t *GpAudioBuffer_SDL2::GetData() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
size_t GpAudioBuffer_SDL2::GetSize() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
|
||||
GpAudioBuffer_SDL2::GpAudioBuffer_SDL2(const int16_t *data, size_t size)
|
||||
: m_data(data)
|
||||
, m_size(size)
|
||||
{
|
||||
SDL_AtomicSet(&m_count, 1);
|
||||
}
|
||||
|
||||
GpAudioBuffer_SDL2::~GpAudioBuffer_SDL2()
|
||||
{
|
||||
}
|
||||
|
||||
void GpAudioBuffer_SDL2::Destroy()
|
||||
{
|
||||
this->~GpAudioBuffer_SDL2();
|
||||
free(this);
|
||||
}
|
||||
|
||||
|
||||
class GpAudioChannel_SDL2 final : public IGpAudioChannel
|
||||
{
|
||||
public:
|
||||
friend class GpAudioDriver_SDL2;
|
||||
typedef GpAudioBuffer_SDL2::AudioSample_t AudioSample_t;
|
||||
|
||||
GpAudioChannel_SDL2();
|
||||
GpAudioChannel_SDL2(GpAudioDriver_SDL2_Duration_t latency, GpAudioDriver_SDL2_Duration_t bufferTime, size_t bufferSamplesMax, uint16_t sampleRate);
|
||||
~GpAudioChannel_SDL2();
|
||||
|
||||
void AddRef();
|
||||
void Release();
|
||||
|
||||
void SetAudioChannelContext(IGpAudioChannelCallbacks *callbacks) override;
|
||||
void PostBuffer(const void *buffer, size_t bufferSize) override;
|
||||
bool PostBuffer(IGpAudioBuffer *buffer) override;
|
||||
void Stop() override;
|
||||
void Destroy() override;
|
||||
|
||||
void Consume(uint8_t *output, size_t sz);
|
||||
void Consume(int16_t *output, size_t sz, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime);
|
||||
|
||||
static GpAudioChannel_SDL2 *Alloc(GpAudioDriver_SDL2 *driver);
|
||||
static GpAudioChannel_SDL2 *Alloc(GpAudioDriver_SDL2 *driver, GpAudioDriver_SDL2_Duration_t latency, GpAudioDriver_SDL2_Duration_t bufferTime, size_t bufferSamplesMax, uint16_t sampleRate);
|
||||
|
||||
private:
|
||||
bool Init(GpAudioDriver_SDL2 *driver);
|
||||
|
||||
static const size_t kMaxBuffers = 16;
|
||||
|
||||
IGpAudioChannelCallbacks *m_callbacks;
|
||||
IGpMutex *m_mutex;
|
||||
GpAudioDriver_SDL2 *m_owner;
|
||||
|
||||
SDL_atomic_t m_refCount;
|
||||
|
||||
GpAudioChannelBufferChain_SDL2 *m_firstPendingBuffer;
|
||||
GpAudioChannelBufferChain_SDL2 *m_lastPendingBuffer;
|
||||
GpAudioBuffer_SDL2 *m_pendingBuffers[kMaxBuffers];
|
||||
size_t m_nextPendingBufferConsumePos;
|
||||
size_t m_nextPendingBufferInsertionPos;
|
||||
size_t m_numQueuedBuffers;
|
||||
size_t m_firstBufferSamplesConsumed;
|
||||
|
||||
ChannelState m_channelState;
|
||||
GpAudioDriver_SDL2_TimePoint_t m_timestamp; // Time that audio will be consumed if posted to the channel, if m_hasTimestamp is true.
|
||||
GpAudioDriver_SDL2_Duration_t m_latency;
|
||||
GpAudioDriver_SDL2_Duration_t m_bufferTime;
|
||||
size_t m_bufferSamplesMax;
|
||||
size_t m_leadingSilence;
|
||||
uint16_t m_sampleRate;
|
||||
bool m_isMixing;
|
||||
bool m_hasTimestamp;
|
||||
};
|
||||
|
||||
class GP_ALIGNED(GP_SYSTEM_MEMORY_ALIGNMENT) GpAudioDriver_SDL2 final : public IGpAudioDriver, public IGpPrefsHandler
|
||||
class GpAudioDriver_SDL2 final : public IGpAudioDriver, public IGpPrefsHandler
|
||||
{
|
||||
public:
|
||||
friend class GpAudioChannel_SDL2;
|
||||
|
||||
typedef GpAudioChannel_SDL2::AudioSample_t AudioSample_t;
|
||||
|
||||
|
||||
explicit GpAudioDriver_SDL2(const GpAudioDriverProperties &properties);
|
||||
~GpAudioDriver_SDL2();
|
||||
|
||||
IGpAudioBuffer *CreateBuffer(const void *data, size_t size) override;
|
||||
IGpAudioChannel *CreateChannel() override;
|
||||
void SetMasterVolume(uint32_t vol, uint32_t maxVolume) override;
|
||||
void Shutdown() override;
|
||||
@@ -122,63 +209,59 @@ public:
|
||||
|
||||
bool Init();
|
||||
|
||||
static GpAudioDriver_SDL2_TimePoint_t GetCurrentTime();
|
||||
|
||||
private:
|
||||
void DetachAudioChannel(GpAudioChannel_SDL2 *channel);
|
||||
|
||||
static void SDLCALL StaticMixAudio(void *userdata, Uint8 *stream, int len);
|
||||
|
||||
void MixAudio(void *stream, size_t len);
|
||||
void RefillMixChunk(GpAudioChannel_SDL2 *const*channels, size_t numChannels);
|
||||
void RefillMixChunk(GpAudioChannel_SDL2 *const*channels, size_t numChannels, size_t maxSamplesToFill, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime);
|
||||
static void AddSamples(AudioSample_t *GP_RESTRICT dest, const AudioSample_t *GP_RESTRICT src, size_t nSamples);
|
||||
|
||||
GpAudioDriverProperties m_properties;
|
||||
IGpMutex *m_mutex;
|
||||
IGpMutex *m_mixState;
|
||||
|
||||
static const size_t kMaxChannels = 16;
|
||||
static const size_t kMixChunkSize = 256;
|
||||
static const int16_t kMaxAudioVolumeScale = 25;
|
||||
static const size_t kMixChunkSamples = 512;
|
||||
static const int16_t kMaxAudioVolumeScale = 64;
|
||||
|
||||
GpAudioChannel_SDL2 *m_channels[kMaxChannels];
|
||||
size_t m_numChannels;
|
||||
|
||||
unsigned int m_sampleRate;
|
||||
GpAudioDriver_SDL2_Duration_t m_latency;
|
||||
GpAudioDriver_SDL2_Duration_t m_bufferTime;
|
||||
size_t m_bufferSamples;
|
||||
|
||||
bool m_sdlAudioRunning;
|
||||
|
||||
GP_ALIGNED(GP_SYSTEM_MEMORY_ALIGNMENT) int16_t m_mixChunk[kMixChunkSize];
|
||||
GP_ALIGNED(GP_SYSTEM_MEMORY_ALIGNMENT) int16_t m_mixChunk[kMixChunkSamples];
|
||||
size_t m_mixChunkReadOffset;
|
||||
|
||||
int16_t m_audioVolumeScale;
|
||||
};
|
||||
|
||||
GpAudioChannelBufferChain_SDL2::GpAudioChannelBufferChain_SDL2()
|
||||
: m_used(0)
|
||||
, m_consumed(0)
|
||||
, m_next(nullptr)
|
||||
, m_hasTrigger(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
GpAudioChannelBufferChain_SDL2 *GpAudioChannelBufferChain_SDL2::Alloc()
|
||||
{
|
||||
void *storage = AlignedAlloc(sizeof(GpAudioChannelBufferChain_SDL2), GP_SYSTEM_MEMORY_ALIGNMENT);
|
||||
return new (storage) GpAudioChannelBufferChain_SDL2();
|
||||
}
|
||||
|
||||
void GpAudioChannelBufferChain_SDL2::Release()
|
||||
{
|
||||
this->~GpAudioChannelBufferChain_SDL2();
|
||||
AlignedFree(this);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
// GpAudioChannel
|
||||
|
||||
GpAudioChannel_SDL2::GpAudioChannel_SDL2()
|
||||
GpAudioChannel_SDL2::GpAudioChannel_SDL2(GpAudioDriver_SDL2_Duration_t latency, GpAudioDriver_SDL2_Duration_t bufferTime, size_t bufferSamplesMax, uint16_t sampleRate)
|
||||
: m_callbacks(nullptr)
|
||||
, m_mutex(nullptr)
|
||||
, m_owner(nullptr)
|
||||
, m_firstPendingBuffer(nullptr)
|
||||
, m_lastPendingBuffer(nullptr)
|
||||
, m_latency(latency)
|
||||
, m_bufferTime(bufferTime)
|
||||
, m_bufferSamplesMax(bufferSamplesMax)
|
||||
, m_leadingSilence(0)
|
||||
, m_sampleRate(sampleRate)
|
||||
, m_isMixing(false)
|
||||
, m_hasTimestamp(false)
|
||||
, m_nextPendingBufferConsumePos(0)
|
||||
, m_nextPendingBufferInsertionPos(0)
|
||||
, m_numQueuedBuffers(0)
|
||||
, m_firstBufferSamplesConsumed(0)
|
||||
{
|
||||
SDL_AtomicSet(&m_refCount, 1);
|
||||
}
|
||||
@@ -190,12 +273,7 @@ GpAudioChannel_SDL2::~GpAudioChannel_SDL2()
|
||||
if (m_mutex)
|
||||
m_mutex->Destroy();
|
||||
|
||||
while (m_firstPendingBuffer)
|
||||
{
|
||||
GpAudioChannelBufferChain_SDL2 *buffer = m_firstPendingBuffer;
|
||||
m_firstPendingBuffer = buffer->m_next;
|
||||
buffer->Release();
|
||||
}
|
||||
assert(m_numQueuedBuffers == 0);
|
||||
}
|
||||
|
||||
void GpAudioChannel_SDL2::AddRef()
|
||||
@@ -218,54 +296,70 @@ void GpAudioChannel_SDL2::SetAudioChannelContext(IGpAudioChannelCallbacks *callb
|
||||
m_callbacks = callbacks;
|
||||
}
|
||||
|
||||
void GpAudioChannel_SDL2::PostBuffer(const void *buffer, size_t bufferSize)
|
||||
bool GpAudioChannel_SDL2::PostBuffer(IGpAudioBuffer *buffer)
|
||||
{
|
||||
buffer->AddRef();
|
||||
|
||||
m_mutex->Lock();
|
||||
|
||||
while (bufferSize > 0)
|
||||
if (m_numQueuedBuffers == kMaxBuffers)
|
||||
{
|
||||
GpAudioChannelBufferChain_SDL2 *newBuffer = GpAudioChannelBufferChain_SDL2::Alloc();
|
||||
if (newBuffer == nullptr)
|
||||
break;
|
||||
|
||||
if (m_lastPendingBuffer == nullptr)
|
||||
m_firstPendingBuffer = newBuffer;
|
||||
else
|
||||
m_lastPendingBuffer->m_next = newBuffer;
|
||||
m_lastPendingBuffer = newBuffer;
|
||||
|
||||
size_t bufferable = newBuffer->kMaxCapacity;
|
||||
if (bufferSize < bufferable)
|
||||
bufferable = bufferSize;
|
||||
|
||||
memcpy(newBuffer->m_data, buffer, bufferable);
|
||||
|
||||
buffer = static_cast<const uint8_t*>(buffer) + bufferable;
|
||||
bufferSize -= bufferable;
|
||||
m_lastPendingBuffer->m_used = bufferable;
|
||||
m_lastPendingBuffer->m_hasTrigger = (bufferSize == 0);
|
||||
m_mutex->Unlock();
|
||||
buffer->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t leadingSilence = 0;
|
||||
if (m_numQueuedBuffers == 0 && m_hasTimestamp && !m_isMixing)
|
||||
{
|
||||
GpAudioDriver_SDL2_TimePoint_t queueTime = GpAudioDriver_SDL2::GetCurrentTime() + m_latency;
|
||||
if (queueTime > m_timestamp)
|
||||
{
|
||||
const GpAudioDriver_SDL2_Duration_t leadTime = queueTime - m_timestamp;
|
||||
|
||||
if (leadTime > m_bufferTime)
|
||||
leadingSilence = m_bufferSamplesMax;
|
||||
else
|
||||
{
|
||||
const GpAudioDriver_SDL2_Rep_t leadTimeRep = leadTime.count();
|
||||
leadingSilence = leadTimeRep * static_cast<GpAudioDriver_SDL2_Rep_t>(m_sampleRate) * GpAudioDriver_SDL2_Period_t::num / GpAudioDriver_SDL2_Period_t::den;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_leadingSilence = leadingSilence;
|
||||
|
||||
m_pendingBuffers[m_nextPendingBufferInsertionPos++] = static_cast<GpAudioBuffer_SDL2*>(buffer);
|
||||
m_numQueuedBuffers++;
|
||||
|
||||
m_nextPendingBufferInsertionPos = m_nextPendingBufferInsertionPos % kMaxBuffers;
|
||||
|
||||
m_mutex->Unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GpAudioChannel_SDL2::Stop()
|
||||
{
|
||||
m_mutex->Lock();
|
||||
|
||||
GpAudioChannelBufferChain_SDL2 *buffer = m_firstPendingBuffer;
|
||||
m_firstPendingBuffer = nullptr;
|
||||
m_lastPendingBuffer = nullptr;
|
||||
m_leadingSilence = 0;
|
||||
|
||||
while (buffer)
|
||||
size_t numBuffersToDischarge = m_numQueuedBuffers;
|
||||
|
||||
for (size_t i = 0; i < numBuffersToDischarge; i++)
|
||||
{
|
||||
if (buffer->m_hasTrigger && m_callbacks)
|
||||
GpAudioBuffer_SDL2 *buffer = m_pendingBuffers[m_nextPendingBufferConsumePos];
|
||||
|
||||
m_nextPendingBufferConsumePos = (m_nextPendingBufferConsumePos + 1) % kMaxBuffers;
|
||||
m_numQueuedBuffers--;
|
||||
|
||||
m_firstBufferSamplesConsumed = 0;
|
||||
|
||||
if (m_callbacks)
|
||||
m_callbacks->NotifyBufferFinished();
|
||||
|
||||
GpAudioChannelBufferChain_SDL2 *nextBuffer = buffer->m_next;
|
||||
buffer->Release();
|
||||
|
||||
buffer = nextBuffer;
|
||||
}
|
||||
|
||||
m_mutex->Unlock();
|
||||
@@ -289,25 +383,57 @@ bool GpAudioChannel_SDL2::Init(GpAudioDriver_SDL2 *driver)
|
||||
return true;
|
||||
}
|
||||
|
||||
void GpAudioChannel_SDL2::Consume(uint8_t *output, size_t sz)
|
||||
void GpAudioChannel_SDL2::Consume(int16_t *output, size_t sz, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime)
|
||||
{
|
||||
m_mutex->Lock();
|
||||
|
||||
while (m_firstPendingBuffer != nullptr)
|
||||
m_isMixing = true;
|
||||
m_hasTimestamp = true;
|
||||
m_timestamp = mixEndTime;
|
||||
|
||||
if (sz <= m_leadingSilence)
|
||||
{
|
||||
GpAudioChannelBufferChain_SDL2 *buffer = m_firstPendingBuffer;
|
||||
const size_t available = (buffer->m_used - buffer->m_consumed);
|
||||
memset(output, 0, sz * sizeof(AudioSample_t));
|
||||
m_leadingSilence -= sz;
|
||||
|
||||
m_isMixing = false;
|
||||
m_mutex->Unlock();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t leadingSilence = m_leadingSilence;
|
||||
if (leadingSilence > 0)
|
||||
{
|
||||
memset(output, 0, leadingSilence * sizeof(AudioSample_t));
|
||||
output += leadingSilence;
|
||||
sz -= leadingSilence;
|
||||
|
||||
m_leadingSilence = 0;
|
||||
}
|
||||
}
|
||||
|
||||
while (m_numQueuedBuffers > 0)
|
||||
{
|
||||
GpAudioBuffer_SDL2 *buffer = m_pendingBuffers[m_nextPendingBufferConsumePos];
|
||||
const int16_t *bufferData = buffer->GetData();
|
||||
const size_t bufferSize = buffer->GetSize();
|
||||
|
||||
assert(m_firstBufferSamplesConsumed < bufferSize);
|
||||
|
||||
const size_t available = (bufferSize - m_firstBufferSamplesConsumed);
|
||||
if (available <= sz)
|
||||
{
|
||||
memcpy(output, buffer->m_data + buffer->m_consumed, available);
|
||||
memcpy(output, bufferData + m_firstBufferSamplesConsumed, available * sizeof(AudioSample_t));
|
||||
sz -= available;
|
||||
output += available;
|
||||
|
||||
m_firstPendingBuffer = buffer->m_next;
|
||||
if (m_firstPendingBuffer == nullptr)
|
||||
m_lastPendingBuffer = nullptr;
|
||||
m_nextPendingBufferConsumePos = (m_nextPendingBufferConsumePos + 1) % kMaxBuffers;
|
||||
m_numQueuedBuffers--;
|
||||
|
||||
if (buffer->m_hasTrigger && m_callbacks)
|
||||
m_firstBufferSamplesConsumed = 0;
|
||||
|
||||
if (m_callbacks)
|
||||
m_callbacks->NotifyBufferFinished();
|
||||
|
||||
buffer->Release();
|
||||
@@ -317,26 +443,28 @@ void GpAudioChannel_SDL2::Consume(uint8_t *output, size_t sz)
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(output, buffer->m_data + buffer->m_consumed, sz);
|
||||
buffer->m_consumed += sz;
|
||||
buffer += sz;
|
||||
memcpy(output, bufferData + m_firstBufferSamplesConsumed, sz * sizeof(AudioSample_t));
|
||||
m_firstBufferSamplesConsumed += sz;
|
||||
output += sz;
|
||||
sz = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_isMixing = false;
|
||||
|
||||
m_mutex->Unlock();
|
||||
|
||||
memset(output, 0x80, sz);
|
||||
memset(output, 0, sz * sizeof(AudioSample_t));
|
||||
}
|
||||
|
||||
GpAudioChannel_SDL2 *GpAudioChannel_SDL2::Alloc(GpAudioDriver_SDL2 *driver)
|
||||
GpAudioChannel_SDL2 *GpAudioChannel_SDL2::Alloc(GpAudioDriver_SDL2 *driver, GpAudioDriver_SDL2_Duration_t latency, GpAudioDriver_SDL2_Duration_t bufferTime, size_t bufferSamplesMax, uint16_t sampleRate)
|
||||
{
|
||||
void *storage = AlignedAlloc(sizeof(GpAudioChannel_SDL2), GP_SYSTEM_MEMORY_ALIGNMENT);
|
||||
if (!storage)
|
||||
return nullptr;
|
||||
|
||||
GpAudioChannel_SDL2 *channel = new (storage) GpAudioChannel_SDL2();
|
||||
GpAudioChannel_SDL2 *channel = new (storage) GpAudioChannel_SDL2(latency, bufferTime, bufferSamplesMax, sampleRate);
|
||||
if (!channel->Init(driver))
|
||||
{
|
||||
channel->Destroy();
|
||||
@@ -354,15 +482,18 @@ GpAudioDriver_SDL2::GpAudioDriver_SDL2(const GpAudioDriverProperties &properties
|
||||
: m_properties(properties)
|
||||
, m_mutex(nullptr)
|
||||
, m_numChannels(0)
|
||||
, m_sampleRate(0)
|
||||
, m_latency(GpAudioDriver_SDL2_Duration_t::zero())
|
||||
, m_bufferTime(GpAudioDriver_SDL2_Duration_t::zero())
|
||||
, m_sdlAudioRunning(false)
|
||||
, m_mixChunkReadOffset(kMixChunkSize)
|
||||
, m_mixChunkReadOffset(kMixChunkSamples)
|
||||
, m_audioVolumeScale(kMaxAudioVolumeScale)
|
||||
|
||||
{
|
||||
for (size_t i = 0; i < kMaxChannels; i++)
|
||||
m_channels[i] = nullptr;
|
||||
|
||||
for (size_t i = 0; i < kMixChunkSize; i++)
|
||||
for (size_t i = 0; i < kMixChunkSamples; i++)
|
||||
m_mixChunk[i] = 0;
|
||||
}
|
||||
|
||||
@@ -375,9 +506,14 @@ GpAudioDriver_SDL2::~GpAudioDriver_SDL2()
|
||||
m_mutex->Destroy();
|
||||
}
|
||||
|
||||
IGpAudioBuffer *GpAudioDriver_SDL2::CreateBuffer(const void *data, size_t size)
|
||||
{
|
||||
return GpAudioBuffer_SDL2::Create(data, size);
|
||||
}
|
||||
|
||||
IGpAudioChannel *GpAudioDriver_SDL2::CreateChannel()
|
||||
{
|
||||
GpAudioChannel_SDL2 *newChannel = GpAudioChannel_SDL2::Alloc(this);
|
||||
GpAudioChannel_SDL2 *newChannel = GpAudioChannel_SDL2::Alloc(this, m_latency, m_bufferTime, m_bufferSamples, m_sampleRate);
|
||||
if (!newChannel)
|
||||
return nullptr;
|
||||
|
||||
@@ -437,7 +573,7 @@ bool GpAudioDriver_SDL2::Init()
|
||||
requestedSpec.channels = 1;
|
||||
requestedSpec.format = AUDIO_S16;
|
||||
requestedSpec.freq = m_properties.m_sampleRate;
|
||||
requestedSpec.samples = 512;
|
||||
requestedSpec.samples = kMixChunkSamples;
|
||||
requestedSpec.userdata = this;
|
||||
|
||||
if (SDL_OpenAudio(&requestedSpec, nullptr))
|
||||
@@ -450,6 +586,10 @@ bool GpAudioDriver_SDL2::Init()
|
||||
SDL_PauseAudio(0);
|
||||
|
||||
m_sdlAudioRunning = true;
|
||||
m_sampleRate = requestedSpec.freq;
|
||||
m_latency = GpAudioDriver_SDL2_Duration_t(static_cast<GpAudioDriver_SDL2_Rep_t>(GpAudioDriver_SDL2_Period_t::den * requestedSpec.samples / GpAudioDriver_SDL2_Period_t::num / m_sampleRate));
|
||||
m_bufferTime = GpAudioDriver_SDL2_Duration_t(static_cast<GpAudioDriver_SDL2_Rep_t>(GpAudioDriver_SDL2_Period_t::den * requestedSpec.samples / GpAudioDriver_SDL2_Period_t::num / m_sampleRate));
|
||||
m_bufferSamples = requestedSpec.samples;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -492,11 +632,16 @@ void GpAudioDriver_SDL2::MixAudio(void *stream, size_t len)
|
||||
}
|
||||
m_mutex->Unlock();
|
||||
|
||||
size_t samplesRemaining = len / sizeof(int16_t);
|
||||
const size_t totalSamples = len / sizeof(int16_t);
|
||||
size_t samplesRemaining = totalSamples;
|
||||
|
||||
GpAudioDriver_SDL2_TimePoint_t audioMixStartTime = GpAudioDriver_SDL2::GetCurrentTime();
|
||||
GpAudioDriver_SDL2_TimePoint_t audioMixBlockStartTime = audioMixStartTime;
|
||||
|
||||
size_t samplesSinceStart = 0;
|
||||
for (;;)
|
||||
{
|
||||
size_t availableInMixChunk = kMixChunkSize - m_mixChunkReadOffset;
|
||||
size_t availableInMixChunk = kMixChunkSamples - m_mixChunkReadOffset;
|
||||
|
||||
if (availableInMixChunk > samplesRemaining)
|
||||
{
|
||||
@@ -510,10 +655,17 @@ void GpAudioDriver_SDL2::MixAudio(void *stream, size_t len)
|
||||
memcpy(stream, m_mixChunk + m_mixChunkReadOffset, availableInMixChunk * sizeof(int16_t));
|
||||
|
||||
stream = static_cast<int16_t*>(stream) + availableInMixChunk;
|
||||
samplesRemaining -= availableInMixChunk;
|
||||
|
||||
samplesSinceStart += availableInMixChunk;
|
||||
|
||||
GpAudioDriver_SDL2_Duration_t audioMixDurationSinceStart = GpAudioDriver_SDL2_Duration_t(static_cast<GpAudioDriver_SDL2_Rep_t>(GpAudioDriver_SDL2_Period_t::den * samplesSinceStart / GpAudioDriver_SDL2_Period_t::num / m_sampleRate));
|
||||
GpAudioDriver_SDL2_TimePoint_t audioMixBlockEndTime = audioMixStartTime + audioMixDurationSinceStart;
|
||||
|
||||
m_mixChunkReadOffset = 0;
|
||||
RefillMixChunk(mixingChannels, numChannels);
|
||||
RefillMixChunk(mixingChannels, numChannels, samplesRemaining, audioMixBlockStartTime, audioMixBlockEndTime);
|
||||
audioMixBlockStartTime = audioMixBlockEndTime;
|
||||
|
||||
samplesRemaining -= availableInMixChunk;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,42 +673,66 @@ void GpAudioDriver_SDL2::MixAudio(void *stream, size_t len)
|
||||
mixingChannels[i]->Release();
|
||||
}
|
||||
|
||||
void GpAudioDriver_SDL2::RefillMixChunk(GpAudioChannel_SDL2 *const*channels, size_t numChannels)
|
||||
void GpAudioDriver_SDL2::RefillMixChunk(GpAudioChannel_SDL2 *const*channels, size_t numChannels, size_t maxSamplesToFill, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime)
|
||||
{
|
||||
uint8_t audioMixBufferUnaligned[kMixChunkSize + GP_SYSTEM_MEMORY_ALIGNMENT];
|
||||
uint8_t *audioMixBuffer = audioMixBufferUnaligned;
|
||||
uint8_t audioMixBufferUnaligned[kMixChunkSamples * sizeof(AudioSample_t) + GP_SYSTEM_MEMORY_ALIGNMENT];
|
||||
uint8_t *audioMixBufferBytes = audioMixBufferUnaligned;
|
||||
|
||||
{
|
||||
uintptr_t bufferPtr = reinterpret_cast<uintptr_t>(audioMixBuffer);
|
||||
uintptr_t bufferPtr = reinterpret_cast<uintptr_t>(audioMixBufferBytes);
|
||||
size_t alignPadding = GP_SYSTEM_MEMORY_ALIGNMENT - (bufferPtr % GP_SYSTEM_MEMORY_ALIGNMENT);
|
||||
audioMixBuffer += alignPadding;
|
||||
audioMixBufferBytes += alignPadding;
|
||||
}
|
||||
|
||||
AudioSample_t *audioMixBuffer = reinterpret_cast<AudioSample_t*>(audioMixBufferBytes);
|
||||
bool noAudio = true;
|
||||
|
||||
const int16_t audioVolumeScale = m_audioVolumeScale;
|
||||
|
||||
size_t samplesToFill = kMixChunkSamples;
|
||||
if (samplesToFill > maxSamplesToFill)
|
||||
{
|
||||
m_mixChunkReadOffset += samplesToFill - maxSamplesToFill;
|
||||
samplesToFill = maxSamplesToFill;
|
||||
}
|
||||
else
|
||||
m_mixChunkReadOffset = 0;
|
||||
|
||||
AudioSample_t *mixChunkStart = m_mixChunk + m_mixChunkReadOffset;
|
||||
|
||||
for (size_t i = 0; i < numChannels; i++)
|
||||
{
|
||||
channels[i]->Consume(audioMixBuffer, kMixChunkSize);
|
||||
channels[i]->Consume(audioMixBuffer, samplesToFill, mixStartTime, mixEndTime);
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
noAudio = false;
|
||||
for (size_t j = 0; j < kMixChunkSize; j++)
|
||||
m_mixChunk[j] = (static_cast<int16_t>(audioMixBuffer[j]) - 0x80) * audioVolumeScale;
|
||||
memcpy(mixChunkStart, audioMixBuffer, samplesToFill * sizeof(AudioSample_t));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t j = 0; j < kMixChunkSize; j++)
|
||||
m_mixChunk[j] += (static_cast<int16_t>(audioMixBuffer[j]) - 0x80) * audioVolumeScale;
|
||||
}
|
||||
AddSamples(mixChunkStart, audioMixBuffer, samplesToFill);
|
||||
}
|
||||
|
||||
if (noAudio)
|
||||
memset(m_mixChunk, 0, kMixChunkSize * sizeof(m_mixChunk[0]));
|
||||
memset(mixChunkStart, 0, samplesToFill * sizeof(AudioSample_t));
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < samplesToFill; i++)
|
||||
mixChunkStart[i] *= audioVolumeScale;
|
||||
}
|
||||
}
|
||||
|
||||
void GpAudioDriver_SDL2::AddSamples(AudioSample_t *GP_RESTRICT dest, const AudioSample_t *GP_RESTRICT src, size_t nSamples)
|
||||
{
|
||||
for (size_t i = 0; i < nSamples; i++)
|
||||
dest[i] += src[i];
|
||||
}
|
||||
|
||||
|
||||
GpAudioDriver_SDL2_TimePoint_t GpAudioDriver_SDL2::GetCurrentTime()
|
||||
{
|
||||
return std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
IGpAudioDriver *GpDriver_CreateAudioDriver_SDL(const GpAudioDriverProperties &properties)
|
||||
{
|
||||
|
@@ -1,12 +1,14 @@
|
||||
#include "IGpDisplayDriver.h"
|
||||
|
||||
#include "CoreDefs.h"
|
||||
#include "GpApplicationName.h"
|
||||
#include "GpComPtr.h"
|
||||
#include "GpFiber_SDL.h"
|
||||
#include "GpDisplayDriverProperties.h"
|
||||
#include "GpVOSEvent.h"
|
||||
#include "GpRingBuffer.h"
|
||||
#include "GpInputDriver_SDL_Gamepad.h"
|
||||
#include "GpSDL.h"
|
||||
#include "GpUnicode.h"
|
||||
#include "IGpCursor.h"
|
||||
#include "IGpDisplayDriverSurface.h"
|
||||
#include "IGpLogDriver.h"
|
||||
@@ -28,6 +30,9 @@
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
#pragma push_macro("LoadCursor")
|
||||
#ifdef LoadCursor
|
||||
@@ -48,68 +53,6 @@ struct GpDisplayDriver_SDL_GL2_Prefs
|
||||
bool m_isFullScreen;
|
||||
};
|
||||
|
||||
namespace DeleteMe
|
||||
{
|
||||
bool DecodeCodePoint(const uint8_t *characters, size_t availableCharacters, size_t &outCharactersDigested, uint32_t &outCodePoint)
|
||||
{
|
||||
if (availableCharacters <= 0)
|
||||
return false;
|
||||
|
||||
if ((characters[0] & 0x80) == 0x00)
|
||||
{
|
||||
outCharactersDigested = 1;
|
||||
outCodePoint = characters[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t sz = 0;
|
||||
uint32_t codePoint = 0;
|
||||
uint32_t minCodePoint = 0;
|
||||
if ((characters[0] & 0xe0) == 0xc0)
|
||||
{
|
||||
sz = 2;
|
||||
minCodePoint = 0x80;
|
||||
codePoint = (characters[0] & 0x1f);
|
||||
}
|
||||
else if ((characters[0] & 0xf0) == 0xe0)
|
||||
{
|
||||
sz = 3;
|
||||
minCodePoint = 0x800;
|
||||
codePoint = (characters[0] & 0x0f);
|
||||
}
|
||||
else if ((characters[0] & 0xf8) == 0xf0)
|
||||
{
|
||||
sz = 4;
|
||||
minCodePoint = 0x10000;
|
||||
codePoint = (characters[0] & 0x07);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
if (availableCharacters < sz)
|
||||
return false;
|
||||
|
||||
for (size_t auxByte = 1; auxByte < sz; auxByte++)
|
||||
{
|
||||
if ((characters[auxByte] & 0xc0) != 0x80)
|
||||
return false;
|
||||
|
||||
codePoint = (codePoint << 6) | (characters[auxByte] & 0x3f);
|
||||
}
|
||||
|
||||
if (codePoint < minCodePoint || codePoint > 0x10ffff)
|
||||
return false;
|
||||
|
||||
if (codePoint >= 0xd800 && codePoint <= 0xdfff)
|
||||
return false;
|
||||
|
||||
outCodePoint = codePoint;
|
||||
outCharactersDigested = sz;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace GpBinarizedShaders
|
||||
{
|
||||
extern const char *g_drawQuadV_GL2;
|
||||
@@ -214,6 +157,7 @@ struct GpGLFunctions
|
||||
|
||||
static void CheckGLError(const GpGLFunctions &gl, IGpLogDriver *logger)
|
||||
{
|
||||
#if GP_DEBUG_CONFIG
|
||||
GLenum errorCode = gl.GetError();
|
||||
if (errorCode != 0)
|
||||
{
|
||||
@@ -222,6 +166,7 @@ static void CheckGLError(const GpGLFunctions &gl, IGpLogDriver *logger)
|
||||
}
|
||||
|
||||
assert(errorCode == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
class GpGLObject
|
||||
@@ -729,17 +674,18 @@ public:
|
||||
explicit GpDisplayDriver_SDL_GL2(const GpDisplayDriverProperties &properties);
|
||||
~GpDisplayDriver_SDL_GL2();
|
||||
|
||||
bool Init();
|
||||
bool Init() GP_ASYNCIFY_PARANOID_OVERRIDE;
|
||||
void ServeTicks(int tickCount) GP_ASYNCIFY_PARANOID_OVERRIDE;
|
||||
void ForceSync() override;
|
||||
void Shutdown() GP_ASYNCIFY_PARANOID_OVERRIDE;
|
||||
|
||||
void TranslateSDLMessage(const SDL_Event *msg, IGpVOSEventQueue *eventQueue, float pixelScaleX, float pixelScaleY, bool obstructiveTextInput);
|
||||
|
||||
void Run() override;
|
||||
void Shutdown() override;
|
||||
void GetInitialDisplayResolution(unsigned int *width, unsigned int *height) override;
|
||||
IGpDisplayDriverSurface *CreateSurface(size_t width, size_t height, size_t pitch, GpPixelFormat_t pixelFormat, SurfaceInvalidateCallback_t invalidateCallback, void *invalidateContext) override;
|
||||
void DrawSurface(IGpDisplayDriverSurface *surface, int32_t x, int32_t y, size_t width, size_t height, const GpDisplayDriverSurfaceEffects *effects) override;
|
||||
IGpCursor *CreateBWCursor(size_t width, size_t height, const void *pixelData, const void *maskData, size_t hotSpotX, size_t hotSpotY) override;
|
||||
IGpCursor *CreateColorCursor(size_t width, size_t height, const void *pixelDataRGBA, size_t hotSpotX, size_t hotSpotY) override;
|
||||
IGpCursor *CreateBWCursor(size_t width, size_t height, const void *pixelData, const void *maskData, size_t hotSpotX, size_t hotSpotY) GP_ASYNCIFY_PARANOID_OVERRIDE;
|
||||
IGpCursor *CreateColorCursor(size_t width, size_t height, const void *pixelDataRGBA, size_t hotSpotX, size_t hotSpotY) GP_ASYNCIFY_PARANOID_OVERRIDE;
|
||||
void SetCursor(IGpCursor *cursor) override;
|
||||
void SetStandardCursor(EGpStandardCursor_t standardCursor) override;
|
||||
void UpdatePalette(const void *paletteData) override;
|
||||
@@ -797,7 +743,7 @@ private:
|
||||
|
||||
void ScaleVirtualScreen();
|
||||
|
||||
GpDisplayDriverTickStatus_t PresentFrameAndSync();
|
||||
bool SyncRender();
|
||||
|
||||
GpGLFunctions m_gl;
|
||||
GpDisplayDriverProperties m_properties;
|
||||
@@ -839,9 +785,6 @@ private:
|
||||
GpComPtr<GpGLRenderTargetView> m_upscaleTextureRTV;
|
||||
GpComPtr<GpGLTexture> m_upscaleTexture;
|
||||
|
||||
uint32_t m_upscaleTextureWidth;
|
||||
uint32_t m_upscaleTextureHeight;
|
||||
|
||||
GpComPtr<GpGLVertexArray> m_quadVertexArray;
|
||||
GpComPtr<GpGLBuffer> m_quadVertexBufferKeepalive;
|
||||
GpComPtr<GpGLBuffer> m_quadIndexBuffer;
|
||||
@@ -898,7 +841,10 @@ private:
|
||||
uint32_t m_initialHeightVirtual;
|
||||
float m_pixelScaleX;
|
||||
float m_pixelScaleY;
|
||||
|
||||
bool m_useUpscaleFilter;
|
||||
uint32_t m_upscaleTextureWidth;
|
||||
uint32_t m_upscaleTextureHeight;
|
||||
|
||||
GpCursor_SDL2 *m_activeCursor;
|
||||
GpCursor_SDL2 *m_pendingCursor;
|
||||
@@ -906,9 +852,6 @@ private:
|
||||
EGpStandardCursor_t m_pendingStandardCursor;
|
||||
bool m_mouseIsInClientArea;
|
||||
|
||||
IGpFiber *m_vosFiber;
|
||||
IGpThreadEvent *m_vosEvent;
|
||||
|
||||
float m_bgColor[4];
|
||||
bool m_bgIsDark;
|
||||
|
||||
@@ -1193,8 +1136,8 @@ GpDisplayDriver_SDL_GL2::GpDisplayDriver_SDL_GL2(const GpDisplayDriverProperties
|
||||
, m_pixelScaleX(1.0f)
|
||||
, m_pixelScaleY(1.0f)
|
||||
, m_useUpscaleFilter(false)
|
||||
, m_vosFiber(nullptr)
|
||||
, m_vosEvent(nullptr)
|
||||
, m_upscaleTextureWidth(0)
|
||||
, m_upscaleTextureHeight(0)
|
||||
, m_pendingCursor(nullptr)
|
||||
, m_activeCursor(nullptr)
|
||||
, m_currentStandardCursor(EGpStandardCursors::kArrow)
|
||||
@@ -1339,9 +1282,246 @@ GpDisplayDriver_SDL_GL2::~GpDisplayDriver_SDL_GL2()
|
||||
|
||||
bool GpDisplayDriver_SDL_GL2::Init()
|
||||
{
|
||||
#if GP_GL_IS_OPENGL_4_CONTEXT
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
||||
#else
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||
#endif
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
|
||||
IGpLogDriver *logger = m_properties.m_logger;
|
||||
|
||||
uint32_t windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
|
||||
if (m_properties.m_systemServices->IsFullscreenOnStartup())
|
||||
{
|
||||
windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
m_isFullScreen = true;
|
||||
}
|
||||
else
|
||||
windowFlags |= SDL_WINDOW_RESIZABLE;
|
||||
|
||||
m_window = SDL_CreateWindow(GP_APPLICATION_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, m_windowWidthPhysical, m_windowHeightPhysical, windowFlags);
|
||||
|
||||
if (m_isFullScreen)
|
||||
{
|
||||
m_windowModeRevertWidth = m_windowWidthPhysical;
|
||||
m_windowModeRevertHeight = m_windowHeightPhysical;
|
||||
|
||||
int windowWidth = 0;
|
||||
int windowHeight = 0;
|
||||
SDL_GetWindowSize(m_window, &windowWidth, &windowHeight);
|
||||
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Initialized fullscreen SDL window %i x %i", windowWidth, windowHeight);
|
||||
|
||||
m_windowWidthPhysical = windowWidth;
|
||||
m_windowHeightPhysical = windowHeight;
|
||||
|
||||
uint32_t desiredWidth = windowWidth;
|
||||
uint32_t desiredHeight = windowHeight;
|
||||
uint32_t virtualWidth = m_windowWidthVirtual;
|
||||
uint32_t virtualHeight = m_windowHeightVirtual;
|
||||
float pixelScaleX = m_pixelScaleX;
|
||||
float pixelScaleY = m_pixelScaleY;
|
||||
|
||||
if (m_properties.m_adjustRequestedResolutionFunc(m_properties.m_adjustRequestedResolutionFuncContext, desiredWidth, desiredHeight, virtualWidth, virtualHeight, pixelScaleX, pixelScaleY))
|
||||
{
|
||||
m_windowWidthVirtual = virtualWidth;
|
||||
m_windowHeightVirtual = virtualHeight;
|
||||
m_pixelScaleX = pixelScaleX;
|
||||
m_pixelScaleY = pixelScaleY;
|
||||
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "AdjustedRequestedResolution succeeded. Virtual dimensions %i x %i Pixel scale %f x %f", static_cast<int>(virtualWidth), static_cast<int>(virtualHeight), static_cast<float>(pixelScaleX), static_cast<float>(pixelScaleY));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Error, "AdjustedRequestedResolution failed!");
|
||||
}
|
||||
}
|
||||
|
||||
const bool obstructiveTextInput = m_properties.m_systemServices->IsTextInputObstructive();
|
||||
|
||||
if (!obstructiveTextInput)
|
||||
SDL_StartTextInput();
|
||||
|
||||
StartOpenGLForWindow(logger);
|
||||
|
||||
if (!m_gl.LookUpFunctions())
|
||||
return false;
|
||||
|
||||
m_initialWidthVirtual = m_windowWidthVirtual;
|
||||
m_initialHeightVirtual = m_windowHeightVirtual;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GpDisplayDriver_SDL_GL2::ServeTicks(int ticks)
|
||||
{
|
||||
IGpLogDriver *logger = m_properties.m_logger;
|
||||
const bool obstructiveTextInput = m_properties.m_systemServices->IsTextInputObstructive();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
SDL_Event msg;
|
||||
if (SDL_PollEvent(&msg) != 0)
|
||||
{
|
||||
switch (msg.type)
|
||||
{
|
||||
case SDL_MOUSEMOTION:
|
||||
{
|
||||
if (!m_mouseIsInClientArea)
|
||||
m_mouseIsInClientArea = true;
|
||||
}
|
||||
break;
|
||||
//case SDL_MOUSELEAVE: // Does SDL support this??
|
||||
// m_mouseIsInClientArea = false;
|
||||
// break;
|
||||
case SDL_RENDER_DEVICE_RESET:
|
||||
case SDL_RENDER_TARGETS_RESET:
|
||||
{
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Triggering GL context reset due to device loss (Type: %i)", static_cast<int>(msg.type));
|
||||
|
||||
m_contextLost = true;
|
||||
}
|
||||
break;
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
case SDL_CONTROLLERDEVICEREMAPPED:
|
||||
if (IGpInputDriverSDLGamepad *gamepadDriver = IGpInputDriverSDLGamepad::GetInstance())
|
||||
gamepadDriver->ProcessSDLEvent(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
TranslateSDLMessage(&msg, m_properties.m_eventQueue, m_pixelScaleX, m_pixelScaleY, obstructiveTextInput);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_isFullScreen != m_isFullScreenDesired)
|
||||
{
|
||||
if (m_isFullScreenDesired)
|
||||
BecomeFullScreen();
|
||||
else
|
||||
BecomeWindowed();
|
||||
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Triggering GL context reset due to fullscreen state change");
|
||||
|
||||
m_contextLost = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
int clientWidth = 0;
|
||||
int clientHeight = 0;
|
||||
SDL_GetWindowSize(m_window, &clientWidth, &clientHeight);
|
||||
|
||||
unsigned int desiredWidth = clientWidth;
|
||||
unsigned int desiredHeight = clientHeight;
|
||||
if (desiredWidth != m_windowWidthPhysical || desiredHeight != m_windowHeightPhysical || m_isResolutionResetDesired)
|
||||
{
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Detected window size change");
|
||||
|
||||
uint32_t prevWidthPhysical = m_windowWidthPhysical;
|
||||
uint32_t prevHeightPhysical = m_windowHeightPhysical;
|
||||
uint32_t prevWidthVirtual = m_windowWidthVirtual;
|
||||
uint32_t prevHeightVirtual = m_windowHeightVirtual;
|
||||
uint32_t virtualWidth = m_windowWidthVirtual;
|
||||
uint32_t virtualHeight = m_windowHeightVirtual;
|
||||
float pixelScaleX = 1.0f;
|
||||
float pixelScaleY = 1.0f;
|
||||
|
||||
if (m_properties.m_adjustRequestedResolutionFunc(m_properties.m_adjustRequestedResolutionFuncContext, desiredWidth, desiredHeight, virtualWidth, virtualHeight, pixelScaleX, pixelScaleY))
|
||||
{
|
||||
bool resizedOK = ResizeOpenGLWindow(m_windowWidthPhysical, m_windowHeightPhysical, desiredWidth, desiredHeight, logger);
|
||||
|
||||
if (!resizedOK)
|
||||
break; // Critical video driver error, exit
|
||||
|
||||
m_windowWidthVirtual = virtualWidth;
|
||||
m_windowHeightVirtual = virtualHeight;
|
||||
m_pixelScaleX = pixelScaleX;
|
||||
m_pixelScaleY = pixelScaleY;
|
||||
m_isResolutionResetDesired = false;
|
||||
|
||||
if (GpVOSEvent *resizeEvent = m_properties.m_eventQueue->QueueEvent())
|
||||
{
|
||||
resizeEvent->m_eventType = GpVOSEventTypes::kVideoResolutionChanged;
|
||||
resizeEvent->m_event.m_resolutionChangedEvent.m_prevWidth = prevWidthVirtual;
|
||||
resizeEvent->m_event.m_resolutionChangedEvent.m_prevHeight = prevHeightVirtual;
|
||||
resizeEvent->m_event.m_resolutionChangedEvent.m_newWidth = m_windowWidthVirtual;
|
||||
resizeEvent->m_event.m_resolutionChangedEvent.m_newHeight = m_windowHeightVirtual;
|
||||
}
|
||||
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Triggering GL context reset due to window size change");
|
||||
|
||||
m_contextLost = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_contextLost)
|
||||
{
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Resetting OpenGL context. Physical: %i x %i Virtual %i x %i", static_cast<int>(m_windowWidthPhysical), static_cast<int>(m_windowHeightPhysical), static_cast<int>(m_windowWidthVirtual), static_cast<int>(m_windowHeightVirtual));
|
||||
|
||||
// Drop everything and reset
|
||||
m_res = InstancedResources();
|
||||
|
||||
if (m_firstSurface)
|
||||
m_firstSurface->DestroyAll();
|
||||
|
||||
if (!InitResources(m_windowWidthPhysical, m_windowHeightPhysical, m_windowWidthVirtual, m_windowHeightVirtual))
|
||||
{
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Terminating display driver due to InitResources failing");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_firstSurface)
|
||||
m_firstSurface->RecreateAll();
|
||||
|
||||
m_contextLost = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool wantTextInput = m_properties.m_systemServices->IsTextInputEnabled();
|
||||
if (wantTextInput != m_textInputEnabled)
|
||||
{
|
||||
m_textInputEnabled = wantTextInput;
|
||||
if (m_textInputEnabled)
|
||||
SDL_StartTextInput();
|
||||
else
|
||||
SDL_StopTextInput();
|
||||
}
|
||||
|
||||
// Handle dismissal of on-screen keyboard
|
||||
const bool isTextInputActuallyActive = SDL_IsTextInputActive();
|
||||
m_textInputEnabled = isTextInputActuallyActive;
|
||||
m_properties.m_systemServices->SetTextInputEnabled(isTextInputActuallyActive);
|
||||
|
||||
if (SyncRender())
|
||||
{
|
||||
ticks--;
|
||||
if (ticks <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GpDisplayDriver_SDL_GL2::ForceSync()
|
||||
{
|
||||
m_frameTimeAccumulated = std::chrono::nanoseconds::zero();
|
||||
}
|
||||
|
||||
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())
|
||||
@@ -1379,7 +1559,7 @@ static void PostTouchEvent(IGpVOSEventQueue *eventQueue, GpTouchEventType_t even
|
||||
|
||||
static bool IdentifyVKey(const SDL_KeyboardEvent *keyEvt, GpKeyIDSubset_t &outSubset, GpKeyboardInputEvent::KeyUnion &outKey)
|
||||
{
|
||||
SDL_KeyCode keyCode = static_cast<SDL_KeyCode>(keyEvt->keysym.sym);
|
||||
SDL_Keycode keyCode = static_cast<SDL_Keycode>(keyEvt->keysym.sym);
|
||||
|
||||
switch (keyCode)
|
||||
{
|
||||
@@ -1457,6 +1637,14 @@ static bool IdentifyVKey(const SDL_KeyboardEvent *keyEvt, GpKeyIDSubset_t &outSu
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case SDLK_LGUI:
|
||||
outSubset = GpKeyIDSubsets::kSpecial;
|
||||
outKey.m_specialKey = GpKeySpecials::kLeftCommand;
|
||||
break;
|
||||
case SDLK_RGUI:
|
||||
outSubset = GpKeyIDSubsets::kSpecial;
|
||||
outKey.m_specialKey = GpKeySpecials::kRightCommand;
|
||||
break;
|
||||
case SDLK_LCTRL:
|
||||
outSubset = GpKeyIDSubsets::kSpecial;
|
||||
outKey.m_specialKey = GpKeySpecials::kLeftCtrl;
|
||||
@@ -1800,7 +1988,7 @@ void GpDisplayDriver_SDL_GL2::TranslateSDLMessage(const SDL_Event *msg, IGpVOSEv
|
||||
{
|
||||
uint32_t codePoint;
|
||||
size_t numDigested;
|
||||
DeleteMe::DecodeCodePoint(reinterpret_cast<const uint8_t*>(teEvt->text) + parseOffset, lenUTF8 - parseOffset, numDigested, codePoint);
|
||||
GpUnicode::UTF8::Decode(reinterpret_cast<const uint8_t*>(teEvt->text) + parseOffset, lenUTF8 - parseOffset, numDigested, codePoint);
|
||||
|
||||
parseOffset += numDigested;
|
||||
|
||||
@@ -1835,248 +2023,6 @@ void GpDisplayDriver_SDL_GL2::TranslateSDLMessage(const SDL_Event *msg, IGpVOSEv
|
||||
}
|
||||
}
|
||||
|
||||
void GpDisplayDriver_SDL_GL2::Run()
|
||||
{
|
||||
#if GP_GL_IS_OPENGL_4_CONTEXT
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
||||
#else
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||
#endif
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
|
||||
IGpLogDriver *logger = m_properties.m_logger;
|
||||
|
||||
m_vosEvent = m_properties.m_systemServices->CreateThreadEvent(true, false);
|
||||
m_vosFiber = new GpFiber_SDL(nullptr, m_vosEvent);
|
||||
|
||||
uint32_t windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
|
||||
if (m_properties.m_systemServices->IsFullscreenOnStartup())
|
||||
{
|
||||
windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
m_isFullScreen = true;
|
||||
}
|
||||
else
|
||||
windowFlags |= SDL_WINDOW_RESIZABLE;
|
||||
|
||||
m_window = SDL_CreateWindow(GP_APPLICATION_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, m_windowWidthPhysical, m_windowHeightPhysical, windowFlags);
|
||||
|
||||
if (m_isFullScreen)
|
||||
{
|
||||
m_windowModeRevertWidth = m_windowWidthPhysical;
|
||||
m_windowModeRevertHeight = m_windowHeightPhysical;
|
||||
|
||||
int windowWidth = 0;
|
||||
int windowHeight = 0;
|
||||
SDL_GetWindowSize(m_window, &windowWidth, &windowHeight);
|
||||
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Initialized fullscreen SDL window %i x %i", windowWidth, windowHeight);
|
||||
|
||||
m_windowWidthPhysical = windowWidth;
|
||||
m_windowHeightPhysical = windowHeight;
|
||||
|
||||
uint32_t desiredWidth = windowWidth;
|
||||
uint32_t desiredHeight = windowHeight;
|
||||
uint32_t virtualWidth = m_windowWidthVirtual;
|
||||
uint32_t virtualHeight = m_windowHeightVirtual;
|
||||
float pixelScaleX = m_pixelScaleX;
|
||||
float pixelScaleY = m_pixelScaleY;
|
||||
|
||||
if (m_properties.m_adjustRequestedResolutionFunc(m_properties.m_adjustRequestedResolutionFuncContext, desiredWidth, desiredHeight, virtualWidth, virtualHeight, pixelScaleX, pixelScaleY))
|
||||
{
|
||||
m_windowWidthVirtual = virtualWidth;
|
||||
m_windowHeightVirtual = virtualHeight;
|
||||
m_pixelScaleX = pixelScaleX;
|
||||
m_pixelScaleY = pixelScaleY;
|
||||
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "AdjustedRequestedResolution succeeded. Virtual dimensions %i x %i Pixel scale %f x %f", static_cast<int>(virtualWidth), static_cast<int>(virtualHeight), static_cast<float>(pixelScaleX), static_cast<float>(pixelScaleY));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Error, "AdjustedRequestedResolution failed!");
|
||||
}
|
||||
}
|
||||
|
||||
const bool obstructiveTextInput = m_properties.m_systemServices->IsTextInputObstructive();
|
||||
|
||||
if (!obstructiveTextInput)
|
||||
SDL_StartTextInput();
|
||||
|
||||
StartOpenGLForWindow(logger);
|
||||
|
||||
if (!m_gl.LookUpFunctions())
|
||||
return;
|
||||
|
||||
m_initialWidthVirtual = m_windowWidthVirtual;
|
||||
m_initialHeightVirtual = m_windowHeightVirtual;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
SDL_Event msg;
|
||||
if (SDL_PollEvent(&msg) != 0)
|
||||
{
|
||||
switch (msg.type)
|
||||
{
|
||||
case SDL_MOUSEMOTION:
|
||||
{
|
||||
if (!m_mouseIsInClientArea)
|
||||
m_mouseIsInClientArea = true;
|
||||
}
|
||||
break;
|
||||
//case SDL_MOUSELEAVE: // Does SDL support this??
|
||||
// m_mouseIsInClientArea = false;
|
||||
// break;
|
||||
case SDL_RENDER_DEVICE_RESET:
|
||||
case SDL_RENDER_TARGETS_RESET:
|
||||
{
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Triggering GL context reset due to device loss (Type: %i)", static_cast<int>(msg.type));
|
||||
|
||||
m_contextLost = true;
|
||||
}
|
||||
break;
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
case SDL_CONTROLLERDEVICEREMAPPED:
|
||||
if (IGpInputDriverSDLGamepad *gamepadDriver = IGpInputDriverSDLGamepad::GetInstance())
|
||||
gamepadDriver->ProcessSDLEvent(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
TranslateSDLMessage(&msg, m_properties.m_eventQueue, m_pixelScaleX, m_pixelScaleY, obstructiveTextInput);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_isFullScreen != m_isFullScreenDesired)
|
||||
{
|
||||
if (m_isFullScreenDesired)
|
||||
BecomeFullScreen();
|
||||
else
|
||||
BecomeWindowed();
|
||||
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Triggering GL context reset due to fullscreen state change");
|
||||
|
||||
m_contextLost = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
int clientWidth = 0;
|
||||
int clientHeight = 0;
|
||||
SDL_GetWindowSize(m_window, &clientWidth, &clientHeight);
|
||||
|
||||
unsigned int desiredWidth = clientWidth;
|
||||
unsigned int desiredHeight = clientHeight;
|
||||
if (desiredWidth != m_windowWidthPhysical || desiredHeight != m_windowHeightPhysical || m_isResolutionResetDesired)
|
||||
{
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Detected window size change");
|
||||
|
||||
uint32_t prevWidthPhysical = m_windowWidthPhysical;
|
||||
uint32_t prevHeightPhysical = m_windowHeightPhysical;
|
||||
uint32_t prevWidthVirtual = m_windowWidthVirtual;
|
||||
uint32_t prevHeightVirtual = m_windowHeightVirtual;
|
||||
uint32_t virtualWidth = m_windowWidthVirtual;
|
||||
uint32_t virtualHeight = m_windowHeightVirtual;
|
||||
float pixelScaleX = 1.0f;
|
||||
float pixelScaleY = 1.0f;
|
||||
|
||||
if (m_properties.m_adjustRequestedResolutionFunc(m_properties.m_adjustRequestedResolutionFuncContext, desiredWidth, desiredHeight, virtualWidth, virtualHeight, pixelScaleX, pixelScaleY))
|
||||
{
|
||||
bool resizedOK = ResizeOpenGLWindow(m_windowWidthPhysical, m_windowHeightPhysical, desiredWidth, desiredHeight, logger);
|
||||
|
||||
if (!resizedOK)
|
||||
break; // Critical video driver error, exit
|
||||
|
||||
m_windowWidthVirtual = virtualWidth;
|
||||
m_windowHeightVirtual = virtualHeight;
|
||||
m_pixelScaleX = pixelScaleX;
|
||||
m_pixelScaleY = pixelScaleY;
|
||||
m_isResolutionResetDesired = false;
|
||||
|
||||
if (GpVOSEvent *resizeEvent = m_properties.m_eventQueue->QueueEvent())
|
||||
{
|
||||
resizeEvent->m_eventType = GpVOSEventTypes::kVideoResolutionChanged;
|
||||
resizeEvent->m_event.m_resolutionChangedEvent.m_prevWidth = prevWidthVirtual;
|
||||
resizeEvent->m_event.m_resolutionChangedEvent.m_prevHeight = prevHeightVirtual;
|
||||
resizeEvent->m_event.m_resolutionChangedEvent.m_newWidth = m_windowWidthVirtual;
|
||||
resizeEvent->m_event.m_resolutionChangedEvent.m_newHeight = m_windowHeightVirtual;
|
||||
}
|
||||
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Triggering GL context reset due to window size change");
|
||||
|
||||
m_contextLost = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_contextLost)
|
||||
{
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Resetting OpenGL context. Physical: %i x %i Virtual %i x %i", static_cast<int>(m_windowWidthPhysical), static_cast<int>(m_windowHeightPhysical), static_cast<int>(m_windowWidthVirtual), static_cast<int>(m_windowHeightVirtual));
|
||||
|
||||
// Drop everything and reset
|
||||
m_res.~InstancedResources();
|
||||
new (&m_res) InstancedResources();
|
||||
|
||||
if (m_firstSurface)
|
||||
m_firstSurface->DestroyAll();
|
||||
|
||||
if (!InitResources(m_windowWidthPhysical, m_windowHeightPhysical, m_windowWidthVirtual, m_windowHeightVirtual))
|
||||
{
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Terminating display driver due to InitResources failing");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_firstSurface)
|
||||
m_firstSurface->RecreateAll();
|
||||
|
||||
m_contextLost = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool wantTextInput = m_properties.m_systemServices->IsTextInputEnabled();
|
||||
if (wantTextInput != m_textInputEnabled)
|
||||
{
|
||||
m_textInputEnabled = wantTextInput;
|
||||
if (m_textInputEnabled)
|
||||
SDL_StartTextInput();
|
||||
else
|
||||
SDL_StopTextInput();
|
||||
}
|
||||
|
||||
// Handle dismissal of on-screen keyboard
|
||||
const bool isTextInputActuallyActive = SDL_IsTextInputActive();
|
||||
m_textInputEnabled = isTextInputActuallyActive;
|
||||
m_properties.m_systemServices->SetTextInputEnabled(isTextInputActuallyActive);
|
||||
|
||||
GpDisplayDriverTickStatus_t tickStatus = PresentFrameAndSync();
|
||||
if (tickStatus == GpDisplayDriverTickStatuses::kFatalFault || tickStatus == GpDisplayDriverTickStatuses::kApplicationTerminated)
|
||||
{
|
||||
if (logger)
|
||||
{
|
||||
if (tickStatus == GpDisplayDriverTickStatuses::kFatalFault)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Terminating display driver due to fatal fault");
|
||||
if (tickStatus == GpDisplayDriverTickStatuses::kApplicationTerminated)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Terminating display driver due to application termination");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exit
|
||||
}
|
||||
|
||||
void GpDisplayDriver_SDL_GL2::Shutdown()
|
||||
{
|
||||
this->~GpDisplayDriver_SDL_GL2();
|
||||
@@ -2460,11 +2406,6 @@ bool GpDisplayDriver_SDL_GL2::InitResources(uint32_t physicalWidth, uint32_t phy
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "GpDisplayDriver_SDL_GL2::InitResources");
|
||||
|
||||
if ((m_pixelScaleX < 2.0f && m_pixelScaleX > 1.0f) || (m_pixelScaleY < 2.0f && m_pixelScaleY > 1.0f))
|
||||
m_useUpscaleFilter = true;
|
||||
else
|
||||
m_useUpscaleFilter = false;
|
||||
|
||||
CheckGLError(m_gl, logger);
|
||||
|
||||
if (!InitBackBuffer(virtualWidth, virtualHeight))
|
||||
@@ -2541,22 +2482,20 @@ bool GpDisplayDriver_SDL_GL2::InitResources(uint32_t physicalWidth, uint32_t phy
|
||||
}
|
||||
|
||||
GpComPtr<GpGLShader<GL_VERTEX_SHADER>> drawQuadVertexShader = CreateShader<GL_VERTEX_SHADER>(GpBinarizedShaders::g_drawQuadV_GL2);
|
||||
|
||||
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuadPaletteFlickerPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadPalettePF_GL2);
|
||||
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuadPaletteNoFlickerPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadPalettePNF_GL2);
|
||||
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuad32FlickerPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuad32PF_GL2);
|
||||
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuad32NoFlickerPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuad32PNF_GL2);
|
||||
|
||||
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuadPaletteICCFPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadPaletteICCPF_GL2);
|
||||
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuadPaletteICCNFPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadPaletteICCPNF_GL2);
|
||||
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuad32ICCFPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuad32ICCPF_GL2);
|
||||
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuad32ICCNFPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuad32ICCPNF_GL2);
|
||||
|
||||
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> scaleQuadPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_scaleQuadP_GL2);
|
||||
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> copyQuadPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_copyQuadP_GL2);
|
||||
|
||||
if (!m_res.m_drawQuadPaletteFlickerProgram.Link(this, drawQuadVertexShader, drawQuadPaletteFlickerPixelShader)
|
||||
|| !m_res.m_drawQuadPaletteNoFlickerProgram.Link(this, drawQuadVertexShader, drawQuadPaletteNoFlickerPixelShader)
|
||||
|
||||
if (!m_res.m_drawQuadPaletteFlickerProgram.Link(this, drawQuadVertexShader, drawQuadPaletteFlickerPixelShader) ||
|
||||
!m_res.m_drawQuadPaletteNoFlickerProgram.Link(this, drawQuadVertexShader, drawQuadPaletteNoFlickerPixelShader)
|
||||
|| !m_res.m_drawQuad32FlickerProgram.Link(this, drawQuadVertexShader, drawQuad32FlickerPixelShader)
|
||||
|| !m_res.m_drawQuad32NoFlickerProgram.Link(this, drawQuadVertexShader, drawQuad32NoFlickerPixelShader)
|
||||
|| !m_res.m_drawQuadPaletteICCFlickerProgram.Link(this, drawQuadVertexShader, drawQuadPaletteICCFPixelShader)
|
||||
@@ -2564,8 +2503,8 @@ bool GpDisplayDriver_SDL_GL2::InitResources(uint32_t physicalWidth, uint32_t phy
|
||||
|| !m_res.m_drawQuad32ICCFlickerProgram.Link(this, drawQuadVertexShader, drawQuad32ICCFPixelShader)
|
||||
|| !m_res.m_drawQuad32ICCNoFlickerProgram.Link(this, drawQuadVertexShader, drawQuad32ICCNFPixelShader)
|
||||
|
||||
//|| !m_drawQuadRGBICCProgram.Link(this, drawQuadVertexShader, drawQuadRGBICCPixelShader)
|
||||
//|| !m_drawQuad15BitICCProgram.Link(this, drawQuadVertexShader, drawQuad15BitICCPixelShader)
|
||||
// || !m_drawQuadRGBICCProgram.Link(this, drawQuadVertexShader, drawQuadRGBICCPixelShader)
|
||||
// || !m_drawQuad15BitICCProgram.Link(this, drawQuadVertexShader, drawQuad15BitICCPixelShader)
|
||||
|| !m_res.m_scaleQuadProgram.Link(this, drawQuadVertexShader, scaleQuadPixelShader)
|
||||
|| !m_res.m_copyQuadProgram.Link(this, drawQuadVertexShader, copyQuadPixelShader))
|
||||
return false;
|
||||
@@ -2780,8 +2719,9 @@ bool GpDisplayDriver_SDL_GL2::InitBackBuffer(uint32_t width, uint32_t height)
|
||||
m_gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
m_useUpscaleFilter = (m_pixelScaleX == 1.5f || m_pixelScaleX == 2.5f || m_pixelScaleY == 1.5f || m_pixelScaleY == 2.5f);
|
||||
|
||||
if (m_pixelScaleX != floor(m_pixelScaleX) || m_pixelScaleY != floor(m_pixelScaleY))
|
||||
if (m_useUpscaleFilter)
|
||||
{
|
||||
uint32_t upscaleX = ceil(m_pixelScaleX);
|
||||
uint32_t upscaleY = ceil(m_pixelScaleY);
|
||||
@@ -2796,8 +2736,8 @@ bool GpDisplayDriver_SDL_GL2::InitBackBuffer(uint32_t width, uint32_t height)
|
||||
return false;
|
||||
}
|
||||
|
||||
m_res.m_upscaleTextureWidth = width * upscaleX;
|
||||
m_res.m_upscaleTextureHeight = height * upscaleY;
|
||||
m_upscaleTextureWidth = width * upscaleX;
|
||||
m_upscaleTextureHeight = height * upscaleY;
|
||||
|
||||
GLenum internalFormat = SupportsSizedFormats() ? GL_RGBA8 : GL_RGBA;
|
||||
|
||||
@@ -2807,7 +2747,7 @@ bool GpDisplayDriver_SDL_GL2::InitBackBuffer(uint32_t width, uint32_t height)
|
||||
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
m_gl.TexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_res.m_upscaleTextureWidth, m_res.m_upscaleTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
m_gl.TexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_upscaleTextureWidth, m_upscaleTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
m_gl.BindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
CheckGLError(m_gl, logger);
|
||||
@@ -2852,7 +2792,7 @@ void GpDisplayDriver_SDL_GL2::ScaleVirtualScreen()
|
||||
{
|
||||
m_gl.BindFramebuffer(GL_FRAMEBUFFER, m_res.m_upscaleTextureRTV->GetID());
|
||||
|
||||
m_gl.Viewport(0, 0, m_res.m_upscaleTextureWidth, m_res.m_upscaleTextureHeight);
|
||||
m_gl.Viewport(0, 0, m_upscaleTextureWidth, m_upscaleTextureHeight);
|
||||
|
||||
const BlitQuadProgram &program = m_res.m_scaleQuadProgram;
|
||||
|
||||
@@ -3058,8 +2998,15 @@ bool GpDisplayDriver_SDL_GL2::BlitQuadProgram::Link(GpDisplayDriver_SDL_GL2 *dri
|
||||
return true;
|
||||
}
|
||||
|
||||
GpDisplayDriverTickStatus_t GpDisplayDriver_SDL_GL2::PresentFrameAndSync()
|
||||
bool GpDisplayDriver_SDL_GL2::SyncRender()
|
||||
{
|
||||
if (m_frameTimeAccumulated >= m_frameTimeSliceSize)
|
||||
{
|
||||
m_frameTimeAccumulated -= m_frameTimeSliceSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SynchronizeCursors();
|
||||
|
||||
float bgColor[4];
|
||||
@@ -3091,6 +3038,10 @@ GpDisplayDriverTickStatus_t GpDisplayDriver_SDL_GL2::PresentFrameAndSync()
|
||||
|
||||
SDL_GL_SwapWindow(m_window);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
emscripten_sleep(1);
|
||||
#endif
|
||||
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock>::duration syncTime = std::chrono::high_resolution_clock::now().time_since_epoch();
|
||||
const intmax_t periodNum = std::chrono::high_resolution_clock::period::num;
|
||||
const intmax_t periodDen = std::chrono::high_resolution_clock::period::den;
|
||||
@@ -3181,22 +3132,9 @@ GpDisplayDriverTickStatus_t GpDisplayDriver_SDL_GL2::PresentFrameAndSync()
|
||||
}
|
||||
|
||||
m_frameTimeAccumulated += frameTimeStep;
|
||||
while (m_frameTimeAccumulated >= m_frameTimeSliceSize)
|
||||
{
|
||||
GpDisplayDriverTickStatus_t tickStatus = m_properties.m_tickFunc(m_properties.m_tickFuncContext, m_vosFiber);
|
||||
m_frameTimeAccumulated -= m_frameTimeSliceSize;
|
||||
|
||||
if (tickStatus == GpDisplayDriverTickStatuses::kSynchronizing)
|
||||
{
|
||||
m_frameTimeAccumulated = std::chrono::high_resolution_clock::duration::zero();
|
||||
break;
|
||||
}
|
||||
else if (tickStatus != GpDisplayDriverTickStatuses::kOK)
|
||||
return tickStatus;
|
||||
}
|
||||
}
|
||||
|
||||
return GpDisplayDriverTickStatuses::kOK;
|
||||
return false;
|
||||
}
|
||||
|
||||
IGpDisplayDriver *GpDriver_CreateDisplayDriver_SDL_GL2(const GpDisplayDriverProperties &properties)
|
||||
@@ -3205,18 +3143,9 @@ IGpDisplayDriver *GpDriver_CreateDisplayDriver_SDL_GL2(const GpDisplayDriverProp
|
||||
if (!driver)
|
||||
return nullptr;
|
||||
|
||||
new (driver) GpDisplayDriver_SDL_GL2(properties);
|
||||
|
||||
if (!driver->Init())
|
||||
{
|
||||
driver->Shutdown();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return driver;
|
||||
return new (driver) GpDisplayDriver_SDL_GL2(properties);
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
T *GpGLObjectImpl<T>::Create(GpDisplayDriver_SDL_GL2 *driver)
|
||||
{
|
||||
@@ -3237,4 +3166,33 @@ T *GpGLObjectImpl<T>::Create(GpDisplayDriver_SDL_GL2 *driver)
|
||||
return obj;
|
||||
}
|
||||
|
||||
#if GP_ASYNCIFY_PARANOID
|
||||
bool IGpDisplayDriver::Init()
|
||||
{
|
||||
return static_cast<GpDisplayDriver_SDL_GL2*>(this)->Init();
|
||||
}
|
||||
|
||||
void IGpDisplayDriver::ServeTicks(int tickCount)
|
||||
{
|
||||
static_cast<GpDisplayDriver_SDL_GL2*>(this)->ServeTicks(tickCount);
|
||||
}
|
||||
|
||||
void IGpDisplayDriver::Shutdown()
|
||||
{
|
||||
static_cast<GpDisplayDriver_SDL_GL2*>(this)->Shutdown();
|
||||
}
|
||||
|
||||
IGpCursor *IGpDisplayDriver::CreateBWCursor(size_t width, size_t height, const void *pixelData, const void *maskData, size_t hotSpotX, size_t hotSpotY)
|
||||
{
|
||||
return static_cast<GpDisplayDriver_SDL_GL2*>(this)->CreateBWCursor(width, height, pixelData, maskData, hotSpotX, hotSpotY);
|
||||
}
|
||||
|
||||
IGpCursor *IGpDisplayDriver::CreateColorCursor(size_t width, size_t height, const void *pixelDataRGBA, size_t hotSpotX, size_t hotSpotY)
|
||||
{
|
||||
return static_cast<GpDisplayDriver_SDL_GL2*>(this)->CreateColorCursor(width, height, pixelDataRGBA, hotSpotX, hotSpotY);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#pragma pop_macro("LoadCursor")
|
||||
|
@@ -1,70 +0,0 @@
|
||||
#include "GpFiberStarter.h"
|
||||
#include "GpFiber_SDL.h"
|
||||
|
||||
#include "IGpSystemServices.h"
|
||||
#include "IGpThreadEvent.h"
|
||||
|
||||
#include "SDL_thread.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace GpFiberStarter_SDL
|
||||
{
|
||||
struct FiberStartState
|
||||
{
|
||||
GpFiberStarter::ThreadFunc_t m_threadFunc;
|
||||
IGpThreadEvent *m_creatingReturnEvent;
|
||||
IGpThreadEvent *m_creatingWakeEvent;
|
||||
void *m_context;
|
||||
};
|
||||
|
||||
static int SDLCALL FiberStartRoutine(void *lpThreadParameter)
|
||||
{
|
||||
const FiberStartState *tss = static_cast<const FiberStartState*>(lpThreadParameter);
|
||||
|
||||
GpFiberStarter::ThreadFunc_t threadFunc = tss->m_threadFunc;
|
||||
IGpThreadEvent *creatingReturnEvent = tss->m_creatingReturnEvent;
|
||||
IGpThreadEvent *wakeEvent = tss->m_creatingWakeEvent;
|
||||
void *context = tss->m_context;
|
||||
creatingReturnEvent->Signal();
|
||||
|
||||
wakeEvent->Wait();
|
||||
|
||||
threadFunc(context);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
IGpFiber *GpFiberStarter::StartFiber(IGpSystemServices *systemServices, ThreadFunc_t threadFunc, void *context, IGpFiber *creatingFiber)
|
||||
{
|
||||
IGpThreadEvent *returnEvent = systemServices->CreateThreadEvent(true, false);
|
||||
if (!returnEvent)
|
||||
return nullptr;
|
||||
|
||||
IGpThreadEvent *wakeEvent = systemServices->CreateThreadEvent(true, false);
|
||||
if (!wakeEvent)
|
||||
{
|
||||
returnEvent->Destroy();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GpFiberStarter_SDL::FiberStartState startState;
|
||||
startState.m_context = context;
|
||||
startState.m_creatingReturnEvent = returnEvent;
|
||||
startState.m_creatingWakeEvent = wakeEvent;
|
||||
startState.m_threadFunc = threadFunc;
|
||||
|
||||
SDL_Thread *thread = SDL_CreateThread(GpFiberStarter_SDL::FiberStartRoutine, "Fiber", &startState);
|
||||
if (!thread)
|
||||
{
|
||||
returnEvent->Destroy();
|
||||
wakeEvent->Destroy();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
returnEvent->Wait();
|
||||
returnEvent->Destroy();
|
||||
|
||||
return new GpFiber_SDL(thread, wakeEvent);
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
#include "GpFiber_SDL.h"
|
||||
#include "IGpThreadEvent.h"
|
||||
|
||||
GpFiber_SDL::GpFiber_SDL(SDL_Thread *thread, IGpThreadEvent *threadEvent)
|
||||
: m_event(threadEvent)
|
||||
, m_thread(thread)
|
||||
{
|
||||
}
|
||||
|
||||
GpFiber_SDL::~GpFiber_SDL()
|
||||
{
|
||||
m_event->Destroy();
|
||||
}
|
||||
|
||||
void GpFiber_SDL::YieldTo(IGpFiber *toFiber)
|
||||
{
|
||||
static_cast<GpFiber_SDL*>(toFiber)->m_event->Signal();
|
||||
m_event->Wait();
|
||||
}
|
||||
|
||||
void GpFiber_SDL::YieldToTerminal(IGpFiber *toFiber)
|
||||
{
|
||||
static_cast<GpFiber_SDL*>(toFiber)->m_event->Signal();
|
||||
}
|
||||
|
||||
void GpFiber_SDL::Destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "IGpFiber.h"
|
||||
#include "SDL_thread.h"
|
||||
|
||||
struct IGpThreadEvent;
|
||||
|
||||
class GpFiber_SDL final : public IGpFiber
|
||||
{
|
||||
public:
|
||||
explicit GpFiber_SDL(SDL_Thread *thread, IGpThreadEvent *threadEvent);
|
||||
~GpFiber_SDL();
|
||||
|
||||
void YieldTo(IGpFiber *fromFiber) override;
|
||||
void YieldToTerminal(IGpFiber *fromFiber) override;
|
||||
void Destroy() override;
|
||||
|
||||
private:
|
||||
static int SDLCALL InternalThreadFunction(void *data);
|
||||
|
||||
bool m_isDestroying;
|
||||
IGpThreadEvent *m_event;
|
||||
SDL_Thread *m_thread;
|
||||
};
|
@@ -1,6 +1,5 @@
|
||||
#include "GpInputDriver_SDL_Gamepad.h"
|
||||
#include "GpVOSEvent.h"
|
||||
#include "GpWindows.h"
|
||||
#include "IGpVOSEventQueue.h"
|
||||
|
||||
#include "SDL_events.h"
|
||||
@@ -178,8 +177,6 @@ void GpInputDriverSDLGamepad::HandleDeviceRemoved(SDL_JoystickID joystickID)
|
||||
{
|
||||
m_playerButtonDown[playerNum][button] = false;
|
||||
|
||||
|
||||
|
||||
GpVOSEvent evt;
|
||||
evt.m_eventType = GpVOSEventTypes::kKeyboardInput;
|
||||
evt.m_event.m_keyboardInputEvent.m_eventType = GpKeyboardInputEventTypes::kUp;
|
||||
@@ -198,7 +195,7 @@ void GpInputDriverSDLGamepad::ProcessSDLEvent(const SDL_Event &msg)
|
||||
IGpVOSEventQueue *evtQueue = m_properties.m_eventQueue;
|
||||
|
||||
switch (msg.type)
|
||||
{
|
||||
{
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
{
|
||||
const SDL_ControllerAxisEvent &axisMsg = msg.caxis;
|
||||
@@ -232,8 +229,8 @@ void GpInputDriverSDLGamepad::ProcessSDLEvent(const SDL_Event &msg)
|
||||
|
||||
m_pendingEvents.push_back(evt);
|
||||
}
|
||||
break;
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
break;
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
{
|
||||
const bool isDown = (msg.type == SDL_CONTROLLERBUTTONDOWN);
|
||||
@@ -288,22 +285,22 @@ void GpInputDriverSDLGamepad::ProcessSDLEvent(const SDL_Event &msg)
|
||||
|
||||
m_pendingEvents.push_back(evt);
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
HandleDeviceAdded(msg.cdevice.which);
|
||||
break;
|
||||
break;
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
HandleDeviceRemoved(msg.cdevice.which);
|
||||
break;
|
||||
break;
|
||||
case SDL_CONTROLLERDEVICEREMAPPED:
|
||||
// Not really sure what to do here, just re-add it
|
||||
HandleDeviceRemoved(msg.cdevice.which);
|
||||
HandleDeviceAdded(msg.cdevice.which);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IGpInputDriver *GpDriver_CreateInputDriver_SDL2_Gamepad(const GpInputDriverProperties &properties)
|
||||
{
|
||||
return GpInputDriverSDLGamepad::Create(properties);
|
||||
return GpInputDriverSDLGamepad::Create(properties);
|
||||
}
|
||||
|
@@ -1,11 +1,10 @@
|
||||
#include "SDL.h"
|
||||
|
||||
#include "GpMain.h"
|
||||
#include "GpAllocator_C.h"
|
||||
#include "GpAudioDriverFactory.h"
|
||||
#include "GpDisplayDriverFactory.h"
|
||||
#include "GpGlobalConfig.h"
|
||||
#include "GpFiber_Win32.h"
|
||||
#include "GpFiber_SDL.h"
|
||||
#include "GpFileSystem_Win32.h"
|
||||
#include "GpLogDriver_Win32.h"
|
||||
#include "GpFontHandlerFactory.h"
|
||||
@@ -38,6 +37,12 @@ IGpInputDriver *GpDriver_CreateInputDriver_SDL2_Gamepad(const GpInputDriverPrope
|
||||
|
||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
||||
{
|
||||
IGpAllocator *alloc = GpAllocator_C::GetInstance();
|
||||
|
||||
GpFileSystem_Win32 *fs = GpFileSystem_Win32::CreateInstance(alloc);
|
||||
if (!fs)
|
||||
return -1;
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0)
|
||||
return -1;
|
||||
|
||||
@@ -58,9 +63,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
IGpLogDriver *logger = GpLogDriver_Win32::GetInstance();
|
||||
GpDriverCollection *drivers = GpAppInterface_Get()->PL_GetDriverCollection();
|
||||
|
||||
drivers->SetDriver<GpDriverIDs::kFileSystem>(GpFileSystem_Win32::GetInstance());
|
||||
drivers->SetDriver<GpDriverIDs::kFileSystem>(fs);
|
||||
drivers->SetDriver<GpDriverIDs::kSystemServices>(GpSystemServices_Win32::GetInstance());
|
||||
drivers->SetDriver<GpDriverIDs::kLog>(GpLogDriver_Win32::GetInstance());
|
||||
drivers->SetDriver<GpDriverIDs::kAlloc>(GpAllocator_C::GetInstance());
|
||||
|
||||
g_gpWindowsGlobals.m_hInstance = hInstance;
|
||||
g_gpWindowsGlobals.m_hPrevInstance = hPrevInstance;
|
||||
@@ -88,6 +94,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
g_gpGlobalConfig.m_osGlobals = &g_gpWindowsGlobals;
|
||||
g_gpGlobalConfig.m_logger = logger;
|
||||
g_gpGlobalConfig.m_systemServices = GpSystemServices_Win32::GetInstance();
|
||||
g_gpGlobalConfig.m_allocator = alloc;
|
||||
|
||||
GpDisplayDriverFactory::RegisterDisplayDriverFactory(EGpDisplayDriverType_SDL_GL2, GpDriver_CreateDisplayDriver_SDL_GL2);
|
||||
GpAudioDriverFactory::RegisterAudioDriverFactory(EGpAudioDriverType_SDL2, GpDriver_CreateAudioDriver_SDL);
|
||||
@@ -104,5 +111,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
|
||||
LocalFree(cmdLineArgs);
|
||||
|
||||
fs->Destroy();
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
|
5
AerofoilSDL/GpSDL.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#ifdef __CYGWIN__
|
||||
#define GP_SDL_DIRECTORY_PREFIX "SDL2/"
|
||||
#else
|
||||
#define GP_SDL_DIRECTORY_PREFIX
|
||||
#endif
|
@@ -11,6 +11,6 @@
|
||||
"}\n"
|
||||
|
||||
namespace GpBinarizedShaders
|
||||
{
|
||||
{
|
||||
const char *g_copyQuadP_GL2 = GP_GL_SHADER_CODE_MEDIUM_PRECISION_PREFIX GP_GL_SHADER_CODE_FUNCTIONS_H GP_GL_SHADER_CODE_COPYQUADP_GLSL;
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@
|
||||
" vec4 resultColor = vec4(SamplePixel(texCoord.xy), 1.0);\n"\
|
||||
" resultColor *= constants_Modulation;\n"\
|
||||
"#ifdef ENABLE_FLICKER\n"\
|
||||
" resultColor = ApplyFlicker(constants_FlickerAxis, texCoord.zw, constants_FlickerStartThreshold, constants_FlickerEndThreshold, resultColor * constants_Modulation);\n"\
|
||||
" resultColor = ApplyFlicker(constants_FlickerAxis, texCoord.zw, constants_FlickerStartThreshold, constants_FlickerEndThreshold, resultColor);\n"\
|
||||
" if (resultColor.a < 1.0)\n"\
|
||||
" discard;\n"\
|
||||
"#endif\n"\
|
||||
|
@@ -18,7 +18,7 @@
|
||||
" vec4 resultColor = vec4(SamplePixel(texCoord.xy), 1.0);\n"\
|
||||
" resultColor *= constants_Modulation;\n"\
|
||||
"#ifdef ENABLE_FLICKER\n"\
|
||||
" resultColor = ApplyFlicker(constants_FlickerAxis, texCoord.zw, constants_FlickerStartThreshold, constants_FlickerEndThreshold, resultColor * constants_Modulation);\n"\
|
||||
" resultColor = ApplyFlicker(constants_FlickerAxis, texCoord.zw, constants_FlickerStartThreshold, constants_FlickerEndThreshold, resultColor);\n"\
|
||||
" if (resultColor.a < 1.0)\n"\
|
||||
" discard;\n"\
|
||||
"#endif\n"\
|
||||
|
@@ -1,7 +1,10 @@
|
||||
#define GP_GL_SHADER_CODE_MEDIUM_PRECISION_PREFIX "precision mediump float;\n"\
|
||||
|
||||
#define GP_GL_SHADER_CODE_HIGH_PRECISION_PREFIX "precision highp float;\n"\
|
||||
|
||||
#ifdef __MACOS__
|
||||
#define GP_GL_SHADER_CODE_MEDIUM_PRECISION_PREFIX ""
|
||||
#define GP_GL_SHADER_CODE_HIGH_PRECISION_PREFIX ""
|
||||
#else
|
||||
#define GP_GL_SHADER_CODE_MEDIUM_PRECISION_PREFIX "precision mediump float;\n"
|
||||
#define GP_GL_SHADER_CODE_HIGH_PRECISION_PREFIX "precision highp float;\n"
|
||||
#endif
|
||||
|
||||
#define GP_GL_SHADER_CODE_FUNCTIONS_H "vec3 pow3(vec3 v, float ex)\n"\
|
||||
"{\n"\
|
||||
@@ -58,7 +61,7 @@
|
||||
"vec4 ApplyDesaturation(float desaturation, vec4 color)\n"\
|
||||
"{\n"\
|
||||
" // This is intentionally done in gamma space\n"\
|
||||
" if (desaturation == 0.0)\n"\
|
||||
" if (desaturation == 0.0 || (color.r == 1.0 && color.g == 1.0 && color.b == 0.0))\n"\
|
||||
" return color;\n"\
|
||||
"\n"\
|
||||
" float grayLevel = dot(color.rgb, vec3(3.0, 6.0, 1.0) / 10.0);\n"\
|
||||
|
3
AerofoilWeb/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
obj
|
||||
res
|
||||
bin
|
4
AerofoilWeb/AerofoilWeb_Combined.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "GpFileSystem_Web.cpp"
|
||||
#include "GpLogDriver_Web.cpp"
|
||||
#include "GpMain_SDL_Web.cpp"
|
||||
#include "GpSystemServices_Web.cpp"
|
15
AerofoilWeb/AerofoilWeb_Resources.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "GpFileSystem_Web_Resources.h"
|
||||
|
||||
namespace GpFileSystem_Web_Resources
|
||||
{
|
||||
namespace ApplicationData
|
||||
{
|
||||
#include "res/ApplicationData.h"
|
||||
}
|
||||
namespace GameData
|
||||
{
|
||||
#include "res/GameData.h"
|
||||
}
|
||||
}
|
11
AerofoilWeb/BuildAerofoilPortable.bat
Normal file
@@ -0,0 +1,11 @@
|
||||
set INPUT_DIR=../AerofoilPortable
|
||||
set OUTPUT_DIR=obj
|
||||
|
||||
rem set DEBUG_LEVEL_FLAGS=-g4 -O0
|
||||
set DEBUG_LEVEL_FLAGS=-O3 -DNDEBUG=1
|
||||
|
||||
set FLAGS=-s USE_SDL=2 -flto -I../GpCommon/ -I../Common/ -s ASYNCIFY %DEBUG_LEVEL_FLAGS% -DGP_DEBUG_CONFIG=0
|
||||
|
||||
emcc -c %INPUT_DIR%/GpAllocator_C.cpp -o %OUTPUT_DIR%/AerofoilPortable_Combined.o %FLAGS%
|
||||
|
||||
pause
|
11
AerofoilWeb/BuildAerofoilSDL.bat
Normal file
@@ -0,0 +1,11 @@
|
||||
set INPUT_DIR=../AerofoilSDL
|
||||
set OUTPUT_DIR=obj
|
||||
|
||||
rem set DEBUG_LEVEL_FLAGS=-g4 -O0
|
||||
set DEBUG_LEVEL_FLAGS=-O3 -DNDEBUG=1
|
||||
|
||||
set FLAGS=-s USE_SDL=2 -flto -I../GpCommon/ -I../PortabilityLayer/ -I../Common/ -s ASYNCIFY %DEBUG_LEVEL_FLAGS% -DGP_DEBUG_CONFIG=0
|
||||
|
||||
emcc -c %INPUT_DIR%/AerofoilSDL_Combined.cpp -o %OUTPUT_DIR%/AerofoilSDL_Combined.o %FLAGS%
|
||||
|
||||
pause
|
11
AerofoilWeb/BuildAerofoilWeb.bat
Normal file
@@ -0,0 +1,11 @@
|
||||
set INPUT_DIR=.
|
||||
set OUTPUT_DIR=obj
|
||||
|
||||
rem set DEBUG_LEVEL_FLAGS=-g4 -O0
|
||||
set DEBUG_LEVEL_FLAGS=-O3 -DNDEBUG=1
|
||||
|
||||
set FLAGS=-s USE_SDL=2 -flto -I../GpCommon/ -I../PortabilityLayer/ -I../Common/ -I../AerofoilPortable/ -I../GpShell/ -s ASYNCIFY %DEBUG_LEVEL_FLAGS% -DGP_DEBUG_CONFIG=0
|
||||
|
||||
emcc -c %INPUT_DIR%/AerofoilWeb_Combined.cpp -o %OUTPUT_DIR%/AerofoilWeb_Combined.o %FLAGS%
|
||||
|
||||
pause
|
11
AerofoilWeb/BuildGpApp.bat
Normal file
@@ -0,0 +1,11 @@
|
||||
set INPUT_DIR=../GpApp
|
||||
set OUTPUT_DIR=obj
|
||||
|
||||
rem set DEBUG_LEVEL_FLAGS=-g4 -O0
|
||||
set DEBUG_LEVEL_FLAGS=-O3 -DNDEBUG=1
|
||||
|
||||
set FLAGS=-s USE_SDL=2 -flto -I../GpCommon/ -I../PortabilityLayer/ -I../Common/ -s ASYNCIFY %DEBUG_LEVEL_FLAGS% -DGP_DEBUG_CONFIG=0
|
||||
|
||||
emcc -c %INPUT_DIR%/GpApp_Combined.cpp -o %OUTPUT_DIR%/GpApp_Combined.o %FLAGS%
|
||||
|
||||
pause
|
11
AerofoilWeb/BuildGpShell.bat
Normal file
@@ -0,0 +1,11 @@
|
||||
set INPUT_DIR=../GpShell
|
||||
set OUTPUT_DIR=obj
|
||||
|
||||
rem set DEBUG_LEVEL_FLAGS=-g4 -O0
|
||||
set DEBUG_LEVEL_FLAGS=-O3 -DNDEBUG=1
|
||||
|
||||
set FLAGS=-s USE_SDL=2 -flto -I../GpCommon/ -I../PortabilityLayer/ -I../Common/ -s ASYNCIFY %DEBUG_LEVEL_FLAGS% -DGP_DEBUG_CONFIG=0
|
||||
|
||||
emcc -c %INPUT_DIR%/GpShell_Combined.cpp -o %OUTPUT_DIR%/GpShell_Combined.o %FLAGS%
|
||||
|
||||
pause
|
11
AerofoilWeb/BuildMacRomanConversion.bat
Normal file
@@ -0,0 +1,11 @@
|
||||
set INPUT_DIR=../MacRomanConversion
|
||||
set OUTPUT_DIR=obj
|
||||
|
||||
rem set DEBUG_LEVEL_FLAGS=-g4 -O0
|
||||
set DEBUG_LEVEL_FLAGS=-O3 -DNDEBUG=1
|
||||
|
||||
set FLAGS=-flto -I../MacRomanConversion/ -s ASYNCIFY %DEBUG_LEVEL_FLAGS%
|
||||
|
||||
emcc -c %INPUT_DIR%/MacRomanConversion.cpp -o %OUTPUT_DIR%/MacRomanConversion.o %FLAGS%
|
||||
|
||||
pause
|
11
AerofoilWeb/BuildPortabilityLayer.bat
Normal file
@@ -0,0 +1,11 @@
|
||||
set INPUT_DIR=../PortabilityLayer
|
||||
set OUTPUT_DIR=obj
|
||||
|
||||
rem set DEBUG_LEVEL_FLAGS=-g4 -O0
|
||||
set DEBUG_LEVEL_FLAGS=-O3 -DNDEBUG=1
|
||||
|
||||
set FLAGS=-s USE_ZLIB=1 -flto -I../GpCommon/ -I../Common/ -I../PortabilityLayer/ -I../rapidjson/include/ -I../MacRomanConversion/ -I../stb/ -s ASYNCIFY %DEBUG_LEVEL_FLAGS% -DGP_DEBUG_CONFIG=0 -Wno-tautological-constant-out-of-range-compare
|
||||
|
||||
emcc -c %INPUT_DIR%/PortabilityLayer_Combined.cpp -o %OUTPUT_DIR%/PortabilityLayer_Combined.o %FLAGS%
|
||||
|
||||
pause
|
10
AerofoilWeb/BuildResources.bat
Normal file
@@ -0,0 +1,10 @@
|
||||
..\x64\Release\bin2h.exe ..\Packaged res\ApplicationData.h
|
||||
..\x64\Release\bin2h.exe ..\Packaged\Houses res\GameData.h
|
||||
|
||||
set INPUT_DIR=.
|
||||
set OUTPUT_DIR=obj
|
||||
set FLAGS=-flto -O3 -DGP_DEBUG_CONFIG=0
|
||||
|
||||
emcc -c %INPUT_DIR%/AerofoilWeb_Resources.cpp -o %OUTPUT_DIR%/AerofoilWeb_Resources.o %FLAGS%
|
||||
|
||||
pause
|
1
AerofoilWeb/FileSaverDotJS
Submodule
867
AerofoilWeb/GpFileSystem_Web.cpp
Normal file
@@ -0,0 +1,867 @@
|
||||
#define _LARGEFILE64_SOURCE
|
||||
#include "GpFileSystem_Web.h"
|
||||
#include "GpIOStream.h"
|
||||
#include "IGpDirectoryCursor.h"
|
||||
#include "IGpSystemServices.h"
|
||||
#include "IGpMutex.h"
|
||||
#include "VirtualDirectory.h"
|
||||
|
||||
#include "PLDrivers.h"
|
||||
|
||||
#include "SDL2/SDL.h"
|
||||
#include "SDL2/SDL_rwops.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <emscripten.h>
|
||||
|
||||
#include "UTF8.h"
|
||||
|
||||
#if defined(__CYGWIN__) || defined(__EMSCRIPTEN__)
|
||||
typedef off_t off64_t;
|
||||
#define fstat64 fstat
|
||||
#define fseek64 fseek
|
||||
#define ftruncate64 ftruncate
|
||||
#define stat64 stat
|
||||
#endif
|
||||
|
||||
EM_JS(void, FlushFileSystem, (), {
|
||||
Asyncify.handleSleep(wakeUp => {
|
||||
FS.syncfs(false, function (err) {
|
||||
assert(!err);
|
||||
wakeUp();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
EM_JS(void, DownloadAndDeleteFile, (const char *fileNamePtr, const char *prettyNamePtr), {
|
||||
var fileName = UTF8ToString(fileNamePtr);
|
||||
var prettyName = UTF8ToString(prettyNamePtr);
|
||||
console.log("Flush download of file " + fileName + " as " + prettyName);
|
||||
|
||||
var mimeType = "application/octet-stream";
|
||||
if (prettyName.endsWith(".bin"))
|
||||
mimeType = "application/macbinary";
|
||||
else if (prettyName.endsWith(".gpf"))
|
||||
mimeType = "application/zip";
|
||||
|
||||
var byteArray = FS.readFile(fileName, { encoding: "binary" });
|
||||
var blob = new Blob([byteArray], { type: mimeType });
|
||||
saveAs(blob, prettyName);
|
||||
});
|
||||
|
||||
|
||||
class GpDirectoryCursor_Web final : public IGpDirectoryCursor
|
||||
{
|
||||
public:
|
||||
explicit GpDirectoryCursor_Web(DIR *dir, const std::string &prefix);
|
||||
~GpDirectoryCursor_Web();
|
||||
|
||||
bool GetNext(const char *&outFileName) override;
|
||||
void Destroy() override;
|
||||
|
||||
private:
|
||||
DIR *m_dir;
|
||||
std::string m_prefix;
|
||||
std::string m_decodedFileName;
|
||||
};
|
||||
|
||||
GpDirectoryCursor_Web::GpDirectoryCursor_Web(DIR *dir, const std::string &prefix)
|
||||
: m_dir(dir)
|
||||
, m_prefix(prefix)
|
||||
{
|
||||
}
|
||||
|
||||
GpDirectoryCursor_Web::~GpDirectoryCursor_Web()
|
||||
{
|
||||
closedir(m_dir);
|
||||
}
|
||||
|
||||
bool GpDirectoryCursor_Web::GetNext(const char *&outFileName)
|
||||
{
|
||||
const size_t prefixLength = m_prefix.size();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
struct dirent *dir = readdir(m_dir);
|
||||
if (!dir)
|
||||
return false;
|
||||
|
||||
const char *fname = dir->d_name;
|
||||
|
||||
const size_t fnameLen = strlen(fname);
|
||||
if (fnameLen > prefixLength && (prefixLength == 0 || !memcmp(&m_prefix[0], fname, prefixLength)))
|
||||
{
|
||||
const char *encodedResult = fname + prefixLength;
|
||||
|
||||
m_decodedFileName.clear();
|
||||
for (size_t i = 0; encodedResult[i] != 0; i++)
|
||||
{
|
||||
char c = encodedResult[i];
|
||||
if (c == '%')
|
||||
{
|
||||
char highNibble = encodedResult[i + 1];
|
||||
if ((highNibble >= '0' && highNibble <= '9') || (highNibble >= 'a' && highNibble <= 'f') || (highNibble >= 'A' && highNibble <= 'F'))
|
||||
{
|
||||
char lowNibble = encodedResult[i + 2];
|
||||
|
||||
if ((lowNibble >= '0' && lowNibble <= '9') || (lowNibble >= 'a' && lowNibble <= 'f') || (lowNibble >= 'A' && lowNibble <= 'F'))
|
||||
{
|
||||
bool failedNibble = false;
|
||||
char nibbles[2] = { highNibble, lowNibble };
|
||||
int decNibbles[2];
|
||||
for (int ni = 0; ni < 2; ni++)
|
||||
{
|
||||
char nc = nibbles[ni];
|
||||
if (nc >= '0' && nc <= '9')
|
||||
decNibbles[ni] = nc - '0';
|
||||
else if (nc >= 'a' && nc <= 'f')
|
||||
decNibbles[ni] = 0xa + (nc - 'a');
|
||||
else if (nc >= 'A' && nc <= 'F')
|
||||
decNibbles[ni] = 0xa + (nc - 'A');
|
||||
else
|
||||
failedNibble = true;
|
||||
}
|
||||
|
||||
if (!failedNibble)
|
||||
{
|
||||
c = static_cast<char>((decNibbles[0] << 4) + decNibbles[1]);
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_decodedFileName += c;
|
||||
}
|
||||
|
||||
outFileName = m_decodedFileName.c_str();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GpDirectoryCursor_Web::Destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
class GpFileStream_Web_StaticMemFile final : public GpIOStream
|
||||
{
|
||||
public:
|
||||
GpFileStream_Web_StaticMemFile(const unsigned char *bytes, size_t size);
|
||||
~GpFileStream_Web_StaticMemFile();
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
const unsigned char *m_bytes;
|
||||
size_t m_offset;
|
||||
size_t m_size;
|
||||
};
|
||||
|
||||
GpFileStream_Web_StaticMemFile::GpFileStream_Web_StaticMemFile(const unsigned char *bytes, size_t size)
|
||||
: m_bytes(bytes)
|
||||
, m_size(size)
|
||||
, m_offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
GpFileStream_Web_StaticMemFile::~GpFileStream_Web_StaticMemFile()
|
||||
{
|
||||
}
|
||||
|
||||
size_t GpFileStream_Web_StaticMemFile::Read(void *bytesOut, size_t size)
|
||||
{
|
||||
size_t available = m_size - m_offset;
|
||||
size = std::min(size, available);
|
||||
|
||||
memcpy(bytesOut, m_bytes + m_offset, size);
|
||||
m_offset += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t GpFileStream_Web_StaticMemFile::Write(const void *bytes, size_t size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool GpFileStream_Web_StaticMemFile::IsSeekable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GpFileStream_Web_StaticMemFile::IsReadOnly() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GpFileStream_Web_StaticMemFile::IsWriteOnly() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GpFileStream_Web_StaticMemFile::SeekStart(GpUFilePos_t loc)
|
||||
{
|
||||
if (loc > m_size)
|
||||
return false;
|
||||
|
||||
m_offset = static_cast<size_t>(loc);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GpFileStream_Web_StaticMemFile::SeekCurrent(GpFilePos_t loc)
|
||||
{
|
||||
GpFilePos_t minOffset = -static_cast<GpFilePos_t>(m_offset);
|
||||
GpFilePos_t maxOffset = static_cast<GpFilePos_t>(m_size - m_offset);
|
||||
|
||||
if (loc < minOffset || loc > maxOffset)
|
||||
return false;
|
||||
|
||||
m_offset = static_cast<size_t>(static_cast<GpFilePos_t>(m_offset) + loc);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GpFileStream_Web_StaticMemFile::SeekEnd(GpUFilePos_t loc)
|
||||
{
|
||||
if (loc > m_size)
|
||||
return false;
|
||||
|
||||
m_offset = m_size - loc;
|
||||
return true;
|
||||
}
|
||||
|
||||
GpUFilePos_t GpFileStream_Web_StaticMemFile::Size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
GpUFilePos_t GpFileStream_Web_StaticMemFile::Tell() const
|
||||
{
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
void GpFileStream_Web_StaticMemFile::GP_ASYNCIFY_PARANOID_NAMED(Close)()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void GpFileStream_Web_StaticMemFile::Flush()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
class GpFileStream_Web_File final : public GpIOStream
|
||||
{
|
||||
public:
|
||||
GpFileStream_Web_File(FILE *f, const std::string &filePath, const std::string &prettyName, bool readOnly, bool writeOnly, bool synchronizeOnClose, bool isIDB);
|
||||
~GpFileStream_Web_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 GP_ASYNCIFY_PARANOID_NAMED(Close)() override;
|
||||
void Flush() override;
|
||||
|
||||
private:
|
||||
FILE *m_f;
|
||||
std::string m_filePath;
|
||||
std::string m_prettyName;
|
||||
bool m_seekable;
|
||||
bool m_isReadOnly;
|
||||
bool m_isWriteOnly;
|
||||
bool m_synchronizeOnClose;
|
||||
bool m_isIDB;
|
||||
};
|
||||
|
||||
|
||||
GpFileStream_Web_File::GpFileStream_Web_File(FILE *f, const std::string &filePath, const std::string &prettyName, bool readOnly, bool writeOnly, bool synchronizeOnClose, bool isIDB)
|
||||
: m_f(f)
|
||||
, m_isReadOnly(readOnly)
|
||||
, m_isWriteOnly(writeOnly)
|
||||
, m_synchronizeOnClose(synchronizeOnClose)
|
||||
, m_isIDB(isIDB)
|
||||
, m_filePath(filePath)
|
||||
, m_prettyName(prettyName)
|
||||
{
|
||||
m_seekable = (fseek(m_f, 0, SEEK_CUR) == 0);
|
||||
}
|
||||
|
||||
GpFileStream_Web_File::~GpFileStream_Web_File()
|
||||
{
|
||||
fclose(m_f);
|
||||
|
||||
if (m_synchronizeOnClose)
|
||||
{
|
||||
if (m_isIDB)
|
||||
GpFileSystem_Web::MarkFSStateDirty();
|
||||
else
|
||||
GpFileSystem_Web::SyncDownloadFile(m_filePath, m_prettyName);
|
||||
}
|
||||
}
|
||||
|
||||
size_t GpFileStream_Web_File::Read(void *bytesOut, size_t size)
|
||||
{
|
||||
if (m_isWriteOnly)
|
||||
return 0;
|
||||
return fread(bytesOut, 1, size, m_f);
|
||||
}
|
||||
|
||||
size_t GpFileStream_Web_File::Write(const void *bytes, size_t size)
|
||||
{
|
||||
if (m_isReadOnly)
|
||||
return 0;
|
||||
return fwrite(bytes, 1, size, m_f);
|
||||
}
|
||||
|
||||
bool GpFileStream_Web_File::IsSeekable() const
|
||||
{
|
||||
return m_seekable;
|
||||
}
|
||||
|
||||
bool GpFileStream_Web_File::IsReadOnly() const
|
||||
{
|
||||
return m_isReadOnly;
|
||||
}
|
||||
|
||||
bool GpFileStream_Web_File::IsWriteOnly() const
|
||||
{
|
||||
return m_isWriteOnly;
|
||||
}
|
||||
|
||||
bool GpFileStream_Web_File::SeekStart(GpUFilePos_t loc)
|
||||
{
|
||||
if (!m_seekable)
|
||||
return false;
|
||||
|
||||
fflush(m_f);
|
||||
return fseek64(m_f, static_cast<off64_t>(loc), SEEK_SET) >= 0;
|
||||
}
|
||||
|
||||
bool GpFileStream_Web_File::SeekCurrent(GpFilePos_t loc)
|
||||
{
|
||||
if (!m_seekable)
|
||||
return false;
|
||||
|
||||
fflush(m_f);
|
||||
return fseek64(m_f, static_cast<off64_t>(loc), SEEK_CUR) >= 0;
|
||||
}
|
||||
|
||||
bool GpFileStream_Web_File::SeekEnd(GpUFilePos_t loc)
|
||||
{
|
||||
if (!m_seekable)
|
||||
return false;
|
||||
|
||||
fflush(m_f);
|
||||
return fseek64(m_f, -static_cast<off64_t>(loc), SEEK_END) >= 0;
|
||||
}
|
||||
|
||||
GpUFilePos_t GpFileStream_Web_File::Size() const
|
||||
{
|
||||
fflush(m_f);
|
||||
|
||||
struct stat64 s;
|
||||
if (fstat64(fileno(m_f), &s) < 0)
|
||||
return 0;
|
||||
|
||||
return static_cast<GpUFilePos_t>(s.st_size);
|
||||
}
|
||||
|
||||
GpUFilePos_t GpFileStream_Web_File::Tell() const
|
||||
{
|
||||
return static_cast<GpUFilePos_t>(ftell(m_f));
|
||||
}
|
||||
|
||||
void GpFileStream_Web_File::GP_ASYNCIFY_PARANOID_NAMED(Close)()
|
||||
{
|
||||
this->~GpFileStream_Web_File();
|
||||
free(this);
|
||||
}
|
||||
|
||||
void GpFileStream_Web_File::Flush()
|
||||
{
|
||||
fflush(m_f);
|
||||
}
|
||||
|
||||
|
||||
bool GpFileSystem_Web::ms_fsStateDirty;
|
||||
|
||||
|
||||
bool GpFileSystem_Web::ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, bool trailingSlash, std::string &resolution, bool &outIsIDB)
|
||||
{
|
||||
const char *pathAppend = nullptr;
|
||||
const std::string *rootPath = nullptr;
|
||||
std::string unsanitized;
|
||||
bool isIDB = false;
|
||||
|
||||
switch (virtualDirectory)
|
||||
{
|
||||
case PortabilityLayer::VirtualDirectories::kApplicationData:
|
||||
unsanitized = std::string("Packaged");
|
||||
break;
|
||||
case PortabilityLayer::VirtualDirectories::kGameData:
|
||||
unsanitized = std::string("Packaged/Houses");
|
||||
break;
|
||||
case PortabilityLayer::VirtualDirectories::kFonts:
|
||||
unsanitized = std::string("Resources");
|
||||
break;
|
||||
case PortabilityLayer::VirtualDirectories::kHighScores:
|
||||
pathAppend = "HighScores";
|
||||
rootPath = &m_prefsPath;
|
||||
isIDB = true;
|
||||
break;
|
||||
case PortabilityLayer::VirtualDirectories::kUserData:
|
||||
pathAppend = "Houses";
|
||||
rootPath = &m_prefsPath;
|
||||
isIDB = true;
|
||||
break;
|
||||
case PortabilityLayer::VirtualDirectories::kUserSaves:
|
||||
pathAppend = "SavedGames";
|
||||
rootPath = &m_prefsPath;
|
||||
isIDB = true;
|
||||
break;
|
||||
case PortabilityLayer::VirtualDirectories::kPrefs:
|
||||
pathAppend = "Prefs";
|
||||
rootPath = &m_prefsPath;
|
||||
isIDB = true;
|
||||
break;
|
||||
case PortabilityLayer::VirtualDirectories::kSourceExport:
|
||||
pathAppend = "Export";
|
||||
rootPath = &m_exportPath;
|
||||
isIDB = false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
|
||||
if (pathAppend)
|
||||
{
|
||||
unsanitized = pathAppend;
|
||||
|
||||
for (size_t i = 0; i < numPaths; i++)
|
||||
{
|
||||
unsanitized += "/";
|
||||
unsanitized += paths[i];
|
||||
}
|
||||
|
||||
if (trailingSlash)
|
||||
unsanitized += "/";
|
||||
|
||||
std::string sanitized;
|
||||
for (size_t i = 0; i < unsanitized.size(); i++)
|
||||
{
|
||||
char c = unsanitized[i];
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')
|
||||
sanitized += c;
|
||||
else
|
||||
{
|
||||
const char *nibbles = "0123456789abcdef";
|
||||
char subPath[4];
|
||||
subPath[0] = '%';
|
||||
subPath[1] = nibbles[(c >> 4) & 0xf];
|
||||
subPath[2] = nibbles[c & 0xf];
|
||||
subPath[3] = 0;
|
||||
|
||||
sanitized += subPath;
|
||||
}
|
||||
}
|
||||
|
||||
resolution = (*rootPath) + "/" + sanitized;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string sanitized = m_basePath + unsanitized;
|
||||
|
||||
for (size_t i = 0; i < numPaths; i++)
|
||||
{
|
||||
sanitized += "/";
|
||||
sanitized += paths[i];
|
||||
}
|
||||
|
||||
resolution = sanitized;
|
||||
}
|
||||
|
||||
outIsIDB = isIDB;
|
||||
return true;
|
||||
}
|
||||
|
||||
GpFileSystem_Web::GpFileSystem_Web()
|
||||
: m_delayCallback(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
GpFileSystem_Web::~GpFileSystem_Web()
|
||||
{
|
||||
}
|
||||
|
||||
void GpFileSystem_Web::Init()
|
||||
{
|
||||
m_prefsPath = "/aerofoil";
|
||||
m_exportPath = "/aerofoil_memfs";
|
||||
|
||||
char *baseDir = SDL_GetBasePath();
|
||||
m_basePath = baseDir;
|
||||
SDL_free(baseDir);
|
||||
|
||||
char baseDirSeparator = m_basePath[m_basePath.size() - 1];
|
||||
if (m_basePath.size() >= 4 && m_basePath.substr(m_basePath.size() - 4, 3) == "bin")
|
||||
m_basePath = m_basePath.substr(0, m_basePath.size() - 4) + "lib" + baseDirSeparator + "aerofoil" + baseDirSeparator;
|
||||
}
|
||||
|
||||
bool GpFileSystem_Web::FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path)
|
||||
{
|
||||
if (const GpFileSystem_Web_Resources::FileCatalog *catalog = GetCatalogForVirtualDirectory(virtualDirectory))
|
||||
{
|
||||
for (size_t i = 0; i < catalog->m_numEntries; i++)
|
||||
{
|
||||
const GpFileSystem_Web_Resources::FileCatalogEntry &entry = catalog->m_entries[i];
|
||||
if (!strcmp(path, entry.m_fileName))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string resolvedPath;
|
||||
bool isIDB = false;
|
||||
if (!ResolvePath(virtualDirectory, &path, 1, false, resolvedPath, isIDB))
|
||||
return false;
|
||||
|
||||
struct stat s;
|
||||
return stat(resolvedPath.c_str(), &s) == 0;
|
||||
}
|
||||
|
||||
bool GpFileSystem_Web::FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &exists)
|
||||
{
|
||||
if (const GpFileSystem_Web_Resources::FileCatalog *catalog = GetCatalogForVirtualDirectory(virtualDirectory))
|
||||
{
|
||||
for (size_t i = 0; i < catalog->m_numEntries; i++)
|
||||
{
|
||||
const GpFileSystem_Web_Resources::FileCatalogEntry &entry = catalog->m_entries[i];
|
||||
if (!strcmp(path, entry.m_fileName))
|
||||
{
|
||||
exists = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
exists = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string resolvedPath;
|
||||
bool isIDB = false;
|
||||
if (!ResolvePath(virtualDirectory, &path, 1, false, resolvedPath, isIDB))
|
||||
{
|
||||
if (exists)
|
||||
exists = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
int permissions = access(resolvedPath.c_str(), W_OK | F_OK);
|
||||
exists = ((permissions & F_OK) != 0);
|
||||
return ((permissions & W_OK) != 0);
|
||||
}
|
||||
|
||||
GpIOStream *GpFileSystem_Web::OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* subPaths, size_t numSubPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition)
|
||||
{
|
||||
if (numSubPaths == 1)
|
||||
{
|
||||
if (const GpFileSystem_Web_Resources::FileCatalog *catalog = GetCatalogForVirtualDirectory(virtualDirectory))
|
||||
{
|
||||
for (size_t i = 0; i < catalog->m_numEntries; i++)
|
||||
{
|
||||
const GpFileSystem_Web_Resources::FileCatalogEntry &entry = catalog->m_entries[i];
|
||||
if (!strcmp(subPaths[0], entry.m_fileName))
|
||||
return new GpFileStream_Web_StaticMemFile(entry.m_data, entry.m_size);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const char *mode = nullptr;
|
||||
bool canWrite = false;
|
||||
bool needResetPosition = false;
|
||||
|
||||
switch (createDisposition)
|
||||
{
|
||||
case GpFileCreationDispositions::kCreateOrOverwrite:
|
||||
mode = "wb";
|
||||
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;
|
||||
};
|
||||
|
||||
std::string resolvedPath;
|
||||
bool isIDB = false;
|
||||
if (!ResolvePath(virtualDirectory, subPaths, numSubPaths, false, resolvedPath, isIDB))
|
||||
return nullptr;
|
||||
|
||||
void *objStorage = malloc(sizeof(GpFileStream_Web_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);
|
||||
|
||||
if (createDisposition == GpFileCreationDispositions::kOverwriteExisting)
|
||||
{
|
||||
if (ftruncate64(fileno(f), 0) < 0)
|
||||
{
|
||||
free(objStorage);
|
||||
fclose(f);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::string prettyName;
|
||||
if (numSubPaths > 0)
|
||||
prettyName = subPaths[numSubPaths - 1];
|
||||
|
||||
return new (objStorage) GpFileStream_Web_File(f, resolvedPath, prettyName, !writeAccess, false, writeAccess, isIDB);
|
||||
}
|
||||
|
||||
bool GpFileSystem_Web::DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed)
|
||||
{
|
||||
if (const GpFileSystem_Web_Resources::FileCatalog *catalog = GetCatalogForVirtualDirectory(virtualDirectory))
|
||||
return false;
|
||||
|
||||
std::string resolvedPath;
|
||||
bool isIDB = false;
|
||||
if (!ResolvePath(virtualDirectory, &path, 1, false, resolvedPath, isIDB))
|
||||
{
|
||||
existed = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (unlink(resolvedPath.c_str()) < 0)
|
||||
{
|
||||
existed = (errno != ENOENT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isIDB)
|
||||
MarkFSStateDirty();
|
||||
|
||||
existed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GpFileSystem_Web::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_Web::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_Web::SetDelayCallback(DelayCallback_t delayCallback)
|
||||
{
|
||||
m_delayCallback = delayCallback;
|
||||
}
|
||||
|
||||
GpFileSystem_Web *GpFileSystem_Web::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;
|
||||
}
|
||||
|
||||
|
||||
IGpDirectoryCursor *GpFileSystem_Web::ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths)
|
||||
{
|
||||
if (const GpFileSystem_Web_Resources::FileCatalog *catalog = GetCatalogForVirtualDirectory(virtualDirectory))
|
||||
return ScanCatalog(*catalog);
|
||||
|
||||
std::string resolvedPrefix;
|
||||
bool isIDB = false;
|
||||
if (!ResolvePath(virtualDirectory, paths, numPaths, true, resolvedPrefix, isIDB))
|
||||
return nullptr;
|
||||
|
||||
std::string trimmedPrefix = resolvedPrefix.substr(m_prefsPath.size() + 1);
|
||||
|
||||
DIR *d = opendir(m_prefsPath.c_str());
|
||||
if (!d)
|
||||
return nullptr;
|
||||
|
||||
return new GpDirectoryCursor_Web(d, trimmedPrefix);
|
||||
}
|
||||
|
||||
const GpFileSystem_Web_Resources::FileCatalog *GpFileSystem_Web::GetCatalogForVirtualDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory)
|
||||
{
|
||||
if (virtualDirectory == PortabilityLayer::VirtualDirectories::kApplicationData)
|
||||
return &GpFileSystem_Web_Resources::ApplicationData::GetCatalog();
|
||||
|
||||
if (virtualDirectory == PortabilityLayer::VirtualDirectories::kGameData)
|
||||
return &GpFileSystem_Web_Resources::GameData::GetCatalog();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IGpDirectoryCursor *GpFileSystem_Web::ScanCatalog(const GpFileSystem_Web_Resources::FileCatalog &catalog)
|
||||
{
|
||||
std::vector<std::string> paths;
|
||||
for (size_t i = 0; i < catalog.m_numEntries; i++)
|
||||
paths.push_back(std::string(catalog.m_entries[i].m_fileName));
|
||||
|
||||
return new GpDirectoryCursor_StringList(paths);
|
||||
}
|
||||
|
||||
void GpFileSystem_Web::MarkFSStateDirty()
|
||||
{
|
||||
ms_fsStateDirty = true;
|
||||
}
|
||||
|
||||
void GpFileSystem_Web::SyncDownloadFile(const std::string &filePath, const std::string &prettyName)
|
||||
{
|
||||
DownloadAndDeleteFile(filePath.c_str(), prettyName.c_str());
|
||||
}
|
||||
|
||||
void GpFileSystem_Web::FlushFS()
|
||||
{
|
||||
if (ms_fsStateDirty)
|
||||
{
|
||||
ms_fsStateDirty = false;
|
||||
FlushFileSystem();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if GP_ASYNCIFY_PARANOID
|
||||
void GpIOStream::Close()
|
||||
{
|
||||
this->GP_ASYNCIFY_PARANOID_NAMED(Close)();
|
||||
GpFileSystem_Web::FlushFS();
|
||||
}
|
||||
|
||||
bool IGpFileSystem::DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed)
|
||||
{
|
||||
bool deleted = static_cast<GpFileSystem_Web*>(this)->DeleteFile(virtualDirectory, path, existed);
|
||||
if (deleted)
|
||||
GpFileSystem_Web::FlushFS();
|
||||
|
||||
return deleted;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
GpFileSystem_Web GpFileSystem_Web::ms_instance;
|
68
AerofoilWeb/GpFileSystem_Web.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include "IGpFileSystem.h"
|
||||
#include "GpFileSystem_Web_Resources.h"
|
||||
|
||||
#include "GpCoreDefs.h"
|
||||
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
|
||||
struct IGpMutex;
|
||||
|
||||
class GpFileSystem_Web final : public IGpFileSystem
|
||||
{
|
||||
public:
|
||||
GpFileSystem_Web();
|
||||
~GpFileSystem_Web();
|
||||
|
||||
void Init();
|
||||
|
||||
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) GP_ASYNCIFY_PARANOID_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;
|
||||
|
||||
static void MarkFSStateDirty();
|
||||
static void SyncDownloadFile(const std::string &filePath, const std::string &prettyName);
|
||||
static void FlushFS();
|
||||
|
||||
static GpFileSystem_Web *GetInstance();
|
||||
|
||||
private:
|
||||
struct ScanDirectoryNestedContext
|
||||
{
|
||||
GpFileSystem_Web *m_this;
|
||||
|
||||
IGpDirectoryCursor *m_returnValue;
|
||||
PortabilityLayer::VirtualDirectory_t m_virtualDirectory;
|
||||
char const *const *m_paths;
|
||||
size_t m_numPaths;
|
||||
};
|
||||
|
||||
static void ScanDirectoryNestedThunk(void *context);
|
||||
IGpDirectoryCursor *ScanDirectoryNestedInternal(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths);
|
||||
|
||||
IGpDirectoryCursor *ScanDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths);
|
||||
|
||||
static const GpFileSystem_Web_Resources::FileCatalog *GetCatalogForVirtualDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory);
|
||||
|
||||
static IGpDirectoryCursor *ScanCatalog(const GpFileSystem_Web_Resources::FileCatalog &catalog);
|
||||
|
||||
bool ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, bool trailingSlash, std::string &resolution, bool &outIsIDB);
|
||||
|
||||
DelayCallback_t m_delayCallback;
|
||||
|
||||
std::string m_prefsPath;
|
||||
std::string m_basePath;
|
||||
std::string m_exportPath;
|
||||
static bool ms_fsStateDirty;
|
||||
|
||||
static GpFileSystem_Web ms_instance;
|
||||
};
|