mirror of
https://github.com/Kapendev/parin.git
synced 2025-04-26 21:19:56 +03:00
Core is now a package.
This commit is contained in:
parent
7bb8fb64e8
commit
f0524a7105
25 changed files with 79 additions and 3530 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -14,4 +14,5 @@ popka-test-*
|
|||
*.o
|
||||
*.obj
|
||||
*.lst
|
||||
libpopka*
|
||||
lib*
|
||||
dub.selections.json
|
||||
|
|
|
@ -70,12 +70,6 @@ For more info about exporting to web, read [this](#web-support).
|
|||
For an initial understanding, the [examples](examples) folder and the [engine.d](source/popka/game/engine.d) file can be a good starting point.
|
||||
You can also read the [TOUR.md](TOUR.md) file for a more in-depth overview of the engine's functionalities.
|
||||
|
||||
## Project Layout
|
||||
|
||||
* [core](source/popka/core): A standard library designed specifically for game development.
|
||||
* [vendor](source/popka/vendor): A collection of third-party libraries.
|
||||
* [game](source/popka/game): A set of tools for creating 2D games.
|
||||
|
||||
## Attributes and BetterC Support
|
||||
|
||||
This project offers support for some attributes (`@safe`, `@nogc`, `nothrow`) and aims for good compatibility with BetterC.
|
||||
|
@ -97,7 +91,7 @@ Additionally, it provides helper functions to reduce some boilerplate code.
|
|||
All the helper functions are inside the [raylibpp.d](source/popka/vendor/ray/raylibpp.d) file.
|
||||
|
||||
```d
|
||||
import popka.vendor.ray;
|
||||
import popka.ray;
|
||||
|
||||
bool rayLoop() {
|
||||
BeginDrawing();
|
||||
|
|
3
TODO.md
3
TODO.md
|
@ -2,7 +2,8 @@
|
|||
|
||||
## NOW
|
||||
|
||||
Cleaning done.
|
||||
It's time to clean popka...
|
||||
I do this because I am thinking of taking part in a game jam.
|
||||
|
||||
## IDEAS
|
||||
|
||||
|
|
3
dub.json
3
dub.json
|
@ -6,6 +6,9 @@
|
|||
"description": "A lightweight and beginner-friendly 2D game engine for the D programming language.",
|
||||
"license": "MIT",
|
||||
"name": "popka",
|
||||
"dependencies": {
|
||||
"joka": "*"
|
||||
},
|
||||
"configurations": [
|
||||
{
|
||||
"name": "pdefault",
|
||||
|
|
|
@ -25,57 +25,58 @@ enum webDir = buildPath(".", "web");
|
|||
|
||||
|
||||
enum defaultDUBContent = `{
|
||||
"name" : "game",
|
||||
"description" : "A game made with Popka.",
|
||||
"authors" : ["Name"],
|
||||
"copyright" : "Copyright © 2024, Name",
|
||||
"license" : "proprietary",
|
||||
"dependencies": {
|
||||
"popka": "*"
|
||||
},
|
||||
"configurations": [
|
||||
{
|
||||
"name": "linux",
|
||||
"targetType": "executable",
|
||||
"platforms": ["linux"],
|
||||
"dflags": ["-i"],
|
||||
"lflags": ["-L.", "-rpath=$$ORIGIN"],
|
||||
"libs": [
|
||||
"raylib",
|
||||
"GL",
|
||||
"m",
|
||||
"pthread",
|
||||
"dl",
|
||||
"rt",
|
||||
"X11"
|
||||
]
|
||||
"name" : "game",
|
||||
"description" : "A game made with Popka.",
|
||||
"authors" : ["Name"],
|
||||
"copyright" : "Copyright © 2024, Name",
|
||||
"license" : "proprietary",
|
||||
"dependencies": {
|
||||
"joka": "*",
|
||||
"popka": "*"
|
||||
},
|
||||
{
|
||||
"name": "windows",
|
||||
"targetType": "executable",
|
||||
"platforms": ["windows"],
|
||||
"dflags": ["-i"],
|
||||
"libs": [
|
||||
"raylib"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "osx",
|
||||
"targetType": "executable",
|
||||
"platforms": ["osx"],
|
||||
"dflags": ["-i"],
|
||||
"lflags": ["-L.", "-rpath", "@executable_path/"],
|
||||
"libs": [
|
||||
"raylib.500"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "web",
|
||||
"targetType": "staticLibrary",
|
||||
"targetName": "webgame",
|
||||
"dflags": ["-mtriple=wasm32-unknown-unknown-wasm", "-checkaction=halt", "-betterC", "--release", "-i"]
|
||||
}
|
||||
]
|
||||
"configurations": [
|
||||
{
|
||||
"name": "linux",
|
||||
"targetType": "executable",
|
||||
"platforms": ["linux"],
|
||||
"dflags": ["-i"],
|
||||
"lflags": ["-L.", "-rpath=$$ORIGIN"],
|
||||
"libs": [
|
||||
"raylib",
|
||||
"GL",
|
||||
"m",
|
||||
"pthread",
|
||||
"dl",
|
||||
"rt",
|
||||
"X11"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows",
|
||||
"targetType": "executable",
|
||||
"platforms": ["windows"],
|
||||
"dflags": ["-i"],
|
||||
"libs": [
|
||||
"raylib"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "osx",
|
||||
"targetType": "executable",
|
||||
"platforms": ["osx"],
|
||||
"dflags": ["-i"],
|
||||
"lflags": ["-L.", "-rpath", "@executable_path/"],
|
||||
"libs": [
|
||||
"raylib.500"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "web",
|
||||
"targetType": "staticLibrary",
|
||||
"targetName": "webgame",
|
||||
"dflags": ["-mtriple=wasm32-unknown-unknown-wasm", "-checkaction=halt", "-betterC", "--release", "-i"]
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
|
@ -1,832 +0,0 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/// The `ascii` module provides functions designed to assist with ascii strings.
|
||||
module popka.core.ascii;
|
||||
|
||||
import popka.core.containers;
|
||||
import popka.core.traits;
|
||||
import popka.core.types;
|
||||
|
||||
public import popka.core.faults;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
enum digitChars = "0123456789";
|
||||
enum upperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
enum lowerChars = "abcdefghijklmnopqrstuvwxyz";
|
||||
enum alphaChars = upperChars ~ lowerChars;
|
||||
enum spaceChars = " \t\v\r\n\f";
|
||||
|
||||
version (Windows) {
|
||||
enum pathSep = '\\';
|
||||
enum otherPathSep = '/';
|
||||
} else {
|
||||
enum pathSep = '/';
|
||||
enum otherPathSep = '\\';
|
||||
}
|
||||
|
||||
struct ToStrOptions {
|
||||
ubyte doublePrecision = 2;
|
||||
}
|
||||
|
||||
bool isDigit(char c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
bool isDigit(IStr str) {
|
||||
foreach (c; str) {
|
||||
if (!isDigit(c)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isUpper(char c) {
|
||||
return c >= 'A' && c <= 'Z';
|
||||
}
|
||||
|
||||
bool isUpper(IStr str) {
|
||||
foreach (c; str) {
|
||||
if (!isUpper(c)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isLower(char c) {
|
||||
return c >= 'a' && c <= 'z';
|
||||
}
|
||||
|
||||
bool isLower(IStr str) {
|
||||
foreach (c; str) {
|
||||
if (!isLower(c)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isAlpha(char c) {
|
||||
return isLower(c) || isUpper(c);
|
||||
}
|
||||
|
||||
bool isAlpha(IStr str) {
|
||||
foreach (c; str) {
|
||||
if (!isAlpha(c)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isSpace(char c) {
|
||||
foreach (sc; spaceChars) {
|
||||
if (c == sc) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isSpace(IStr str) {
|
||||
foreach (c; str) {
|
||||
if (!isSpace(c)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isCStr(IStr str) {
|
||||
return str.length != 0 && str[$ - 1] == '\0';
|
||||
}
|
||||
|
||||
char toUpper(char c) {
|
||||
return isLower(c) ? cast(char) (c - 32) : c;
|
||||
}
|
||||
|
||||
void toUpper(Str str) {
|
||||
foreach (ref c; str) {
|
||||
c = toUpper(c);
|
||||
}
|
||||
}
|
||||
|
||||
char toLower(char c) {
|
||||
return isUpper(c) ? cast(char) (c + 32) : c;
|
||||
}
|
||||
|
||||
void toLower(Str str) {
|
||||
foreach (ref c; str) {
|
||||
c = toLower(c);
|
||||
}
|
||||
}
|
||||
|
||||
@trusted
|
||||
Sz length(ICStr str) {
|
||||
Sz result = 0;
|
||||
while (str[result] != '\0') {
|
||||
result += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool equals(IStr str, IStr other) {
|
||||
return str == other;
|
||||
}
|
||||
|
||||
bool equals(IStr str, char other) {
|
||||
return equals(str, charToStr(other));
|
||||
}
|
||||
|
||||
bool equalsNoCase(IStr str, IStr other) {
|
||||
if (str.length != other.length) return false;
|
||||
foreach (i; 0 .. str.length) {
|
||||
if (toUpper(str[i]) != toUpper(other[i])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool equalsNoCase(IStr str, char other) {
|
||||
return equalsNoCase(str, charToStr(other));
|
||||
}
|
||||
|
||||
bool startsWith(IStr str, IStr start) {
|
||||
if (str.length < start.length) return false;
|
||||
return str[0 .. start.length] == start;
|
||||
}
|
||||
|
||||
bool startsWith(IStr str, char start) {
|
||||
return startsWith(str, charToStr(start));
|
||||
}
|
||||
|
||||
bool endsWith(IStr str, IStr end) {
|
||||
if (str.length < end.length) return false;
|
||||
return str[$ - end.length .. $] == end;
|
||||
}
|
||||
|
||||
bool endsWith(IStr str, char end) {
|
||||
return endsWith(str, charToStr(end));
|
||||
}
|
||||
|
||||
int count(IStr str, IStr item) {
|
||||
int result = 0;
|
||||
if (str.length < item.length || item.length == 0) return result;
|
||||
foreach (i; 0 .. str.length - item.length) {
|
||||
if (str[i .. i + item.length] == item) {
|
||||
result += 1;
|
||||
i += item.length - 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int count(IStr str, char item) {
|
||||
return count(str, charToStr(item));
|
||||
}
|
||||
|
||||
int findStart(IStr str, IStr item) {
|
||||
if (str.length < item.length || item.length == 0) return -1;
|
||||
foreach (i; 0 .. str.length - item.length + 1) {
|
||||
if (str[i .. i + item.length] == item) return cast(int) i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int findStart(IStr str, char item) {
|
||||
return findStart(str, charToStr(item));
|
||||
}
|
||||
|
||||
int findEnd(IStr str, IStr item) {
|
||||
if (str.length < item.length || item.length == 0) return -1;
|
||||
foreach_reverse (i; 0 .. str.length - item.length + 1) {
|
||||
if (str[i .. i + item.length] == item) return cast(int) i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int findEnd(IStr str, char item) {
|
||||
return findEnd(str, charToStr(item));
|
||||
}
|
||||
|
||||
IStr trimStart(IStr str) {
|
||||
IStr result = str;
|
||||
while (result.length > 0) {
|
||||
if (isSpace(result[0])) result = result[1 .. $];
|
||||
else break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
IStr trimEnd(IStr str) {
|
||||
IStr result = str;
|
||||
while (result.length > 0) {
|
||||
if (isSpace(result[$ - 1])) result = result[0 .. $ - 1];
|
||||
else break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
IStr trim(IStr str) {
|
||||
return str.trimStart().trimEnd();
|
||||
}
|
||||
|
||||
IStr removePrefix(IStr str, IStr prefix) {
|
||||
if (str.startsWith(prefix)) {
|
||||
return str[prefix.length .. $];
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
IStr removeSuffix(IStr str, IStr suffix) {
|
||||
if (str.endsWith(suffix)) {
|
||||
return str[0 .. $ - suffix.length];
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
IStr advance(IStr str, Sz amount) {
|
||||
if (str.length < amount) {
|
||||
return str[$ .. $];
|
||||
} else {
|
||||
return str[amount .. $];
|
||||
}
|
||||
}
|
||||
|
||||
void copyChars(Str str, IStr source, Sz startIndex = 0) {
|
||||
if (str.length < source.length) {
|
||||
assert(0, "Destination string `{}` must be at least as long as the source string `{}`.".format(str, source));
|
||||
}
|
||||
foreach (i, c; source) {
|
||||
str[startIndex + i] = c;
|
||||
}
|
||||
}
|
||||
|
||||
void copy(ref Str str, IStr source, Sz startIndex = 0) {
|
||||
copyChars(str, source, startIndex);
|
||||
str = str[0 .. startIndex + source.length];
|
||||
}
|
||||
|
||||
IStr pathDir(IStr path) {
|
||||
auto end = findEnd(path, pathSep);
|
||||
if (end == -1) {
|
||||
return ".";
|
||||
} else {
|
||||
return path[0 .. end];
|
||||
}
|
||||
}
|
||||
|
||||
IStr pathConcat(IStr[] args...) {
|
||||
static char[1024][4] buffers = void;
|
||||
static byte bufferIndex = 0;
|
||||
|
||||
if (args.length == 0) {
|
||||
return ".";
|
||||
}
|
||||
|
||||
bufferIndex = (bufferIndex + 1) % buffers.length;
|
||||
|
||||
auto result = buffers[bufferIndex][];
|
||||
auto length = 0;
|
||||
foreach (i, arg; args) {
|
||||
result.copyChars(arg, length);
|
||||
length += arg.length;
|
||||
if (i != args.length - 1) {
|
||||
result.copyChars(charToStr(pathSep), length);
|
||||
length += 1;
|
||||
}
|
||||
}
|
||||
result = result[0 .. length];
|
||||
return result;
|
||||
}
|
||||
|
||||
IStr skipValue(ref inout(char)[] str, IStr separator) {
|
||||
if (str.length < separator.length || separator.length == 0) {
|
||||
str = str[$ .. $];
|
||||
return "";
|
||||
}
|
||||
foreach (i; 0 .. str.length - separator.length) {
|
||||
if (str[i .. i + separator.length] == separator) {
|
||||
auto line = str[0 .. i];
|
||||
str = str[i + separator.length .. $];
|
||||
return line;
|
||||
}
|
||||
}
|
||||
auto line = str[0 .. $];
|
||||
if (str[$ - separator.length .. $] == separator) {
|
||||
line = str[0 .. $ - 1];
|
||||
}
|
||||
str = str[$ .. $];
|
||||
return line;
|
||||
}
|
||||
|
||||
IStr skipValue(ref inout(char)[] str, char separator) {
|
||||
return skipValue(str, charToStr(separator));
|
||||
}
|
||||
|
||||
IStr skipLine(ref inout(char)[] str) {
|
||||
return skipValue(str, '\n');
|
||||
}
|
||||
|
||||
IStr boolToStr(bool value) {
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
IStr charToStr(char value) {
|
||||
static char[1] buffer = void;
|
||||
|
||||
auto result = buffer[];
|
||||
result[0] = value;
|
||||
result = result[0 .. 1];
|
||||
return result;
|
||||
}
|
||||
|
||||
IStr unsignedToStr(ulong value) {
|
||||
static char[64] buffer = void;
|
||||
|
||||
auto result = buffer[];
|
||||
if (value == 0) {
|
||||
result[0] = '0';
|
||||
result = result[0 .. 1];
|
||||
} else {
|
||||
auto digitCount = 0;
|
||||
for (auto temp = value; temp != 0; temp /= 10) {
|
||||
result[$ - 1 - digitCount] = (temp % 10) + '0';
|
||||
digitCount += 1;
|
||||
}
|
||||
result = result[$ - digitCount .. $];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
IStr signedToStr(long value) {
|
||||
static char[64] buffer = void;
|
||||
|
||||
auto result = buffer[];
|
||||
if (value < 0) {
|
||||
auto temp = unsignedToStr(-value);
|
||||
result[0] = '-';
|
||||
result.copy(temp, 1);
|
||||
} else {
|
||||
auto temp = unsignedToStr(value);
|
||||
result.copy(temp, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
IStr doubleToStr(double value, ulong precision = 2) {
|
||||
static char[64] buffer = void;
|
||||
|
||||
if (precision == 0) {
|
||||
return signedToStr(cast(long) value);
|
||||
}
|
||||
|
||||
auto result = buffer[];
|
||||
auto cleanNumber = value;
|
||||
auto rightDigitCount = 0;
|
||||
while (cleanNumber != cast(double) (cast(long) cleanNumber)) {
|
||||
rightDigitCount += 1;
|
||||
cleanNumber *= 10;
|
||||
}
|
||||
|
||||
// Add extra zeros at the end if needed.
|
||||
// I do this because it makes it easier to remove the zeros later.
|
||||
if (precision > rightDigitCount) {
|
||||
foreach (j; 0 .. precision - rightDigitCount) {
|
||||
rightDigitCount += 1;
|
||||
cleanNumber *= 10;
|
||||
}
|
||||
}
|
||||
|
||||
// Digits go in the buffer from right to left.
|
||||
auto cleanNumberStr = signedToStr(cast(long) cleanNumber);
|
||||
auto i = result.length;
|
||||
// Check two cases: 0.NN, N.NN
|
||||
if (cast(long) value == 0) {
|
||||
i -= cleanNumberStr.length;
|
||||
result.copyChars(cleanNumberStr, i);
|
||||
foreach (j; 0 .. rightDigitCount - cleanNumberStr.length) {
|
||||
i -= 1;
|
||||
result[i] = '0';
|
||||
}
|
||||
i -= 2;
|
||||
result.copyChars("0.", i);
|
||||
} else {
|
||||
i -= rightDigitCount;
|
||||
result.copyChars(cleanNumberStr[$ - rightDigitCount .. $], i);
|
||||
i -= 1;
|
||||
result[i] = '.';
|
||||
i -= cleanNumberStr.length - rightDigitCount;
|
||||
result.copyChars(cleanNumberStr[0 .. $ - rightDigitCount], i);
|
||||
}
|
||||
// Remove extra zeros at the end if needed.
|
||||
if (precision < rightDigitCount) {
|
||||
result = result[0 .. $ - rightDigitCount + precision];
|
||||
}
|
||||
return result[i .. $];
|
||||
}
|
||||
|
||||
@trusted
|
||||
IStr cStrToStr(ICStr value) {
|
||||
return value[0 .. value.length];
|
||||
}
|
||||
|
||||
IStr enumToStr(T)(T value) {
|
||||
switch (value) {
|
||||
static foreach (member; __traits(allMembers, T)) {
|
||||
mixin("case T." ~ member ~ ": return member;");
|
||||
}
|
||||
default: assert(0, "WTF!");
|
||||
}
|
||||
}
|
||||
|
||||
IStr toStr(T)(T value, ToStrOptions options = ToStrOptions()) {
|
||||
static if (isCharType!T) {
|
||||
return charToStr(value);
|
||||
} else static if (isBoolType!T) {
|
||||
return boolToStr(value);
|
||||
} else static if (isUnsignedType!T) {
|
||||
return unsignedToStr(value);
|
||||
} else static if (isSignedType!T) {
|
||||
return signedToStr(value);
|
||||
} else static if (isFloatingType!T) {
|
||||
return doubleToStr(value, options.doublePrecision);
|
||||
} else static if (isStrType!T) {
|
||||
return value;
|
||||
} else static if (isCStrType!T) {
|
||||
return cStrToStr(value);
|
||||
} else static if (isEnumType!T) {
|
||||
return enumToStr(value);
|
||||
} else static if (__traits(hasMember, T, "toStr")) {
|
||||
return value.toStr();
|
||||
} else {
|
||||
static assert(0, funcImplementationErrorMessage!(T, "toStr"));
|
||||
}
|
||||
}
|
||||
|
||||
Result!bool toBool(IStr str) {
|
||||
if (str == "false") {
|
||||
return Result!bool(false);
|
||||
} else if (str == "true") {
|
||||
return Result!bool(true);
|
||||
} else {
|
||||
return Result!bool(Fault.invalid);
|
||||
}
|
||||
}
|
||||
|
||||
Result!ulong toUnsigned(IStr str) {
|
||||
if (str.length == 0 || str.length >= 18) {
|
||||
return Result!ulong(Fault.invalid);
|
||||
} else {
|
||||
if (str.length == 1 && str[0] == '+') {
|
||||
return Result!ulong(Fault.invalid);
|
||||
}
|
||||
ulong value = 0;
|
||||
ulong level = 1;
|
||||
foreach_reverse (i, c; str[(str[0] == '+' ? 1 : 0) .. $]) {
|
||||
if (isDigit(c)) {
|
||||
value += (c - '0') * level;
|
||||
level *= 10;
|
||||
} else {
|
||||
return Result!ulong(Fault.invalid);
|
||||
}
|
||||
}
|
||||
return Result!ulong(value);
|
||||
}
|
||||
}
|
||||
|
||||
Result!ulong toUnsigned(char c) {
|
||||
if (isDigit(c)) {
|
||||
return Result!ulong(c - '0');
|
||||
} else {
|
||||
return Result!ulong(Fault.invalid);
|
||||
}
|
||||
}
|
||||
|
||||
Result!long toSigned(IStr str) {
|
||||
if (str.length == 0 || str.length >= 18) {
|
||||
return Result!long(Fault.invalid);
|
||||
} else {
|
||||
auto temp = toUnsigned(str[(str[0] == '-' ? 1 : 0) .. $]);
|
||||
return Result!long(str[0] == '-' ? -temp.value : temp.value, temp.fault);
|
||||
}
|
||||
}
|
||||
|
||||
Result!long toSigned(char c) {
|
||||
if (isDigit(c)) {
|
||||
return Result!long(c - '0');
|
||||
} else {
|
||||
return Result!long(Fault.invalid);
|
||||
}
|
||||
}
|
||||
|
||||
Result!double toDouble(IStr str) {
|
||||
auto dotIndex = findStart(str, '.');
|
||||
if (dotIndex == -1) {
|
||||
auto temp = toSigned(str);
|
||||
return Result!double(temp.value, temp.fault);
|
||||
} else {
|
||||
auto left = toSigned(str[0 .. dotIndex]);
|
||||
auto right = toSigned(str[dotIndex + 1 .. $]);
|
||||
if (left.isNone || right.isNone) {
|
||||
return Result!double(Fault.invalid);
|
||||
} else if (str[dotIndex + 1] == '-' || str[dotIndex + 1] == '+') {
|
||||
return Result!double(Fault.invalid);
|
||||
} else {
|
||||
auto sign = str[0] == '-' ? -1 : 1;
|
||||
auto level = 10;
|
||||
foreach (i; 1 .. str[dotIndex + 1 .. $].length) {
|
||||
level *= 10;
|
||||
}
|
||||
return Result!double(left.value + sign * (right.value / (cast(double) level)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result!double toDouble(char c) {
|
||||
if (isDigit(c)) {
|
||||
return Result!double(c - '0');
|
||||
} else {
|
||||
return Result!double(Fault.invalid);
|
||||
}
|
||||
}
|
||||
|
||||
Result!T toEnum(T)(IStr str) {
|
||||
switch (str) {
|
||||
static foreach (member; __traits(allMembers, T)) {
|
||||
mixin("case " ~ member.stringof ~ ": return Result!T(T." ~ member ~ ");");
|
||||
}
|
||||
default: return Result!T(Fault.invalid);
|
||||
}
|
||||
}
|
||||
|
||||
@trusted
|
||||
Result!ICStr toCStr(IStr str) {
|
||||
static char[1024] buffer = void;
|
||||
|
||||
if (buffer.length < str.length) {
|
||||
return Result!ICStr(Fault.invalid);
|
||||
} else {
|
||||
auto value = buffer[];
|
||||
value.copyChars(str);
|
||||
value[str.length] = '\0';
|
||||
return Result!ICStr(value.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Check if the args count is the same with the `{}` count and also add extra stuff.
|
||||
IStr format(A...)(IStr formatStr, A args) {
|
||||
static char[1024][8] buffers = void;
|
||||
static byte bufferIndex = 0;
|
||||
|
||||
bufferIndex = (bufferIndex + 1) % buffers.length;
|
||||
|
||||
auto result = buffers[bufferIndex][];
|
||||
auto resultIndex = 0;
|
||||
auto formatStrIndex = 0;
|
||||
auto argIndex = 0;
|
||||
|
||||
while (formatStrIndex < formatStr.length) {
|
||||
auto c1 = formatStr[formatStrIndex];
|
||||
auto c2 = formatStrIndex + 1 >= formatStr.length ? '+' : formatStr[formatStrIndex + 1];
|
||||
if (c1 == '{' && c2 == '}' && argIndex < args.length) {
|
||||
static foreach (i, arg; args) {
|
||||
if (i == argIndex) {
|
||||
auto temp = toStr(arg);
|
||||
foreach (i, c; temp) {
|
||||
result[resultIndex + i] = c;
|
||||
}
|
||||
resultIndex += temp.length;
|
||||
formatStrIndex += 2;
|
||||
argIndex += 1;
|
||||
goto loopExit;
|
||||
}
|
||||
}
|
||||
loopExit:
|
||||
} else {
|
||||
result[resultIndex] = c1;
|
||||
resultIndex += 1;
|
||||
formatStrIndex += 1;
|
||||
}
|
||||
}
|
||||
result = result[0 .. resultIndex];
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Check if the args count is the same with the `{}` count and also add extra stuff.
|
||||
void formatl(A...)(ref LStr text, IStr formatStr, A args) {
|
||||
text.clear();
|
||||
|
||||
auto formatStrIndex = 0;
|
||||
auto argIndex = 0;
|
||||
|
||||
while (formatStrIndex < formatStr.length) {
|
||||
auto c1 = formatStr[formatStrIndex];
|
||||
auto c2 = formatStrIndex + 1 >= formatStr.length ? '+' : formatStr[formatStrIndex + 1];
|
||||
if (c1 == '{' && c2 == '}' && argIndex < args.length) {
|
||||
static foreach (i, arg; args) {
|
||||
if (i == argIndex) {
|
||||
auto temp = toStr(arg);
|
||||
foreach (i, c; temp) {
|
||||
text.append(c);
|
||||
}
|
||||
formatStrIndex += 2;
|
||||
argIndex += 1;
|
||||
goto loopExit;
|
||||
}
|
||||
}
|
||||
loopExit:
|
||||
} else {
|
||||
text.append(c1);
|
||||
formatStrIndex += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function test.
|
||||
@trusted
|
||||
unittest {
|
||||
enum TestEnum {
|
||||
one,
|
||||
two,
|
||||
}
|
||||
|
||||
char[128] buffer = void;
|
||||
Str str;
|
||||
|
||||
assert(isDigit("0123456789?") == false);
|
||||
assert(isDigit("0123456789") == true);
|
||||
assert(isUpper("hello") == false);
|
||||
assert(isUpper("HELLO") == true);
|
||||
assert(isLower("HELLO") == false);
|
||||
assert(isLower("hello") == true);
|
||||
assert(isSpace(" \t\r\n ") == true);
|
||||
assert(isCStr("hello") == false);
|
||||
assert(isCStr("hello\0") == true);
|
||||
|
||||
str = buffer[];
|
||||
str.copy("Hello");
|
||||
assert(str == "Hello");
|
||||
str.toUpper();
|
||||
assert(str == "HELLO");
|
||||
str.toLower();
|
||||
assert(str == "hello");
|
||||
|
||||
str = buffer[];
|
||||
str.copy("Hello\0");
|
||||
assert(isCStr(str) == true);
|
||||
assert(str.ptr.length + 1 == str.length);
|
||||
|
||||
str = buffer[];
|
||||
str.copy("Hello");
|
||||
assert(str.equals("HELLO") == false);
|
||||
assert(str.equalsNoCase("HELLO") == true);
|
||||
assert(str.startsWith("H") == true);
|
||||
assert(str.startsWith("Hell") == true);
|
||||
assert(str.startsWith("Hello") == true);
|
||||
assert(str.endsWith("o") == true);
|
||||
assert(str.endsWith("ello") == true);
|
||||
assert(str.endsWith("Hello") == true);
|
||||
|
||||
str = buffer[];
|
||||
str.copy("hello hello world.");
|
||||
assert(str.count("hello") == 2);
|
||||
assert(str.findStart("HELLO") == -1);
|
||||
assert(str.findStart("hello") == 0);
|
||||
assert(str.findEnd("HELLO") == -1);
|
||||
assert(str.findEnd("hello") == 6);
|
||||
|
||||
str = buffer[];
|
||||
str.copy(" Hello world. ");
|
||||
assert(str.trimStart() == "Hello world. ");
|
||||
assert(str.trimEnd() == " Hello world.");
|
||||
assert(str.trim() == "Hello world.");
|
||||
assert(str.removePrefix("Hello") == str);
|
||||
assert(str.trim().removePrefix("Hello") == " world.");
|
||||
assert(str.removeSuffix("world.") == str);
|
||||
assert(str.trim().removeSuffix("world.") == "Hello ");
|
||||
assert(str.advance(0) == str);
|
||||
assert(str.advance(1) == str[1 .. $]);
|
||||
assert(str.advance(str.length) == "");
|
||||
assert(str.advance(str.length + 1) == "");
|
||||
assert(pathConcat("one", "two").pathDir() == "one");
|
||||
assert(pathConcat("one").pathDir() == ".");
|
||||
|
||||
str = buffer[];
|
||||
str.copy("one, two ,three,");
|
||||
assert(skipValue(str, ',') == "one");
|
||||
assert(skipValue(str, ',') == " two ");
|
||||
assert(skipValue(str, ',') == "three");
|
||||
assert(skipValue(str, ',') == "");
|
||||
assert(str.length == 0);
|
||||
|
||||
assert(boolToStr(false) == "false");
|
||||
assert(boolToStr(true) == "true");
|
||||
assert(charToStr('L') == "L");
|
||||
|
||||
assert(unsignedToStr(0) == "0");
|
||||
assert(unsignedToStr(69) == "69");
|
||||
assert(signedToStr(0) == "0");
|
||||
assert(signedToStr(69) == "69");
|
||||
assert(signedToStr(-69) == "-69");
|
||||
assert(signedToStr(-69) == "-69");
|
||||
|
||||
assert(doubleToStr(0.00, 0) == "0");
|
||||
assert(doubleToStr(0.00, 1) == "0.0");
|
||||
assert(doubleToStr(0.00, 2) == "0.00");
|
||||
assert(doubleToStr(0.00, 3) == "0.000");
|
||||
assert(doubleToStr(0.60, 1) == "0.6");
|
||||
assert(doubleToStr(0.60, 2) == "0.60");
|
||||
assert(doubleToStr(0.60, 3) == "0.600");
|
||||
assert(doubleToStr(0.09, 1) == "0.0");
|
||||
assert(doubleToStr(0.09, 2) == "0.09");
|
||||
assert(doubleToStr(0.09, 3) == "0.090");
|
||||
assert(doubleToStr(69.0, 1) == "69.0");
|
||||
assert(doubleToStr(69.0, 2) == "69.00");
|
||||
assert(doubleToStr(69.0, 3) == "69.000");
|
||||
|
||||
assert(cStrToStr("Hello\0") == "Hello");
|
||||
|
||||
assert(enumToStr(TestEnum.one) == "one");
|
||||
assert(enumToStr(TestEnum.two) == "two");
|
||||
|
||||
assert(toBool("F").isSome == false);
|
||||
assert(toBool("F").unwrapOr() == false);
|
||||
assert(toBool("T").isSome == false);
|
||||
assert(toBool("T").unwrapOr() == false);
|
||||
assert(toBool("false").isSome == true);
|
||||
assert(toBool("false").unwrapOr() == false);
|
||||
assert(toBool("true").isSome == true);
|
||||
assert(toBool("true").unwrapOr() == true);
|
||||
|
||||
assert(toUnsigned("1_069").isSome == false);
|
||||
assert(toUnsigned("1_069").unwrapOr() == 0);
|
||||
assert(toUnsigned("+1069").isSome == true);
|
||||
assert(toUnsigned("+1069").unwrapOr() == 1069);
|
||||
assert(toUnsigned("1069").isSome == true);
|
||||
assert(toUnsigned("1069").unwrapOr() == 1069);
|
||||
assert(toUnsigned('+').isSome == false);
|
||||
assert(toUnsigned('+').unwrapOr() == 0);
|
||||
assert(toUnsigned('0').isSome == true);
|
||||
assert(toUnsigned('0').unwrapOr() == 0);
|
||||
assert(toUnsigned('9').isSome == true);
|
||||
assert(toUnsigned('9').unwrapOr() == 9);
|
||||
|
||||
assert(toSigned("1_069").isSome == false);
|
||||
assert(toSigned("1_069").unwrapOr() == 0);
|
||||
assert(toSigned("-1069").isSome == true);
|
||||
assert(toSigned("-1069").unwrapOr() == -1069);
|
||||
assert(toSigned("+1069").isSome == true);
|
||||
assert(toSigned("+1069").unwrapOr() == 1069);
|
||||
assert(toSigned("1069").isSome == true);
|
||||
assert(toSigned("1069").unwrapOr() == 1069);
|
||||
assert(toSigned('+').isSome == false);
|
||||
assert(toSigned('+').unwrapOr() == 0);
|
||||
assert(toSigned('0').isSome == true);
|
||||
assert(toSigned('0').unwrapOr() == 0);
|
||||
assert(toSigned('9').isSome == true);
|
||||
assert(toSigned('9').unwrapOr() == 9);
|
||||
|
||||
assert(toDouble("1_069").isSome == false);
|
||||
assert(toDouble("1_069").unwrapOr() == 0);
|
||||
assert(toDouble(".1069").isSome == false);
|
||||
assert(toDouble(".1069").unwrapOr() == 0);
|
||||
assert(toDouble("1069.").isSome == false);
|
||||
assert(toDouble("1069.").unwrapOr() == 0);
|
||||
assert(toDouble(".").isSome == false);
|
||||
assert(toDouble(".").unwrapOr() == 0);
|
||||
assert(toDouble("-1069.-69").isSome == false);
|
||||
assert(toDouble("-1069.-69").unwrapOr() == 0);
|
||||
assert(toDouble("-1069.+69").isSome == false);
|
||||
assert(toDouble("-1069.+69").unwrapOr() == 0);
|
||||
assert(toDouble("-1069").isSome == true);
|
||||
assert(toDouble("-1069").unwrapOr() == -1069);
|
||||
assert(toDouble("+1069").isSome == true);
|
||||
assert(toDouble("+1069").unwrapOr() == 1069);
|
||||
assert(toDouble("1069").isSome == true);
|
||||
assert(toDouble("1069").unwrapOr() == 1069);
|
||||
assert(toDouble("1069.0").isSome == true);
|
||||
assert(toDouble("1069.0").unwrapOr() == 1069);
|
||||
assert(toDouble("-1069.0095").isSome == true);
|
||||
assert(toDouble("-1069.0095").unwrapOr() == -1069.0095);
|
||||
assert(toDouble("+1069.0095").isSome == true);
|
||||
assert(toDouble("+1069.0095").unwrapOr() == 1069.0095);
|
||||
assert(toDouble("1069.0095").isSome == true);
|
||||
assert(toDouble("1069.0095").unwrapOr() == 1069.0095);
|
||||
assert(toDouble("-0.0095").isSome == true);
|
||||
assert(toDouble("-0.0095").unwrapOr() == -0.0095);
|
||||
assert(toDouble("+0.0095").isSome == true);
|
||||
assert(toDouble("+0.0095").unwrapOr() == 0.0095);
|
||||
assert(toDouble("0.0095").isSome == true);
|
||||
assert(toDouble("0.0095").unwrapOr() == 0.0095);
|
||||
assert(toDouble('+').isSome == false);
|
||||
assert(toDouble('+').unwrapOr() == 0);
|
||||
assert(toDouble('0').isSome == true);
|
||||
assert(toDouble('0').unwrapOr() == 0);
|
||||
assert(toDouble('9').isSome == true);
|
||||
assert(toDouble('9').unwrapOr() == 9);
|
||||
|
||||
assert(toEnum!TestEnum("?").isSome == false);
|
||||
assert(toEnum!TestEnum("?").unwrapOr() == TestEnum.one);
|
||||
assert(toEnum!TestEnum("one").isSome == true);
|
||||
assert(toEnum!TestEnum("one").unwrapOr() == TestEnum.one);
|
||||
assert(toEnum!TestEnum("two").isSome == true);
|
||||
assert(toEnum!TestEnum("two").unwrapOr() == TestEnum.two);
|
||||
|
||||
assert(toCStr("Hello").unwrapOr().length == "Hello".length);
|
||||
assert(toCStr("Hello").unwrapOr().cStrToStr() == "Hello");
|
||||
|
||||
// TODO: Write tests for `format` when it is done.
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/// The `colors` module provides color-related types and functions.
|
||||
module popka.core.colors;
|
||||
|
||||
import popka.core.ascii;
|
||||
import popka.core.traits;
|
||||
import popka.core.types;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
enum blank = Color();
|
||||
enum black = Color(0);
|
||||
enum white = Color(255);
|
||||
|
||||
enum red = Color(255, 0, 0);
|
||||
enum green = Color(0, 255, 0);
|
||||
enum blue = Color(0, 0, 255);
|
||||
enum yellow = Color(255, 255, 0);
|
||||
enum magenta = Color(255, 0, 255);
|
||||
enum cyan = Color(0, 255, 255);
|
||||
|
||||
enum gray1 = toRgb(0x202020);
|
||||
enum gray2 = toRgb(0x606060);
|
||||
enum gray3 = toRgb(0x9f9f9f);
|
||||
enum gray4 = toRgb(0xdfdfdf);
|
||||
|
||||
alias gray = gray1;
|
||||
|
||||
struct Color {
|
||||
ubyte r;
|
||||
ubyte g;
|
||||
ubyte b;
|
||||
ubyte a;
|
||||
|
||||
enum length = 4;
|
||||
enum zero = Color(0, 0, 0, 0);
|
||||
enum one = Color(1, 1, 1, 1);
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
pragma(inline, true)
|
||||
this(ubyte r, ubyte g, ubyte b, ubyte a = 255) {
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
pragma(inline, true)
|
||||
this(ubyte r) {
|
||||
this(r, r, r, 255);
|
||||
}
|
||||
|
||||
mixin addRgbaOps!(Color, length);
|
||||
|
||||
Color alpha(ubyte a) {
|
||||
return Color(r, g, b, a);
|
||||
}
|
||||
|
||||
IStr toStr() {
|
||||
return "({} {} {} {})".format(r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
Color toRgb(uint rgb) {
|
||||
return Color(
|
||||
(rgb & 0xFF0000) >> 16,
|
||||
(rgb & 0xFF00) >> 8,
|
||||
(rgb & 0xFF),
|
||||
);
|
||||
}
|
||||
|
||||
Color toRgba(uint rgba) {
|
||||
return Color(
|
||||
(rgba & 0xFF000000) >> 24,
|
||||
(rgba & 0xFF0000) >> 16,
|
||||
(rgba & 0xFF00) >> 8,
|
||||
(rgba & 0xFF),
|
||||
);
|
||||
}
|
||||
|
||||
unittest {
|
||||
assert(toRgb(0xff0000) == red);
|
||||
assert(toRgb(0x00ff00) == green);
|
||||
assert(toRgb(0x0000ff) == blue);
|
||||
|
||||
assert(toRgba(0xff0000ff) == red);
|
||||
assert(toRgba(0x00ff00ff) == green);
|
||||
assert(toRgba(0x0000ffff) == blue);
|
||||
|
||||
assert(black.toStr() == "(0 0 0 255)");
|
||||
assert(black.alpha(69).toStr() == "(0 0 0 69)");
|
||||
}
|
|
@ -1,597 +0,0 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/// The `containers` module provides various data structures.
|
||||
module popka.core.containers;
|
||||
|
||||
import popka.core.ascii;
|
||||
import popka.core.stdc;
|
||||
import popka.core.types;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
enum defaultListCapacity = 64;
|
||||
|
||||
alias LStr = List!char;
|
||||
alias LStr16 = List!wchar;
|
||||
alias LStr32 = List!dchar;
|
||||
|
||||
struct List(T) {
|
||||
T[] items;
|
||||
Sz capacity;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
this(const(T)[] args...) {
|
||||
foreach (arg; args) {
|
||||
append(arg);
|
||||
}
|
||||
}
|
||||
|
||||
this(List!T list) {
|
||||
foreach (item; list.items) {
|
||||
append(item);
|
||||
}
|
||||
}
|
||||
|
||||
this(FlagList!T list) {
|
||||
foreach (item; list.items) {
|
||||
append(item);
|
||||
}
|
||||
}
|
||||
|
||||
T[] opSlice(Sz dimension)(Sz i, Sz j) {
|
||||
return items[i .. j];
|
||||
}
|
||||
|
||||
T[] opIndex() {
|
||||
return items[];
|
||||
}
|
||||
|
||||
// D calls this function when the slice operator is used. Does something but I do not remember what lol.
|
||||
T[] opIndex(T[] slice) {
|
||||
return slice;
|
||||
}
|
||||
|
||||
// D will let you get the pointer of the array item if you return a ref value.
|
||||
ref T opIndex(Sz i) {
|
||||
return items[i];
|
||||
}
|
||||
|
||||
@trusted
|
||||
void opIndexAssign(const(T) rhs, Sz i) {
|
||||
items[i] = cast(T) rhs;
|
||||
}
|
||||
|
||||
@trusted
|
||||
void opIndexOpAssign(IStr op)(const(T) rhs, Sz i) {
|
||||
mixin("items[i] " ~ op ~ "= cast(T) rhs;");
|
||||
}
|
||||
|
||||
bool opEquals(List!T rhs) {
|
||||
return items == rhs.items;
|
||||
}
|
||||
|
||||
@trusted
|
||||
bool opEquals(const(T)[] rhs) {
|
||||
return items == cast(T[]) rhs;
|
||||
}
|
||||
|
||||
Sz opDollar(Sz dimension)() {
|
||||
return items.length;
|
||||
}
|
||||
|
||||
Sz length() {
|
||||
return items.length;
|
||||
}
|
||||
|
||||
@trusted
|
||||
T* ptr() {
|
||||
return items.ptr;
|
||||
}
|
||||
|
||||
@trusted
|
||||
void append(const(T)[] args...) {
|
||||
foreach (arg; args) {
|
||||
Sz newLength = length + 1;
|
||||
if (newLength > capacity) {
|
||||
capacity = findListCapacity(newLength);
|
||||
items = (cast(T*) realloc(items.ptr, capacity * T.sizeof))[0 .. newLength];
|
||||
} else {
|
||||
items = items.ptr[0 .. newLength];
|
||||
}
|
||||
items[$ - 1] = cast(T) arg;
|
||||
}
|
||||
}
|
||||
|
||||
void remove(Sz i) {
|
||||
items[i] = items[$ - 1];
|
||||
items = items[0 .. $ - 1];
|
||||
}
|
||||
|
||||
T pop() {
|
||||
if (length > 0) {
|
||||
T temp = items[$ - 1];
|
||||
remove(length - 1);
|
||||
return temp;
|
||||
} else {
|
||||
return T.init;
|
||||
}
|
||||
}
|
||||
|
||||
@trusted
|
||||
void reserve(Sz capacity) {
|
||||
auto targetCapacity = findListCapacity(capacity);
|
||||
if (targetCapacity > this.capacity) {
|
||||
this.capacity = targetCapacity;
|
||||
items = (cast(T*) realloc(items.ptr, this.capacity * T.sizeof))[0 .. length];
|
||||
}
|
||||
}
|
||||
|
||||
void resize(Sz length) {
|
||||
if (length <= this.length) {
|
||||
items = items[0 .. length];
|
||||
} else {
|
||||
reserve(length);
|
||||
foreach (i; 0 .. length - this.length) {
|
||||
append(T.init);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@trusted
|
||||
void fill(const(T) value) {
|
||||
foreach (ref item; items) {
|
||||
item = cast(T) value;
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
items = items[0 .. 0];
|
||||
}
|
||||
|
||||
@trusted
|
||||
void free() {
|
||||
.free(items.ptr);
|
||||
items = [];
|
||||
capacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct FlagList(T) {
|
||||
List!T data;
|
||||
List!bool flags;
|
||||
Sz hotIndex;
|
||||
Sz openIndex;
|
||||
Sz length;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
this(const(T)[] args...) {
|
||||
foreach (arg; args) {
|
||||
append(arg);
|
||||
}
|
||||
}
|
||||
|
||||
this(List!T list) {
|
||||
foreach (item; list.items) {
|
||||
append(item);
|
||||
}
|
||||
}
|
||||
|
||||
this(FlagList!T list) {
|
||||
data.resize(list.data.length);
|
||||
flags.resize(list.flags.length);
|
||||
foreach (i; 0 .. flags.length) {
|
||||
data[i] = list.data[i];
|
||||
flags[i] = list.flags[i];
|
||||
}
|
||||
hotIndex = list.hotIndex;
|
||||
openIndex = list.openIndex;
|
||||
length = list.length;
|
||||
}
|
||||
|
||||
ref T opIndex(Sz i) {
|
||||
if (!has(i)) {
|
||||
assert(0, "ID `[{}]` does not exist.".format(i));
|
||||
}
|
||||
return data[i];
|
||||
}
|
||||
|
||||
@trusted
|
||||
void opIndexAssign(const(T) rhs, Sz i) {
|
||||
if (!has(i)) {
|
||||
assert(0, "ID `[{}]` does not exist.".format(i));
|
||||
}
|
||||
data[i] = cast(T) rhs;
|
||||
}
|
||||
|
||||
@trusted
|
||||
void opIndexOpAssign(IStr op)(const(T) rhs, Sz i) {
|
||||
if (!has(i)) {
|
||||
assert(0, "ID `[{}]` does not exist.".format(i));
|
||||
}
|
||||
mixin("data[i] ", op, "= cast(T) rhs;");
|
||||
}
|
||||
|
||||
Sz capacity() {
|
||||
return data.capacity;
|
||||
}
|
||||
|
||||
@trusted
|
||||
T* ptr() {
|
||||
return data.ptr;
|
||||
}
|
||||
|
||||
bool has(Sz id) {
|
||||
return id < flags.length && flags[id];
|
||||
}
|
||||
|
||||
@trusted
|
||||
void append(const(T)[] args...) {
|
||||
foreach (arg; args) {
|
||||
if (openIndex == flags.length) {
|
||||
data.append(arg);
|
||||
flags.append(true);
|
||||
hotIndex = openIndex;
|
||||
openIndex = flags.length;
|
||||
length += 1;
|
||||
} else {
|
||||
auto isFull = true;
|
||||
foreach (i; openIndex .. flags.length) {
|
||||
if (!flags[i]) {
|
||||
data[i] = arg;
|
||||
flags[i] = true;
|
||||
hotIndex = i;
|
||||
openIndex = i;
|
||||
isFull = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isFull) {
|
||||
data.append(arg);
|
||||
flags.append(true);
|
||||
hotIndex = flags.length - 1;
|
||||
openIndex = flags.length;
|
||||
}
|
||||
length += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void remove(Sz i) {
|
||||
if (!has(i)) {
|
||||
assert(0, "ID `[{}]` does not exist.".format(i));
|
||||
}
|
||||
flags[i] = false;
|
||||
hotIndex = i;
|
||||
if (i < openIndex) {
|
||||
openIndex = i;
|
||||
}
|
||||
length -= 1;
|
||||
}
|
||||
|
||||
@trusted
|
||||
void fill(const(T) value) {
|
||||
foreach (ref item; items) {
|
||||
item = cast(T) value;
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
data.clear();
|
||||
flags.clear();
|
||||
hotIndex = 0;
|
||||
openIndex = 0;
|
||||
length = 0;
|
||||
}
|
||||
|
||||
void free() {
|
||||
data.free();
|
||||
flags.free();
|
||||
hotIndex = 0;
|
||||
openIndex = 0;
|
||||
length = 0;
|
||||
}
|
||||
|
||||
auto ids() {
|
||||
struct Range {
|
||||
bool[] flags;
|
||||
Sz id;
|
||||
|
||||
bool empty() {
|
||||
return id == flags.length;
|
||||
}
|
||||
|
||||
Sz front() {
|
||||
return id;
|
||||
}
|
||||
|
||||
void popFront() {
|
||||
id += 1;
|
||||
while (id != flags.length && !flags[id]) {
|
||||
id += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sz id = 0;
|
||||
while (id < flags.length && !flags[id]) {
|
||||
id += 1;
|
||||
}
|
||||
return Range(flags.items, id);
|
||||
}
|
||||
|
||||
auto items() {
|
||||
struct Range {
|
||||
T[] data;
|
||||
bool[] flags;
|
||||
Sz id;
|
||||
|
||||
bool empty() {
|
||||
return id == flags.length;
|
||||
}
|
||||
|
||||
ref T front() {
|
||||
return data[id];
|
||||
}
|
||||
|
||||
void popFront() {
|
||||
id += 1;
|
||||
while (id != flags.length && !flags[id]) {
|
||||
id += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sz id = 0;
|
||||
while (id < flags.length && !flags[id]) {
|
||||
id += 1;
|
||||
}
|
||||
return Range(data.items, flags.items, id);
|
||||
}
|
||||
}
|
||||
|
||||
struct Grid(T) {
|
||||
List!T tiles;
|
||||
Sz rowCount;
|
||||
Sz colCount;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
this(Sz rowCount, Sz colCount) {
|
||||
resize(rowCount, colCount);
|
||||
}
|
||||
|
||||
T[] opIndex() {
|
||||
return tiles[];
|
||||
}
|
||||
|
||||
ref T opIndex(Sz row, Sz col) {
|
||||
if (!has(row, col)) {
|
||||
assert(0, "Tile `[{}, {}]` does not exist.".format(row, col));
|
||||
}
|
||||
return tiles[colCount * row + col];
|
||||
}
|
||||
|
||||
void opIndexAssign(T rhs, Sz row, Sz col) {
|
||||
if (!has(row, col)) {
|
||||
assert(0, "Tile `[{}, {}]` does not exist.".format(row, col));
|
||||
}
|
||||
tiles[colCount * row + col] = rhs;
|
||||
}
|
||||
|
||||
void opIndexOpAssign(IStr op)(T rhs, Sz row, Sz col) {
|
||||
if (!has(row, col)) {
|
||||
assert(0, "Tile `[{}, {}]` does not exist.".format(row, col));
|
||||
}
|
||||
mixin("tiles[colCount * row + col] " ~ op ~ "= rhs;");
|
||||
}
|
||||
|
||||
Sz opDollar(Sz dimension)() {
|
||||
static if (dimension == 0) {
|
||||
return rowCount;
|
||||
} else static if (dimension == 1) {
|
||||
return colCount;
|
||||
} else {
|
||||
assert(0, "WTF!");
|
||||
}
|
||||
}
|
||||
|
||||
Sz length() {
|
||||
return tiles.length;
|
||||
}
|
||||
|
||||
Sz capacity() {
|
||||
return tiles.capacity;
|
||||
}
|
||||
|
||||
@trusted
|
||||
T* ptr() {
|
||||
return tiles.ptr;
|
||||
}
|
||||
|
||||
bool has(Sz row, Sz col) {
|
||||
return row < rowCount && col < colCount;
|
||||
}
|
||||
|
||||
void resize(Sz rowCount, Sz colCount) {
|
||||
this.tiles.resize(rowCount * colCount);
|
||||
this.rowCount = rowCount;
|
||||
this.colCount = colCount;
|
||||
}
|
||||
|
||||
void fill(T value) {
|
||||
tiles.fill(value);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
tiles.clear();
|
||||
rowCount = 0;
|
||||
colCount = 0;
|
||||
}
|
||||
|
||||
void free() {
|
||||
tiles.free();
|
||||
rowCount = 0;
|
||||
colCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Sz findListCapacity(Sz length) {
|
||||
Sz result = defaultListCapacity;
|
||||
while (result < length) {
|
||||
result *= 2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Function test.
|
||||
unittest {
|
||||
assert(findListCapacity(0) == defaultListCapacity);
|
||||
assert(findListCapacity(defaultListCapacity) == defaultListCapacity);
|
||||
assert(findListCapacity(defaultListCapacity + 1) == defaultListCapacity * 2);
|
||||
}
|
||||
|
||||
// List test.
|
||||
unittest {
|
||||
LStr text;
|
||||
|
||||
text = LStr();
|
||||
assert(text.length == 0);
|
||||
assert(text.capacity == 0);
|
||||
assert(text.ptr == null);
|
||||
|
||||
text = LStr("abc");
|
||||
assert(text.length == 3);
|
||||
assert(text.capacity == defaultListCapacity);
|
||||
assert(text.ptr != null);
|
||||
text.free();
|
||||
assert(text.length == 0);
|
||||
assert(text.capacity == 0);
|
||||
assert(text.ptr == null);
|
||||
|
||||
text = LStr("Hello world!");
|
||||
assert(text.length == "Hello world!".length);
|
||||
assert(text.capacity == defaultListCapacity);
|
||||
assert(text.ptr != null);
|
||||
assert(text[] == text.items);
|
||||
assert(text[0] == text.items[0]);
|
||||
assert(text[0 .. $] == text.items[0 .. $]);
|
||||
assert(text[0] == 'H');
|
||||
text[0] = 'h';
|
||||
assert(text[0] == 'h');
|
||||
text.append("!!");
|
||||
assert(text == "hello world!!!");
|
||||
assert(text.pop() == '!');
|
||||
assert(text.pop() == '!');
|
||||
assert(text == "hello world!");
|
||||
text.resize(0);
|
||||
assert(text == "");
|
||||
assert(text.length == 0);
|
||||
assert(text.capacity == defaultListCapacity);
|
||||
assert(text.pop() == char.init);
|
||||
text.resize(1);
|
||||
assert(text[0] == char.init);
|
||||
assert(text.length == 1);
|
||||
assert(text.capacity == defaultListCapacity);
|
||||
text.clear();
|
||||
text.reserve(5);
|
||||
assert(text.length == 0);
|
||||
assert(text.capacity == defaultListCapacity);
|
||||
text.reserve(defaultListCapacity + 1);
|
||||
assert(text.length == 0);
|
||||
assert(text.capacity == defaultListCapacity * 2);
|
||||
text.free();
|
||||
}
|
||||
|
||||
// FlagList test.
|
||||
unittest {
|
||||
FlagList!int numbers;
|
||||
|
||||
numbers = FlagList!int();
|
||||
assert(numbers.length == 0);
|
||||
assert(numbers.capacity == 0);
|
||||
assert(numbers.ptr == null);
|
||||
assert(numbers.hotIndex == 0);
|
||||
assert(numbers.openIndex == 0);
|
||||
|
||||
numbers = FlagList!int(1, 2, 3);
|
||||
assert(numbers.length == 3);
|
||||
assert(numbers.capacity == defaultListCapacity);
|
||||
assert(numbers.ptr != null);
|
||||
assert(numbers.hotIndex == 2);
|
||||
assert(numbers.openIndex == 3);
|
||||
assert(numbers[0] == 1);
|
||||
assert(numbers[1] == 2);
|
||||
assert(numbers[2] == 3);
|
||||
assert(numbers.has(0) == true);
|
||||
assert(numbers.has(1) == true);
|
||||
assert(numbers.has(2) == true);
|
||||
assert(numbers.has(3) == false);
|
||||
numbers.remove(1);
|
||||
assert(numbers.has(0) == true);
|
||||
assert(numbers.has(1) == false);
|
||||
assert(numbers.has(2) == true);
|
||||
assert(numbers.has(3) == false);
|
||||
assert(numbers.hotIndex == 1);
|
||||
assert(numbers.openIndex == 1);
|
||||
numbers.append(1);
|
||||
assert(numbers.has(0) == true);
|
||||
assert(numbers.has(1) == true);
|
||||
assert(numbers.has(2) == true);
|
||||
assert(numbers.has(3) == false);
|
||||
assert(numbers.hotIndex == 1);
|
||||
assert(numbers.openIndex == 1);
|
||||
numbers.append(4);
|
||||
assert(numbers.has(0) == true);
|
||||
assert(numbers.has(1) == true);
|
||||
assert(numbers.has(2) == true);
|
||||
assert(numbers.has(3) == true);
|
||||
assert(numbers.hotIndex == 3);
|
||||
assert(numbers.openIndex == 4);
|
||||
numbers.clear();
|
||||
numbers.append(1);
|
||||
assert(numbers.has(0) == true);
|
||||
assert(numbers.has(1) == false);
|
||||
assert(numbers.has(2) == false);
|
||||
assert(numbers.has(3) == false);
|
||||
assert(numbers.hotIndex == 0);
|
||||
assert(numbers.openIndex == 1);
|
||||
numbers.free();
|
||||
assert(numbers.length == 0);
|
||||
assert(numbers.capacity == 0);
|
||||
assert(numbers.ptr == null);
|
||||
assert(numbers.hotIndex == 0);
|
||||
assert(numbers.openIndex == 0);
|
||||
}
|
||||
|
||||
// Grid test
|
||||
unittest {
|
||||
Grid!int numbers;
|
||||
|
||||
numbers = Grid!int();
|
||||
assert(numbers.length == 0);
|
||||
assert(numbers.capacity == 0);
|
||||
assert(numbers.ptr == null);
|
||||
assert(numbers.rowCount == 0);
|
||||
assert(numbers.colCount == 0);
|
||||
|
||||
numbers = Grid!int(8, 8);
|
||||
assert(numbers.length == 8 * 8);
|
||||
assert(numbers.capacity == defaultListCapacity);
|
||||
assert(numbers.ptr != null);
|
||||
assert(numbers.rowCount == 8);
|
||||
assert(numbers.colCount == 8);
|
||||
assert(numbers[0, 0] == 0);
|
||||
assert(numbers[7, 7] == 0);
|
||||
assert(numbers.has(7, 8) == false);
|
||||
assert(numbers.has(8, 7) == false);
|
||||
assert(numbers.has(8, 8) == false);
|
||||
numbers.free();
|
||||
assert(numbers.length == 0);
|
||||
assert(numbers.capacity == 0);
|
||||
assert(numbers.ptr == null);
|
||||
assert(numbers.rowCount == 0);
|
||||
assert(numbers.colCount == 0);
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/// The `faults` module provides a set of codes and data structures for error handling.
|
||||
module popka.core.faults;
|
||||
|
||||
import popka.core.ascii;
|
||||
import popka.core.traits;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
enum Fault : ubyte {
|
||||
none,
|
||||
some,
|
||||
invalid,
|
||||
overflow,
|
||||
cantFind,
|
||||
cantOpen,
|
||||
cantClose,
|
||||
cantRead,
|
||||
cantWrite,
|
||||
}
|
||||
|
||||
struct Result(T) {
|
||||
static if (isNumberType!T) {
|
||||
T value = 0;
|
||||
} else {
|
||||
T value;
|
||||
}
|
||||
Fault fault = Fault.some;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
this(T value) {
|
||||
this.value = value;
|
||||
this.fault = Fault.none;
|
||||
}
|
||||
|
||||
this(Fault fault) {
|
||||
this.fault = fault;
|
||||
}
|
||||
|
||||
this(T value, Fault fault) {
|
||||
if (fault) {
|
||||
this.fault = fault;
|
||||
} else {
|
||||
this.value = value;
|
||||
this.fault = Fault.none;
|
||||
}
|
||||
}
|
||||
|
||||
T unwrap() {
|
||||
if (fault) {
|
||||
assert(0, "Fault `{}` was detected.".format(fault));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
T unwrapOr(T dflt) {
|
||||
if (fault) {
|
||||
return dflt;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
T unwrapOr() {
|
||||
return value;
|
||||
}
|
||||
|
||||
bool isNone() {
|
||||
return fault != 0;
|
||||
}
|
||||
|
||||
bool isSome() {
|
||||
return fault == 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Result test.
|
||||
unittest {
|
||||
assert(Result!int().isNone == true);
|
||||
assert(Result!int().isSome == false);
|
||||
assert(Result!int().unwrapOr() == 0);
|
||||
assert(Result!int(0).isNone == false);
|
||||
assert(Result!int(0).isSome == true);
|
||||
assert(Result!int(0).unwrapOr() == 0);
|
||||
assert(Result!int(69).isNone == false);
|
||||
assert(Result!int(69).isSome == true);
|
||||
assert(Result!int(69).unwrapOr() == 69);
|
||||
assert(Result!int(Fault.none).isNone == false);
|
||||
assert(Result!int(Fault.none).isSome == true);
|
||||
assert(Result!int(Fault.none).unwrapOr() == 0);
|
||||
assert(Result!int(Fault.some).isNone == true);
|
||||
assert(Result!int(Fault.some).isSome == false);
|
||||
assert(Result!int(Fault.some).unwrapOr() == 0);
|
||||
assert(Result!int(69, Fault.none).isNone == false);
|
||||
assert(Result!int(69, Fault.none).isSome == true);
|
||||
assert(Result!int(69, Fault.none).unwrapOr() == 69);
|
||||
assert(Result!int(69, Fault.some).isNone == true);
|
||||
assert(Result!int(69, Fault.some).isSome == false);
|
||||
assert(Result!int(69, Fault.some).unwrapOr() == 0);
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/// The `io` module provides input and output functions such as file reading.
|
||||
module popka.core.io;
|
||||
|
||||
import popka.core.ascii;
|
||||
import popka.core.stdc;
|
||||
import popka.core.traits;
|
||||
import popka.core.types;
|
||||
|
||||
public import popka.core.containers;
|
||||
public import popka.core.faults;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
@trusted
|
||||
void printf(A...)(IStr text, A args) {
|
||||
.fputs(format("{}\0", format(text, args)).ptr, .stdout);
|
||||
}
|
||||
|
||||
@trusted
|
||||
void printfln(A...)(IStr text, A args) {
|
||||
.fputs(format("{}\n\0", format(text, args)).ptr, .stdout);
|
||||
}
|
||||
|
||||
void print(A...)(A args) {
|
||||
static foreach (arg; args) {
|
||||
printf("{}", arg);
|
||||
}
|
||||
}
|
||||
|
||||
void println(A...)(A args) {
|
||||
static foreach (arg; args) {
|
||||
printf("{}", arg);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
@trusted
|
||||
Fault readTextIntoBuffer(IStr path, ref LStr text) {
|
||||
auto file = fopen(toCStr(path).unwrapOr(), "rb");
|
||||
if (file == null) {
|
||||
return Fault.cantOpen;
|
||||
}
|
||||
if (fseek(file, 0, SEEK_END) != 0) {
|
||||
fclose(file);
|
||||
return Fault.cantRead;
|
||||
}
|
||||
|
||||
auto fileSize = ftell(file);
|
||||
if (fileSize == -1) {
|
||||
fclose(file);
|
||||
return Fault.cantRead;
|
||||
}
|
||||
if (fseek(file, 0, SEEK_SET) != 0) {
|
||||
fclose(file);
|
||||
return Fault.cantRead;
|
||||
}
|
||||
|
||||
text.resize(fileSize);
|
||||
fread(text.items.ptr, fileSize, 1, file);
|
||||
if (fclose(file) != 0) {
|
||||
return Fault.cantClose;
|
||||
}
|
||||
return Fault.none;
|
||||
}
|
||||
|
||||
Result!LStr readText(IStr path) {
|
||||
LStr value;
|
||||
return Result!LStr(value, readTextIntoBuffer(path, value));
|
||||
}
|
||||
|
||||
@trusted
|
||||
Fault writeText(IStr path, IStr text) {
|
||||
auto file = fopen(toCStr(path).unwrapOr(), "w");
|
||||
if (file == null) {
|
||||
return Fault.cantOpen;
|
||||
}
|
||||
fwrite(text.ptr, char.sizeof, text.length, file);
|
||||
if (fclose(file) != 0) {
|
||||
return Fault.cantClose;
|
||||
}
|
||||
return Fault.none;
|
||||
}
|
||||
|
||||
// Function test.
|
||||
unittest {
|
||||
assert(readText("").isSome == false);
|
||||
assert(writeText("", "") != Fault.none);
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,14 +0,0 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
module popka.core;
|
||||
|
||||
public import popka.core.ascii;
|
||||
public import popka.core.colors;
|
||||
public import popka.core.containers;
|
||||
public import popka.core.faults;
|
||||
public import popka.core.io;
|
||||
public import popka.core.math;
|
||||
public import popka.core.traits;
|
||||
public import popka.core.types;
|
||||
public import popka.core.unions;
|
|
@ -1,142 +0,0 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/// The `stdc` module provides access to the C standard library.
|
||||
module popka.core.stdc;
|
||||
|
||||
@nogc nothrow extern(C):
|
||||
|
||||
// types.l
|
||||
|
||||
version (WebAssembly) {
|
||||
alias CLong = int;
|
||||
alias CULong = uint;
|
||||
} else {
|
||||
alias CLong = long;
|
||||
alias CULong = ulong;
|
||||
}
|
||||
|
||||
// math.h
|
||||
|
||||
float sqrtf(float x);
|
||||
float sqrt(double x);
|
||||
|
||||
float sinf(float x);
|
||||
float sin(double x);
|
||||
|
||||
float cosf(float x);
|
||||
float cos(double x);
|
||||
|
||||
// stdlib.h
|
||||
|
||||
void* malloc(size_t size);
|
||||
void* realloc(void* ptr, size_t size);
|
||||
void free(void* ptr);
|
||||
|
||||
// stdio.h
|
||||
|
||||
alias FILE = void;
|
||||
|
||||
enum SEEK_SET = 0;
|
||||
enum SEEK_CUR = 1;
|
||||
enum SEEK_END = 2;
|
||||
|
||||
enum STDIN_FILENO = 0;
|
||||
enum STDOUT_FILENO = 1;
|
||||
enum STDERR_FILENO = 2;
|
||||
|
||||
// NOTE: Code from the D standard library.
|
||||
version (CRuntime_Microsoft) {
|
||||
FILE* __acrt_iob_func(int hnd); // VS2015+, reimplemented in msvc.d for VS2013-
|
||||
FILE* stdin()() { return __acrt_iob_func(0); }
|
||||
FILE* stdout()() { return __acrt_iob_func(1); }
|
||||
FILE* stderr()() { return __acrt_iob_func(2); }
|
||||
} else version (CRuntime_Glibc) {
|
||||
extern __gshared FILE* stdin;
|
||||
extern __gshared FILE* stdout;
|
||||
extern __gshared FILE* stderr;
|
||||
} else version (Darwin) {
|
||||
extern __gshared FILE* __stdinp;
|
||||
extern __gshared FILE* __stdoutp;
|
||||
extern __gshared FILE* __stderrp;
|
||||
alias __stdinp stdin;
|
||||
alias __stdoutp stdout;
|
||||
alias __stderrp stderr;
|
||||
} else version (FreeBSD) {
|
||||
extern __gshared FILE* __stdinp;
|
||||
extern __gshared FILE* __stdoutp;
|
||||
extern __gshared FILE* __stderrp;
|
||||
alias __stdinp stdin;
|
||||
alias __stdoutp stdout;
|
||||
alias __stderrp stderr;
|
||||
} else version (NetBSD) {
|
||||
extern __gshared FILE[3] __sF;
|
||||
auto __stdin()() { return &__sF[0]; }
|
||||
auto __stdout()() { return &__sF[1]; }
|
||||
auto __stderr()() { return &__sF[2]; }
|
||||
alias __stdin stdin;
|
||||
alias __stdout stdout;
|
||||
alias __stderr stderr;
|
||||
} else version (OpenBSD) {
|
||||
extern __gshared FILE[3] __sF;
|
||||
auto __stdin()() { return &__sF[0]; }
|
||||
auto __stdout()() { return &__sF[1]; }
|
||||
auto __stderr()() { return &__sF[2]; }
|
||||
alias __stdin stdin;
|
||||
alias __stdout stdout;
|
||||
alias __stderr stderr;
|
||||
} else version (DragonFlyBSD) {
|
||||
extern __gshared FILE* __stdinp;
|
||||
extern __gshared FILE* __stdoutp;
|
||||
extern __gshared FILE* __stderrp;
|
||||
alias __stdinp stdin;
|
||||
alias __stdoutp stdout;
|
||||
alias __stderrp stderr;
|
||||
} else version (Solaris) {
|
||||
extern __gshared FILE[_NFILE] __iob;
|
||||
auto stdin()() { return &__iob[0]; }
|
||||
auto stdout()() { return &__iob[1]; }
|
||||
auto stderr()() { return &__iob[2]; }
|
||||
} else version (CRuntime_Bionic) {
|
||||
extern __gshared FILE[3] __sF;
|
||||
auto stdin()() { return &__sF[0]; }
|
||||
auto stdout()() { return &__sF[1]; }
|
||||
auto stderr()() { return &__sF[2]; }
|
||||
} else version (CRuntime_Musl) {
|
||||
extern __gshared FILE* stdin;
|
||||
extern __gshared FILE* stdout;
|
||||
extern __gshared FILE* stderr;
|
||||
} else version (CRuntime_Newlib) {
|
||||
__gshared struct _reent {
|
||||
int _errno;
|
||||
__sFILE* _stdin;
|
||||
__sFILE* _stdout;
|
||||
__sFILE* _stderr;
|
||||
}
|
||||
_reent* __getreent();
|
||||
pragma(inline, true) {
|
||||
auto stdin()() { return __getreent()._stdin; }
|
||||
auto stdout()() { return __getreent()._stdout; }
|
||||
auto stderr()() { return __getreent()._stderr; }
|
||||
}
|
||||
} else version (CRuntime_UClibc) {
|
||||
extern __gshared FILE* stdin;
|
||||
extern __gshared FILE* stdout;
|
||||
extern __gshared FILE* stderr;
|
||||
} else version (WASI) {
|
||||
extern __gshared FILE* stdin;
|
||||
extern __gshared FILE* stdout;
|
||||
extern __gshared FILE* stderr;
|
||||
} else {
|
||||
extern __gshared FILE* stdin;
|
||||
extern __gshared FILE* stdout;
|
||||
extern __gshared FILE* stderr;
|
||||
}
|
||||
|
||||
FILE* fopen(const(char)* filename, const(char)* mode);
|
||||
CLong ftell(FILE* stream);
|
||||
int fseek(FILE* stream, CLong offset, int origin);
|
||||
size_t fread(void* ptr, size_t size, size_t count, FILE* stream);
|
||||
int fclose(FILE* stream);
|
||||
int fputs(const(char)* str, FILE* stream);
|
||||
size_t fwrite(const(void)* buffer, size_t size, size_t count, FILE* stream);
|
|
@ -1,301 +0,0 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/// The `traits` module provides compile-time functions such as type checking.
|
||||
module popka.core.traits;
|
||||
|
||||
import popka.core.types;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
alias AliasArgs(A...) = A;
|
||||
|
||||
bool isBoolType(T)() {
|
||||
return is(T == bool) ||
|
||||
is(T == const(bool)) ||
|
||||
is(T == immutable(bool));
|
||||
}
|
||||
|
||||
bool isUnsignedType(T)() {
|
||||
return is(T == ubyte) ||
|
||||
is(T == const(ubyte)) ||
|
||||
is(T == immutable(ubyte)) ||
|
||||
is(T == ushort) ||
|
||||
is(T == const(ushort)) ||
|
||||
is(T == immutable(ushort)) ||
|
||||
is(T == uint) ||
|
||||
is(T == const(uint)) ||
|
||||
is(T == immutable(uint)) ||
|
||||
is(T == ulong) ||
|
||||
is(T == const(ulong)) ||
|
||||
is(T == immutable(ulong));
|
||||
}
|
||||
|
||||
bool isSignedType(T)() {
|
||||
return is(T == byte) ||
|
||||
is(T == const(byte)) ||
|
||||
is(T == immutable(byte)) ||
|
||||
is(T == short) ||
|
||||
is(T == const(short)) ||
|
||||
is(T == immutable(short)) ||
|
||||
is(T == int) ||
|
||||
is(T == const(int)) ||
|
||||
is(T == immutable(int)) ||
|
||||
is(T == long) ||
|
||||
is(T == const(long)) ||
|
||||
is(T == immutable(long));
|
||||
}
|
||||
|
||||
bool isIntegerType(T)() {
|
||||
return isUnsignedType!T || isSignedType!T;
|
||||
}
|
||||
|
||||
bool isFloatingType(T)() {
|
||||
return is(T == float) ||
|
||||
is(T == const(float)) ||
|
||||
is(T == immutable(float)) ||
|
||||
is(T == double) ||
|
||||
is(T == const(double)) ||
|
||||
is(T == immutable(double));
|
||||
}
|
||||
|
||||
bool isNumberType(T)() {
|
||||
return isIntegerType!T || isFloatingType!T;
|
||||
}
|
||||
|
||||
bool isCharType(T)() {
|
||||
return is(T == char) ||
|
||||
is(T == const(char)) ||
|
||||
is(T == immutable(char));
|
||||
}
|
||||
|
||||
bool isPrimaryType(T)() {
|
||||
return isBoolType!T ||
|
||||
isUnsignedType!T ||
|
||||
isSignedType!T ||
|
||||
isDoubleType!T ||
|
||||
isCharType!T;
|
||||
}
|
||||
|
||||
bool isArrayType(T)() {
|
||||
return is(T : const(A)[N], A, N);
|
||||
}
|
||||
|
||||
bool isPtrType(T)() {
|
||||
return is(T : const(void)*);
|
||||
}
|
||||
|
||||
bool isSliceType(T)() {
|
||||
return is(T : const(A)[], A);
|
||||
}
|
||||
|
||||
bool isEnumType(T)() {
|
||||
return is(T == enum);
|
||||
}
|
||||
|
||||
bool isStructType(T)() {
|
||||
return is(T == struct);
|
||||
}
|
||||
|
||||
bool isStrType(T)() {
|
||||
return is(T : IStr);
|
||||
}
|
||||
|
||||
bool isCStrType(T)() {
|
||||
return is(T : ICStr);
|
||||
}
|
||||
|
||||
bool hasMember(T, IStr name)() {
|
||||
return __traits(hasMember, T, name);
|
||||
}
|
||||
|
||||
int findInAliasArgs(T, A...)() {
|
||||
int result = -1;
|
||||
static foreach (i, TT; A) {
|
||||
static if (is(T == TT)) {
|
||||
result = i;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool isInAliasArgs(T, A...)() {
|
||||
return findInAliasArgs!(T, A) != -1;
|
||||
}
|
||||
|
||||
IStr funcImplementationErrorMessage(T, IStr func)() {
|
||||
return "Type `" ~ T.stringof ~ "` does not implement the `" ~ func ~ "` function.";
|
||||
}
|
||||
|
||||
IStr toCleanNumber(alias i)() {
|
||||
enum str = i.stringof;
|
||||
static if (str.length >= 3 && (((str[$ - 1] == 'L' || str[$ - 1] == 'l') && (str[$ - 2] == 'U' || str[$ - 2] == 'u')) || ((str[$ - 1] == 'U' || str[$ - 1] == 'u') && (str[$ - 2] == 'L' || str[$ - 2] == 'l')))) {
|
||||
return str[0 .. $ - 2];
|
||||
} else static if (str.length >= 2 && (str[$ - 1] == 'U' || str[$ - 1] == 'u')) {
|
||||
return str[0 .. $ - 1];
|
||||
} else static if (str.length >= 2 && (str[$ - 1] == 'L' || str[$ - 1] == 'l')) {
|
||||
return str[0 .. $ - 1];
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
mixin template addXyzwOps(T, Sz N) {
|
||||
static assert(N >= 1 && N <= 4, "Vector `" ~ T.stringof ~ "` must have a dimension between 1 and 4.");
|
||||
|
||||
pragma(inline, true)
|
||||
T opUnary(IStr op)() {
|
||||
static if (N == 1) {
|
||||
return T(
|
||||
mixin(op, "x"),
|
||||
);
|
||||
} else static if (N == 2) {
|
||||
return T(
|
||||
mixin(op, "x"),
|
||||
mixin(op, "y"),
|
||||
);
|
||||
} else static if (N == 3) {
|
||||
return T(
|
||||
mixin(op, "x"),
|
||||
mixin(op, "y"),
|
||||
mixin(op, "z"),
|
||||
);
|
||||
} else static if (N == 4) {
|
||||
return T(
|
||||
mixin(op, "x"),
|
||||
mixin(op, "y"),
|
||||
mixin(op, "z"),
|
||||
mixin(op, "w"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pragma(inline, true)
|
||||
T opBinary(IStr op)(T rhs) {
|
||||
static if (N == 1) {
|
||||
return T(
|
||||
mixin("x", op, "rhs.x"),
|
||||
);
|
||||
} else static if (N == 2) {
|
||||
return T(
|
||||
mixin("x", op, "rhs.x"),
|
||||
mixin("y", op, "rhs.y"),
|
||||
);
|
||||
} else static if (N == 3) {
|
||||
return T(
|
||||
mixin("x", op, "rhs.x"),
|
||||
mixin("y", op, "rhs.y"),
|
||||
mixin("z", op, "rhs.z"),
|
||||
);
|
||||
} else static if (N == 4) {
|
||||
return T(
|
||||
mixin("x", op, "rhs.x"),
|
||||
mixin("y", op, "rhs.y"),
|
||||
mixin("z", op, "rhs.z"),
|
||||
mixin("w", op, "rhs.w"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pragma(inline, true)
|
||||
void opOpAssign(IStr op)(T rhs) {
|
||||
static if (N == 1) {
|
||||
mixin("x", op, "=rhs.x;");
|
||||
} else static if (N == 2) {
|
||||
mixin("x", op, "=rhs.x;");
|
||||
mixin("y", op, "=rhs.y;");
|
||||
} else static if (N == 3) {
|
||||
mixin("x", op, "=rhs.x;");
|
||||
mixin("y", op, "=rhs.y;");
|
||||
mixin("z", op, "=rhs.z;");
|
||||
} else static if (N == 4) {
|
||||
mixin("x", op, "=rhs.x;");
|
||||
mixin("y", op, "=rhs.y;");
|
||||
mixin("z", op, "=rhs.z;");
|
||||
mixin("w", op, "=rhs.w;");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mixin template addRgbaOps(T, Sz N) {
|
||||
static assert(N >= 1 && N <= 4, "Color `" ~ T.stringof ~ "` must have a dimension between 1 and 4.");
|
||||
|
||||
pragma(inline, true)
|
||||
T opUnary(IStr op)() {
|
||||
static if (N == 1) {
|
||||
return T(
|
||||
mixin(op, "r"),
|
||||
);
|
||||
} else static if (N == 2) {
|
||||
return T(
|
||||
mixin(op, "r"),
|
||||
mixin(op, "g"),
|
||||
);
|
||||
} else static if (N == 3) {
|
||||
return T(
|
||||
mixin(op, "r"),
|
||||
mixin(op, "g"),
|
||||
mixin(op, "b"),
|
||||
);
|
||||
} else static if (N == 4) {
|
||||
return T(
|
||||
mixin(op, "r"),
|
||||
mixin(op, "g"),
|
||||
mixin(op, "b"),
|
||||
mixin(op, "a"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pragma(inline, true)
|
||||
T opBinary(IStr op)(T rhs) {
|
||||
static if (N == 1) {
|
||||
return T(
|
||||
mixin("r", op, "rhs.r"),
|
||||
);
|
||||
} else static if (N == 2) {
|
||||
return T(
|
||||
mixin("r", op, "rhs.r"),
|
||||
mixin("g", op, "rhs.g"),
|
||||
);
|
||||
} else static if (N == 3) {
|
||||
return T(
|
||||
mixin("r", op, "rhs.r"),
|
||||
mixin("g", op, "rhs.g"),
|
||||
mixin("b", op, "rhs.b"),
|
||||
);
|
||||
} else static if (N == 4) {
|
||||
return T(
|
||||
mixin("r", op, "rhs.r"),
|
||||
mixin("g", op, "rhs.g"),
|
||||
mixin("b", op, "rhs.b"),
|
||||
mixin("a", op, "rhs.a"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pragma(inline, true)
|
||||
void opOpAssign(IStr op)(T rhs) {
|
||||
static if (N == 1) {
|
||||
mixin("r", op, "=rhs.r;");
|
||||
} else static if (N == 2) {
|
||||
mixin("r", op, "=rhs.r;");
|
||||
mixin("g", op, "=rhs.g;");
|
||||
} else static if (N == 3) {
|
||||
mixin("r", op, "=rhs.r;");
|
||||
mixin("g", op, "=rhs.g;");
|
||||
mixin("b", op, "=rhs.b;");
|
||||
} else static if (N == 4) {
|
||||
mixin("r", op, "=rhs.r;");
|
||||
mixin("g", op, "=rhs.g;");
|
||||
mixin("b", op, "=rhs.b;");
|
||||
mixin("a", op, "=rhs.a;");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function test.
|
||||
unittest {
|
||||
assert(isInAliasArgs!(int, AliasArgs!(float)) == false);
|
||||
assert(isInAliasArgs!(int, AliasArgs!(float, int)) == true);
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/// The `types` module provides a collection of type definitions.
|
||||
module popka.core.types;
|
||||
|
||||
// Popka utilizes two groups of attributes:
|
||||
// 1. @safe @nogc nothrow
|
||||
// 2. @trusted @nogc nothrow
|
||||
@safe @nogc nothrow:
|
||||
|
||||
alias Sz = size_t;
|
||||
|
||||
alias Str = char[];
|
||||
alias Str16 = wchar[];
|
||||
alias Str32 = dchar[];
|
||||
|
||||
alias IStr = const(char)[];
|
||||
alias IStr16 = const(wchar)[];
|
||||
alias IStr32 = const(dchar)[];
|
||||
|
||||
alias CStr = char*;
|
||||
alias CStr16 = wchar*;
|
||||
alias CStr32 = dchar*;
|
||||
|
||||
alias ICStr = const(char)*;
|
||||
alias ICStr16 = const(wchar)*;
|
||||
alias ICStr32 = const(dchar)*;
|
|
@ -1,196 +0,0 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/// The `unions` module provides functions and data structures for working with unions.
|
||||
module popka.core.unions;
|
||||
|
||||
import popka.core.types;
|
||||
import popka.core.traits;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
alias VariantKind = int;
|
||||
|
||||
struct None {}
|
||||
|
||||
union VariantValue(A...) {
|
||||
static assert(A.length != 0, "Arguments must contain at least one element.");
|
||||
|
||||
static foreach (i, T; A) {
|
||||
static if (i == 0 && isNumberType!T) {
|
||||
mixin("T ", "member", toCleanNumber!i, "= 0;");
|
||||
} else {
|
||||
mixin("T ", "member", toCleanNumber!i, ";");
|
||||
}
|
||||
}
|
||||
|
||||
enum length = A.length;
|
||||
alias Types = A;
|
||||
}
|
||||
|
||||
struct Variant(A...) {
|
||||
VariantValue!A value;
|
||||
VariantKind kind;
|
||||
alias value this;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
static foreach (i, T; A) {
|
||||
@trusted
|
||||
this(T value) {
|
||||
this.value = *(cast(VariantValue!A*) &value);
|
||||
this.kind = i;
|
||||
}
|
||||
}
|
||||
|
||||
static foreach (i, T; A) {
|
||||
@trusted
|
||||
void opAssign(T rhs) {
|
||||
value = *(cast(VariantValue!A*) &rhs);
|
||||
kind = i;
|
||||
}
|
||||
}
|
||||
|
||||
IStr kindName() {
|
||||
static foreach (i, T; A) {
|
||||
if (kind == i) {
|
||||
return T.stringof;
|
||||
}
|
||||
}
|
||||
assert(0, "WTF!");
|
||||
}
|
||||
|
||||
bool isKind(T)() {
|
||||
static assert(isInAliasArgs!(T, A), "Type `" ~ T.stringof ~ "` is not part of the variant.");
|
||||
return kind == findInAliasArgs!(T, A);
|
||||
}
|
||||
|
||||
@trusted
|
||||
ref A[0] base() {
|
||||
return member0;
|
||||
}
|
||||
|
||||
@trusted
|
||||
ref T get(T)() {
|
||||
if (isKind!T) {
|
||||
mixin("return ", "value.member", findInAliasArgs!(T, A), ";");
|
||||
} else {
|
||||
static foreach (i, TT; A) {
|
||||
if (i == kind) {
|
||||
assert(0, "Value is `" ~ A[i].stringof ~ "` and not `" ~ T.stringof ~ "`.");
|
||||
}
|
||||
}
|
||||
assert(0, "WTF!");
|
||||
}
|
||||
}
|
||||
|
||||
@trusted
|
||||
auto call(IStr func, AA...)(AA args) {
|
||||
switch (kind) {
|
||||
static foreach (i, T; A) {
|
||||
static assert(hasMember!(T, func), funcImplementationErrorMessage!(T, func));
|
||||
mixin("case ", i, ": return value.member", toCleanNumber!i, ".", func, "(args);");
|
||||
}
|
||||
default: assert(0, "WTF!");
|
||||
}
|
||||
}
|
||||
|
||||
template kindOf(T) {
|
||||
static assert(isInAliasArgs!(T, A), "Type `" ~ T.stringof ~ "` is not part of the variant.");
|
||||
enum kindOf = findInAliasArgs!(T, A);
|
||||
}
|
||||
|
||||
template kindNameOf(T) {
|
||||
static assert(isInAliasArgs!(T, A), "Type `" ~ T.stringof ~ "` is not part of the variant.");
|
||||
enum kindNameOf = T.stringof;
|
||||
}
|
||||
}
|
||||
|
||||
T toVariant(T)(VariantKind kind) {
|
||||
static assert(isVariantType!T, "Type `" ~ T.stringof ~ "` is not a variant.");
|
||||
|
||||
T result;
|
||||
static foreach (i, Type; T.Types) {
|
||||
if (i == kind) {
|
||||
static if (isNumberType!Type) {
|
||||
result = cast(Type) 0;
|
||||
} else {
|
||||
result = Type.init;
|
||||
}
|
||||
goto loopExit;
|
||||
}
|
||||
}
|
||||
loopExit:
|
||||
return result;
|
||||
}
|
||||
|
||||
T toVariant(T)(IStr kindName) {
|
||||
static assert(isVariantType!T, "Type `" ~ T.stringof ~ "` is not a variant.");
|
||||
|
||||
T result;
|
||||
static foreach (i, Type; T.Types) {
|
||||
if (Type.stringof == kindName) {
|
||||
static if (isNumberType!Type) {
|
||||
result = cast(Type) 0;
|
||||
} else {
|
||||
result = Type.init;
|
||||
}
|
||||
goto loopExit;
|
||||
}
|
||||
}
|
||||
loopExit:
|
||||
return result;
|
||||
}
|
||||
|
||||
bool isVariantType(T)() {
|
||||
return is(T : Variant!A, A...);
|
||||
}
|
||||
|
||||
mixin template addBase(T) {
|
||||
T base;
|
||||
alias base this;
|
||||
}
|
||||
|
||||
// Variant test.
|
||||
unittest {
|
||||
alias Number = Variant!(float, double);
|
||||
|
||||
assert(Number().kindName == "float");
|
||||
assert(Number().isKind!float == true);
|
||||
assert(Number().isKind!double == false);
|
||||
assert(Number().get!float() == 0);
|
||||
assert(Number(0.0f).kindName == "float");
|
||||
assert(Number(0.0f).isKind!float == true);
|
||||
assert(Number(0.0f).isKind!double == false);
|
||||
assert(Number(0.0f).get!float() == 0);
|
||||
assert(Number(0.0).isKind!float == false);
|
||||
assert(Number(0.0).isKind!double == true);
|
||||
assert(Number(0.0).kindName == "double");
|
||||
assert(Number(0.0).get!double() == 0);
|
||||
assert(Number.kindOf!float == 0);
|
||||
assert(Number.kindOf!double == 1);
|
||||
assert(Number.kindNameOf!float == "float");
|
||||
assert(Number.kindNameOf!double == "double");
|
||||
|
||||
auto number = Number();
|
||||
number = 0.0;
|
||||
assert(number.get!double() == 0);
|
||||
number = 0.0f;
|
||||
assert(number.get!float() == 0);
|
||||
number.get!float() += 69.0f;
|
||||
assert(number.get!float() == 69);
|
||||
|
||||
auto numberPtr = &number.get!float();
|
||||
*numberPtr *= 10;
|
||||
assert(number.get!float() == 690);
|
||||
}
|
||||
|
||||
// Function test.
|
||||
unittest {
|
||||
alias Number = Variant!(float, double);
|
||||
|
||||
assert(toVariant!Number(Number.kindOf!float).get!float() == 0);
|
||||
assert(toVariant!Number(Number.kindOf!double).get!double() == 0);
|
||||
assert(toVariant!Number(Number.kindNameOf!float).get!float() == 0);
|
||||
assert(toVariant!Number(Number.kindNameOf!double).get!double() == 0);
|
||||
}
|
|
@ -4,12 +4,9 @@
|
|||
/// The dialogue module is a versatile dialogue system,
|
||||
/// enabling the creation of interactive conversations and branching narratives.
|
||||
|
||||
module popka.game.dialogue;
|
||||
module popka.dialogue;
|
||||
|
||||
import popka.core.containers;
|
||||
import popka.core.io;
|
||||
import popka.core.ascii;
|
||||
import popka.game.engine;
|
||||
import popka.engine;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
|
@ -3,16 +3,11 @@
|
|||
|
||||
/// The engine module functions as a lightweight 2D game engine.
|
||||
|
||||
module popka.game.engine;
|
||||
module popka.engine;
|
||||
|
||||
import ray = popka.vendor.ray;
|
||||
import popka.core.ascii;
|
||||
import popka.core.colors;
|
||||
import popka.core.containers;
|
||||
import popka.core.faults;
|
||||
import popka.core.io;
|
||||
import popka.core.math;
|
||||
import popka.core.types;
|
||||
import ray = popka.ray;
|
||||
|
||||
public import joka;
|
||||
|
||||
@trusted @nogc nothrow:
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
module popka.game;
|
||||
|
||||
public import popka.game.dialogue;
|
||||
public import popka.game.engine;
|
|
@ -3,5 +3,5 @@
|
|||
|
||||
module popka;
|
||||
|
||||
public import popka.core;
|
||||
public import popka.game;
|
||||
public import popka.dialogue;
|
||||
public import popka.engine;
|
||||
|
|
8
source/popka/ray/package.d
Normal file
8
source/popka/ray/package.d
Normal file
|
@ -0,0 +1,8 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
module popka.ray;
|
||||
|
||||
public import popka.ray.raylib;
|
||||
public import popka.ray.raylibpp;
|
||||
public import popka.ray.rlgl;
|
|
@ -76,7 +76,7 @@
|
|||
*
|
||||
**********************************************************************************************/
|
||||
|
||||
module popka.vendor.ray.raylib;
|
||||
module popka.ray.raylib;
|
||||
|
||||
// import core.stdc.config;
|
||||
// import core.stdc.stdarg;
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
/// The raylibpp module contains helper functions to reduce some raylib boilerplate.
|
||||
|
||||
module popka.vendor.ray.raylibpp;
|
||||
module popka.ray.raylibpp;
|
||||
|
||||
import popka.vendor.ray.raylib;
|
||||
import popka.ray.raylib;
|
||||
|
||||
alias drawPixel = DrawPixel;
|
||||
alias drawPixel = DrawPixelV;
|
|
@ -104,7 +104,7 @@
|
|||
*
|
||||
**********************************************************************************************/
|
||||
|
||||
module popka.vendor.ray.rlgl;
|
||||
module popka.ray.rlgl;
|
||||
|
||||
@nogc nothrow extern(C):
|
||||
|
8
source/popka/vendor/ray/package.d
vendored
8
source/popka/vendor/ray/package.d
vendored
|
@ -1,8 +0,0 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
module popka.vendor.ray;
|
||||
|
||||
public import popka.vendor.ray.raylib;
|
||||
public import popka.vendor.ray.raylibpp;
|
||||
public import popka.vendor.ray.rlgl;
|
Loading…
Add table
Add a link
Reference in a new issue