More public 0.6.4 fixes (#1016)
Co-authored-by: Hyper_ <40342021+NotHyper-474@users.noreply.github.com> Co-authored-by: Lasercar <64717068+Lasercar@users.noreply.github.com> Co-authored-by: Lasercar <B_l_u_t_a_c_k@hotmail.com.au> Co-authored-by: anysad <anysadiscool@gmail.com> Co-authored-by: Hundrec <hundrecard@gmail.com> Co-authored-by: VioletSnowLeopard <ngzeretzke@gmail.com> Co-authored-by: unknown <47027981+ACrazyTown@users.noreply.github.com> Co-authored-by: kade-github <26305836+Kade-github@users.noreply.github.com> Co-authored-by: Abnormal <86753001+AbnormalPoof@users.noreply.github.com> Co-authored-by: CrusherNotDrip <90648119+CrusherNotDrip@users.noreply.github.com> Co-authored-by: cyn0x8 <cyn0x8+git@gmail.com>
This commit is contained in:
co-authored by
Hyper_
Lasercar
Lasercar
anysad
Hundrec
VioletSnowLeopard
unknown
kade-github
Abnormal
CrusherNotDrip
cyn0x8
parent
35f7113091
commit
44c32cb0fc
+5
-10
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Adaptive turns VSync off during FPS drops, which is better if supported by your graphics card.
|
||||
- Doesn't work on Mac, sorry!
|
||||
- Otis now has muzzle flashes when shooting.
|
||||
- Added a little easter egg to one of Pico's Results Screen animations.
|
||||
- Overhauled the sticker system to allow modders to easily add custom sticker sets. ([cdc468b](https://github.com/FunkinCrew/Funkin/commit/cdc468ba15683b3c0f54015e337673c77ddd7962)) - by @AbnormalPoof in [#4003](https://github.com/FunkinCrew/Funkin/pull/4003)
|
||||
- Implemented a macro to optimize registries and entries. ([1497521](https://github.com/FunkinCrew/Funkin/commit/14975212a918adb1a5d96a349ec9e8e63c9fc64b)) - by @lemz1 in [#3694](https://github.com/FunkinCrew/Funkin/pull/3694)
|
||||
- The macro is now used to retrieve base game assets. ([963e2ec](https://github.com/FunkinCrew/Funkin/commit/963e2ecd630fae24dae4206d315618cf4c4be6db)) - by @lemz1 in [#4707](https://github.com/FunkinCrew/Funkin/pull/4707)
|
||||
@@ -43,6 +44,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Renamed “Auto Pause” preference to “Pause on Unfocus” for clarity. ([52be941](https://github.com/FunkinCrew/Funkin/commit/52be941b4503da0ac76918e2482ab1804866f2cf)) - by @JackXson-Real in [#4346](https://github.com/FunkinCrew/Funkin/pull/4346)
|
||||
- Overhauled `FileUtil`, introducing various fixes, new functions, and sandboxing. ([95ade2a](https://github.com/FunkinCrew/Funkin/commit/95ade2a08b7709e8208ec1b3e123bf5b4308ba10)) - by @cyn0x8 in [#3032](https://github.com/FunkinCrew/Funkin/pull/3032)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Story mode medals are now awarded even without scoring a new personal best.
|
||||
- Highscores are now submitted to Newgrounds even without scoring a new personal best.
|
||||
- The Freeplay menu no longer displays songs without Erect variations when returning from an Erect variation song.
|
||||
- Fixed Freeplay DJ animations for Boyfriend and Pico when idling (properly this time).
|
||||
- Alternate instrumentals for Cocoa, Senpai, Roses, and Stress are now locked until their Pico Mix is beaten.
|
||||
@@ -183,16 +188,6 @@ The Pit Stop 2 update!
|
||||
- Removed an unused class from Polymod blacklist. ([06c12e3](https://github.com/FunkinCrew/Funkin/commit/06c12e36c6bd6df4e2be32a3bec540172e79e162)) - by @AbnormalPoof in [#3729](https://github.com/FunkinCrew/Funkin/pull/3729)
|
||||
- Many additional small bug fixes.
|
||||
|
||||
### Fixed
|
||||
|
||||
* @PatoFlamejanteTV made their first contribution in [#3843](https://github.com/FunkinCrew/Funkin/pull/3843)
|
||||
* @sphis-Sinco made their first contribution in [#3881](https://github.com/FunkinCrew/Funkin/pull/3881)
|
||||
* @MrMadera made their first contribution in [#3934](https://github.com/FunkinCrew/Funkin/pull/3934)
|
||||
* @MidyGamy made their first contribution in [#4068](https://github.com/FunkinCrew/Funkin/pull/4068)
|
||||
* @Lasercar made their first contribution in [#4082](https://github.com/FunkinCrew/Funkin/pull/4082)
|
||||
* @MrScottyPieey made their first contribution in [#4085](https://github.com/FunkinCrew/Funkin/pull/4085)
|
||||
|
||||
|
||||
## New Contributors for 0.6.0
|
||||
|
||||
* @PatoFlamejanteTV made their first contribution in [#3843](https://github.com/FunkinCrew/Funkin/pull/3843)
|
||||
|
||||
@@ -44,7 +44,8 @@ class Medals
|
||||
if (NewgroundsClient.instance.isLoggedIn())
|
||||
{
|
||||
var medalList = NewgroundsClient.instance.medals;
|
||||
if (medalList == null) return;
|
||||
@:privateAccess
|
||||
if (medalList == null || medalList._map == null) return;
|
||||
|
||||
var medalData:Null<MedalData> = medalList.get(medal.getId());
|
||||
@:privateAccess
|
||||
|
||||
@@ -66,6 +66,8 @@ class FlxAtlasSprite extends FlxAnimate
|
||||
throw 'FlxAtlasSprite not initialized properly. Are you sure the path (${path}) exists?';
|
||||
}
|
||||
|
||||
onAnimationComplete.add(cleanupAnimation);
|
||||
|
||||
// This defaults the sprite to play the first animation in the atlas,
|
||||
// then pauses it. This ensures symbols are intialized properly.
|
||||
this.anim.play('');
|
||||
@@ -317,7 +319,6 @@ class FlxAtlasSprite extends FlxAnimate
|
||||
else if (fr != null && anim.curFrame != anim.length - 1)
|
||||
{
|
||||
anim.curFrame--;
|
||||
cleanupAnimation(currentAnimation ?? "");
|
||||
_onAnimationComplete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,8 +80,10 @@ class PolymodHandler
|
||||
*/
|
||||
public static function loadAllMods():Void
|
||||
{
|
||||
#if sys
|
||||
// Create the mod root if it doesn't exist.
|
||||
createModRoot();
|
||||
#end
|
||||
trace('Initializing Polymod (using all mods)...');
|
||||
loadModsById(getAllModIds());
|
||||
}
|
||||
@@ -91,9 +93,10 @@ class PolymodHandler
|
||||
*/
|
||||
public static function loadEnabledMods():Void
|
||||
{
|
||||
#if sys
|
||||
// Create the mod root if it doesn't exist.
|
||||
createModRoot();
|
||||
|
||||
#end
|
||||
trace('Initializing Polymod (using configured mods)...');
|
||||
loadModsById(Save.instance.enabledModIds);
|
||||
}
|
||||
@@ -103,9 +106,10 @@ class PolymodHandler
|
||||
*/
|
||||
public static function loadNoMods():Void
|
||||
{
|
||||
#if sys
|
||||
// Create the mod root if it doesn't exist.
|
||||
createModRoot();
|
||||
|
||||
#end
|
||||
// We still need to configure the debug print calls etc.
|
||||
trace('Initializing Polymod (using no mods)...');
|
||||
loadModsById([]);
|
||||
|
||||
@@ -1229,7 +1229,7 @@ class PlayState extends MusicBeatSubState
|
||||
if (!isMinimalMode)
|
||||
{
|
||||
if (iconP1 != null) iconP1.updatePosition();
|
||||
if (iconP1 != null) iconP2.updatePosition();
|
||||
if (iconP2 != null) iconP2.updatePosition();
|
||||
}
|
||||
|
||||
// Transition to the game over substate.
|
||||
@@ -1898,14 +1898,15 @@ class PlayState extends MusicBeatSubState
|
||||
// Position the player strumline on the right half of the screen
|
||||
playerStrumline.x = (FlxG.width / 2 + Constants.STRUMLINE_X_OFFSET) + (cutoutSize / 2.0); // Classic style
|
||||
// playerStrumline.x = FlxG.width - playerStrumline.width - Constants.STRUMLINE_X_OFFSET; // Centered style
|
||||
playerStrumline.y = Preferences.downscroll ? FlxG.height - playerStrumline.height - Constants.STRUMLINE_Y_OFFSET : Constants.STRUMLINE_Y_OFFSET;
|
||||
|
||||
playerStrumline.y = Preferences.downscroll ? FlxG.height - playerStrumline.height - Constants.STRUMLINE_Y_OFFSET - noteStyle.getStrumlineOffsets()[1] : Constants.STRUMLINE_Y_OFFSET;
|
||||
|
||||
playerStrumline.zIndex = 1001;
|
||||
playerStrumline.cameras = [camHUD];
|
||||
|
||||
// Position the opponent strumline on the left half of the screen
|
||||
opponentStrumline.x = Constants.STRUMLINE_X_OFFSET + cutoutSize;
|
||||
opponentStrumline.y = Preferences.downscroll ? FlxG.height - opponentStrumline.height - Constants.STRUMLINE_Y_OFFSET : Constants.STRUMLINE_Y_OFFSET;
|
||||
opponentStrumline.x = Constants.STRUMLINE_X_OFFSET;
|
||||
opponentStrumline.y = Preferences.downscroll ? FlxG.height - opponentStrumline.height - Constants.STRUMLINE_Y_OFFSET - noteStyle.getStrumlineOffsets()[1] : Constants.STRUMLINE_Y_OFFSET;
|
||||
|
||||
opponentStrumline.zIndex = 1000;
|
||||
opponentStrumline.cameras = [camHUD];
|
||||
@@ -2889,7 +2890,7 @@ class PlayState extends MusicBeatSubState
|
||||
#end
|
||||
|
||||
// 9: Toggle the old icon.
|
||||
if (FlxG.keys.justPressed.NINE) iconP1.toggleOldIcon();
|
||||
if (FlxG.keys.justPressed.NINE && iconP1 != null) iconP1.toggleOldIcon();
|
||||
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
// PAGEUP: Skip forward two sections.
|
||||
|
||||
@@ -572,7 +572,7 @@ class DebugBoundingState extends FlxState
|
||||
_file.addEventListener(Event.COMPLETE, onSaveComplete);
|
||||
_file.addEventListener(Event.CANCEL, onSaveCancel);
|
||||
_file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError);
|
||||
_file.save(saveString,);
|
||||
_file.save(saveString, fileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3225,7 +3225,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
#if sys
|
||||
// TODO: Is there a way to open a folder and highlight a file in it?
|
||||
var absoluteBackupsPath:String = Path.join([Sys.getCwd(), ChartEditorImportExportHandler.BACKUPS_PATH]);
|
||||
WindowUtil.openFolder(absoluteBackupsPath);
|
||||
FileUtil.openFolder(absoluteBackupsPath);
|
||||
return true;
|
||||
#else
|
||||
trace('No file system access, cannot open backups folder.');
|
||||
@@ -5838,6 +5838,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
{
|
||||
uiCamera.revive();
|
||||
FlxG.cameras.reset(uiCamera);
|
||||
uiCamera.onResize();
|
||||
|
||||
add(this.root);
|
||||
}
|
||||
|
||||
@@ -91,6 +91,8 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog
|
||||
#end
|
||||
|
||||
linkRecentChart.onClick = function(_event) {
|
||||
linkRecentChart.hide();
|
||||
|
||||
this.hideDialog(DialogButton.CANCEL);
|
||||
|
||||
// Load chart from file
|
||||
|
||||
@@ -1267,7 +1267,7 @@ class StageEditorState extends UIState
|
||||
case "open folder":
|
||||
#if sys
|
||||
var absoluteBackupsPath:String = haxe.io.Path.join([Sys.getCwd(), BACKUPS_PATH]);
|
||||
WindowUtil.openFolder(absoluteBackupsPath);
|
||||
FileUtil.openFolder(absoluteBackupsPath);
|
||||
#end
|
||||
|
||||
case "test stage":
|
||||
|
||||
@@ -5,7 +5,6 @@ import haxe.ui.containers.dialogs.Dialog.DialogButton;
|
||||
import funkin.util.FileUtil;
|
||||
import haxe.io.Path;
|
||||
import funkin.util.DateUtil;
|
||||
import funkin.util.WindowUtil;
|
||||
|
||||
using StringTools;
|
||||
|
||||
@@ -57,7 +56,7 @@ class BackupAvailableDialog extends Dialog
|
||||
// :[
|
||||
#if sys
|
||||
var absoluteBackupsPath:String = Path.join([Sys.getCwd(), StageEditorState.BACKUPS_PATH]);
|
||||
WindowUtil.openFolder(absoluteBackupsPath);
|
||||
FileUtil.openFolder(absoluteBackupsPath);
|
||||
#end
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class FreeplayScore extends FlxTypedSpriteGroup<ScoreNum>
|
||||
loopNum--;
|
||||
}
|
||||
|
||||
while (loopNum > 0)
|
||||
while (loopNum >= 0)
|
||||
{
|
||||
group.members[loopNum].digit = 0;
|
||||
loopNum--;
|
||||
|
||||
@@ -567,13 +567,13 @@ class FreeplayState extends MusicBeatSubState
|
||||
switch (str)
|
||||
{
|
||||
case 'fav':
|
||||
generateSongList({filterType: FAVORITE}, true);
|
||||
generateSongList({filterType: FAVORITE}, true, false);
|
||||
case 'ALL':
|
||||
generateSongList(null, true);
|
||||
generateSongList(null, true, false);
|
||||
case '#':
|
||||
generateSongList({filterType: REGEXP, filterData: '0-9'}, true);
|
||||
generateSongList({filterType: REGEXP, filterData: '0-9'}, true, false);
|
||||
default:
|
||||
generateSongList({filterType: REGEXP, filterData: str}, true);
|
||||
generateSongList({filterType: REGEXP, filterData: str}, true, false);
|
||||
}
|
||||
|
||||
// If the current song is still in the list, or if it was random, we'll land on it
|
||||
@@ -2033,7 +2033,8 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
var songScore:Null<SaveScoreData> = Save.instance.getSongScore(daSong.data.id, currentDifficulty, currentVariation);
|
||||
intendedScore = songScore?.score ?? 0;
|
||||
intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes);
|
||||
intendedCompletion = songScore == null ? 0.0 : Math.max(0, ((songScore.tallies.sick +
|
||||
songScore.tallies.good - songScore.tallies.missed) / songScore.tallies.totalNotes));
|
||||
rememberedDifficulty = currentDifficulty;
|
||||
grpCapsules.members[curSelected].refreshDisplay((prepForNewRank == true) ? false : true);
|
||||
}
|
||||
|
||||
@@ -108,6 +108,7 @@ class MainMenuState extends MusicBeatState
|
||||
createMenuItem('freeplay', 'mainmenu/freeplay', function() {
|
||||
persistentDraw = true;
|
||||
persistentUpdate = false;
|
||||
rememberedSelectedIndex = menuItems.selectedIndex;
|
||||
// Freeplay has its own custom transition
|
||||
FlxTransitionableState.skipNextTransIn = true;
|
||||
FlxTransitionableState.skipNextTransOut = true;
|
||||
@@ -422,6 +423,7 @@ class MainMenuState extends MusicBeatState
|
||||
{
|
||||
if (menuItems.enabled && !menuItems.busy)
|
||||
{
|
||||
rememberedSelectedIndex = menuItems.selectedIndex;
|
||||
FlxG.switchState(() -> new TitleState());
|
||||
FunkinSound.playOnce(Paths.sound('cancelMenu'));
|
||||
}
|
||||
|
||||
+138
-57
@@ -52,24 +52,44 @@ class FileUtil
|
||||
/**
|
||||
* Paths which should not be deleted or modified by scripts.
|
||||
*/
|
||||
private static final PROTECTED_PATHS:Array<String> = [
|
||||
'',
|
||||
'assets',
|
||||
'manifest',
|
||||
'manifest/*',
|
||||
'plugins',
|
||||
'plugins/*',
|
||||
'Funkin.exe',
|
||||
'Funkin',
|
||||
'libvlc.dll',
|
||||
'libvlccore.dll',
|
||||
'lime.ndll'
|
||||
];
|
||||
public static var PROTECTED_PATHS(get, never):Array<String>;
|
||||
|
||||
public static function get_PROTECTED_PATHS():Array<String>
|
||||
{
|
||||
final protected:Array<String> = [
|
||||
'',
|
||||
'.',
|
||||
'assets',
|
||||
'assets/*',
|
||||
'backups',
|
||||
'backups/*',
|
||||
'manifest',
|
||||
'manifest/*',
|
||||
'plugins',
|
||||
'plugins/*',
|
||||
'Funkin.exe',
|
||||
'Funkin',
|
||||
'icon.ico',
|
||||
'libvlc.dll',
|
||||
'libvlccore.dll',
|
||||
'lime.ndll',
|
||||
'scores.json'
|
||||
];
|
||||
|
||||
#if sys
|
||||
for (i in 0...protected.length)
|
||||
{
|
||||
protected[i] = sys.FileSystem.fullPath(Path.join([gameDirectory, protected[i]]));
|
||||
}
|
||||
#end
|
||||
|
||||
return protected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalid filesystem characters.
|
||||
* Regex for invalid filesystem characters.
|
||||
*/
|
||||
private static final INVALID_CHARS:Array<String> = [':', '*', '?', '"', '<', '>', '|'];
|
||||
public static final INVALID_CHARS:EReg = ~/[:*?"<>|\n\r\t]/g;
|
||||
|
||||
#if sys
|
||||
private static var _gameDirectory:Null<String> = null;
|
||||
@@ -82,7 +102,7 @@ class FileUtil
|
||||
return _gameDirectory;
|
||||
}
|
||||
|
||||
return _gameDirectory = Path.directory(Path.normalize(sys.FileSystem.fullPath(Sys.programPath())));
|
||||
return _gameDirectory = sys.FileSystem.fullPath(Path.directory(Sys.programPath()));
|
||||
}
|
||||
#end
|
||||
|
||||
@@ -601,8 +621,6 @@ class FileUtil
|
||||
throw 'Target path is a directory, not a file: "$path"';
|
||||
}
|
||||
|
||||
createDirIfNotExists(Path.directory(path));
|
||||
|
||||
var shouldWrite:Bool = true;
|
||||
switch (mode)
|
||||
{
|
||||
@@ -791,14 +809,14 @@ class FileUtil
|
||||
*/
|
||||
public static function createDirIfNotExists(dir:String):Void
|
||||
{
|
||||
#if sys
|
||||
if (!directoryExists(dir))
|
||||
{
|
||||
#if sys
|
||||
sys.FileSystem.createDirectory(dir);
|
||||
#else
|
||||
throw 'Directory creation is not supported on this platform.';
|
||||
#end
|
||||
}
|
||||
#else
|
||||
throw 'Directory creation is not supported on this platform.';
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1124,8 +1142,64 @@ class FileUtil
|
||||
};
|
||||
}
|
||||
|
||||
public static function openFolder(pathFolder:String) // what is this for??
|
||||
WindowUtil.openFolder(pathFolder);
|
||||
/**
|
||||
* Runs platform-specific code to open a path in the file explorer.
|
||||
*
|
||||
* @param pathFolder The path of the folder to open.
|
||||
*/
|
||||
public static function openFolder(pathFolder:String):Void
|
||||
{
|
||||
#if sys
|
||||
pathFolder = pathFolder.trim();
|
||||
if (!directoryExists(pathFolder))
|
||||
{
|
||||
throw 'Path is not a directory: "$pathFolder"';
|
||||
}
|
||||
|
||||
#if windows
|
||||
Sys.command('explorer', [pathFolder.replace('/', '\\')]);
|
||||
#elseif mac
|
||||
// mac could be fuckie with where the log folder is relative to the game file...
|
||||
// if this comment is still here... it means it has NOT been verified on mac yet!
|
||||
//
|
||||
// FileUtil.hx note: this was originally used to open the logs specifically!
|
||||
// thats why the above comment is there!
|
||||
Sys.command('open', [pathFolder]);
|
||||
#elseif linux
|
||||
// TODO: implement linux
|
||||
// some shit with xdg-open :thinking: emoji...
|
||||
#end
|
||||
#else
|
||||
throw 'External folder open is not supported on this platform.';
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs platform-specific code to open a file explorer and select a specific file.
|
||||
*
|
||||
* @param path The path of the file to select.
|
||||
*/
|
||||
public static function openSelectFile(path:String):Void
|
||||
{
|
||||
#if sys
|
||||
path = path.trim();
|
||||
if (!pathExists(path))
|
||||
{
|
||||
throw 'Path does not exist: "$path"';
|
||||
}
|
||||
|
||||
#if windows
|
||||
Sys.command('explorer', ['/select,', path.replace('/', '\\')]);
|
||||
#elseif mac
|
||||
Sys.command('open', ['-R', path]);
|
||||
#elseif linux
|
||||
// TODO: unsure of the linux equivalent to opening a folder and then "selecting" a file.
|
||||
Sys.command('open', [path]);
|
||||
#end
|
||||
#else
|
||||
throw 'External file selection is not supported on this platform.';
|
||||
#end
|
||||
}
|
||||
|
||||
private static function convertTypeFilter(?typeFilter:Array<FileFilter>):Null<String>
|
||||
{
|
||||
@@ -1160,7 +1234,8 @@ class FileUtilSandboxed
|
||||
*/
|
||||
public static function sanitizePath(path:String):String
|
||||
{
|
||||
if (path == null)
|
||||
path = (path ?? '').trim();
|
||||
if (path == '')
|
||||
{
|
||||
#if sys
|
||||
return FileUtil.gameDirectory;
|
||||
@@ -1174,19 +1249,14 @@ class FileUtilSandboxed
|
||||
path = path.substring(path.lastIndexOf(':') + 1);
|
||||
}
|
||||
|
||||
path = path.trim().replace('\\', '/');
|
||||
path = path.replace('\\', '/');
|
||||
while (path.contains('//'))
|
||||
{
|
||||
path = path.replace('//', '/');
|
||||
}
|
||||
|
||||
@:privateAccess for (char in FileUtil.INVALID_CHARS)
|
||||
{
|
||||
path = path.replace(char, '');
|
||||
}
|
||||
|
||||
var parts:Array<String> = path.split('/');
|
||||
var sanitized:Array<String> = new Array<String>();
|
||||
final parts:Array<String> = FileUtil.INVALID_CHARS.replace(path, '').split('/');
|
||||
final sanitized:Array<String> = new Array<String>();
|
||||
for (part in parts)
|
||||
{
|
||||
switch (part)
|
||||
@@ -1196,25 +1266,31 @@ class FileUtilSandboxed
|
||||
case '..':
|
||||
sanitized.pop();
|
||||
default:
|
||||
sanitized.push(part);
|
||||
sanitized.push(part.trim());
|
||||
}
|
||||
}
|
||||
|
||||
var ret:String = sanitized.join('/');
|
||||
if (sanitized.length == 0)
|
||||
{
|
||||
#if sys
|
||||
return FileUtil.gameDirectory;
|
||||
#else
|
||||
return '';
|
||||
#end
|
||||
}
|
||||
|
||||
#if sys
|
||||
var realPath:String = Path.normalize(sys.FileSystem.fullPath(Path.join([FileUtil.gameDirectory, ret])));
|
||||
// ^ TODO: figure out how to get "real" path of symlinked paths
|
||||
|
||||
if (!realPath.startsWith(FileUtil.gameDirectory.toString()))
|
||||
// TODO: figure out how to get "real" path of symlinked paths
|
||||
final realPath:String = sys.FileSystem.fullPath(Path.join([FileUtil.gameDirectory, sanitized.join('/')]));
|
||||
if (!realPath.startsWith(FileUtil.gameDirectory))
|
||||
{
|
||||
return FileUtil.gameDirectory;
|
||||
}
|
||||
|
||||
ret = realPath;
|
||||
return realPath;
|
||||
#else
|
||||
return sanitized.join('/');
|
||||
#end
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1222,9 +1298,9 @@ class FileUtilSandboxed
|
||||
* @param path The path to check.
|
||||
* @return Whether the path is protected.
|
||||
*/
|
||||
public static function isProtected(path:String):Bool
|
||||
public static function isProtected(path:String, sanitizeFirst:Bool = true):Bool
|
||||
{
|
||||
path = sanitizePath(path);
|
||||
if (sanitizeFirst) path = sanitizePath(path);
|
||||
@:privateAccess for (protected in FileUtil.PROTECTED_PATHS)
|
||||
{
|
||||
if (path == protected || (protected.contains('*') && path.startsWith(protected.substring(0, protected.indexOf('*')))))
|
||||
@@ -1301,7 +1377,7 @@ class FileUtilSandboxed
|
||||
|
||||
public static function saveFilesAsZIPToPath(resources:Array<Entry>, path:String, mode:FileWriteMode = Skip):Bool
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot write to protected path: $path';
|
||||
return FileUtil.saveFilesAsZIPToPath(resources, path, mode);
|
||||
}
|
||||
|
||||
@@ -1332,32 +1408,32 @@ class FileUtilSandboxed
|
||||
|
||||
public static function writeStringToPath(path:String, data:String, mode:FileWriteMode = Skip):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot write to protected path: $path';
|
||||
FileUtil.writeStringToPath(path, data, mode);
|
||||
}
|
||||
|
||||
public static function writeBytesToPath(path:String, data:Bytes, mode:FileWriteMode = Skip):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot write to protected path: $path';
|
||||
FileUtil.writeBytesToPath(path, data, mode);
|
||||
}
|
||||
|
||||
public static function appendStringToPath(path:String, data:String):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot write to protected path: $path';
|
||||
FileUtil.appendStringToPath(path, data);
|
||||
}
|
||||
|
||||
public static function moveFile(path:String, destination:String):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot move protected path: $path';
|
||||
if (isProtected(destination = sanitizePath(destination))) throw 'Cannot move to protected path: $destination';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot move protected path: $path';
|
||||
if (isProtected(destination = sanitizePath(destination), false)) throw 'Cannot move to protected path: $destination';
|
||||
FileUtil.moveFile(path, destination);
|
||||
}
|
||||
|
||||
public static function deleteFile(path:String):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot delete protected path: $path';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot delete protected path: $path';
|
||||
FileUtil.deleteFile(path);
|
||||
}
|
||||
|
||||
@@ -1393,14 +1469,14 @@ class FileUtilSandboxed
|
||||
|
||||
public static function moveDir(path:String, destination:String, ?ignore:Array<String>, strict:Bool = true):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot move protected path: "$path"';
|
||||
if (isProtected(destination = sanitizePath(destination))) throw 'Cannot move to protected path: "$destination"';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot move protected path: "$path"';
|
||||
if (isProtected(destination = sanitizePath(destination), false)) throw 'Cannot move to protected path: "$destination"';
|
||||
FileUtil.moveDir(path, destination, ignore, strict);
|
||||
}
|
||||
|
||||
public static function deleteDir(path:String, recursive:Bool = false, ?ignore:Array<String>):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot delete protected path: "$path"';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot delete protected path: "$path"';
|
||||
FileUtil.deleteDir(path, recursive, ignore);
|
||||
}
|
||||
|
||||
@@ -1416,7 +1492,7 @@ class FileUtilSandboxed
|
||||
|
||||
public static function rename(path:String, newName:String, keepExtension:Bool = true):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot rename protected path: "$path"';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot rename protected path: "$path"';
|
||||
FileUtil.rename(path, sanitizePath(newName), keepExtension);
|
||||
}
|
||||
|
||||
@@ -1445,9 +1521,14 @@ class FileUtilSandboxed
|
||||
return FileUtil.makeZIPEntryFromBytes(name, data);
|
||||
}
|
||||
|
||||
public static function openFolder(pathFolder:String)
|
||||
public static function openFolder(pathFolder:String):Void
|
||||
{
|
||||
FileUtil.openFolder(pathFolder);
|
||||
FileUtil.openFolder(sanitizePath(pathFolder));
|
||||
}
|
||||
|
||||
public static function openSelectFile(path:String):Void
|
||||
{
|
||||
FileUtil.openSelectFile(sanitizePath(path));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,10 @@ class MemoryUtil
|
||||
{
|
||||
#if cpp
|
||||
var result:String = 'HXCPP-Immix:';
|
||||
result += '\n- Memory Used: ${cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_USAGE)} bytes';
|
||||
result += '\n- Memory Reserved: ${cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_RESERVED)} bytes';
|
||||
result += '\n- Memory Current Pool: ${cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_CURRENT)} bytes';
|
||||
result += '\n- Memory Large Pool: ${cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_LARGE)} bytes';
|
||||
result += '\n- Memory Used: ${cpp.vm.Gc.memInfo64(cpp.vm.Gc.MEM_INFO_USAGE)} bytes';
|
||||
result += '\n- Memory Reserved: ${cpp.vm.Gc.memInfo64(cpp.vm.Gc.MEM_INFO_RESERVED)} bytes';
|
||||
result += '\n- Memory Current Pool: ${cpp.vm.Gc.memInfo64(cpp.vm.Gc.MEM_INFO_CURRENT)} bytes';
|
||||
result += '\n- Memory Large Pool: ${cpp.vm.Gc.memInfo64(cpp.vm.Gc.MEM_INFO_LARGE)} bytes';
|
||||
result += '\n- HXCPP Debugger: ${#if HXCPP_DEBUGGER 'Enabled' #else 'Disabled' #end}';
|
||||
result += '\n- HXCPP Exp Generational Mode: ${#if HXCPP_GC_GENERATIONAL 'Enabled' #else 'Disabled' #end}';
|
||||
result += '\n- HXCPP Exp Moving GC: ${#if HXCPP_GC_MOVING 'Enabled' #else 'Disabled' #end}';
|
||||
|
||||
@@ -16,6 +16,39 @@ using StringTools;
|
||||
#end
|
||||
class WindowUtil
|
||||
{
|
||||
/**
|
||||
* A regex to match valid URLs.
|
||||
*/
|
||||
public static final URL_REGEX:EReg = ~/^https?:\/?\/?(?:www\.)?[-a-zA-Z0-9@:%_\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/;
|
||||
|
||||
/**
|
||||
* Sanitizes a URL via a regex.
|
||||
*
|
||||
* @param targetUrl The URL to sanitize.
|
||||
* @return The sanitized URL, or an empty string if the URL is invalid.
|
||||
*/
|
||||
public static function sanitizeURL(targetUrl:String):String
|
||||
{
|
||||
targetUrl = (targetUrl ?? '').trim();
|
||||
if (targetUrl == '')
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
final lowerUrl:String = targetUrl.toLowerCase();
|
||||
if (!lowerUrl.startsWith('http:') && !lowerUrl.startsWith('https:'))
|
||||
{
|
||||
targetUrl = 'http://' + targetUrl;
|
||||
}
|
||||
|
||||
if (URL_REGEX.match(targetUrl))
|
||||
{
|
||||
return URL_REGEX.matched(0);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs platform-specific code to open a URL in a web browser.
|
||||
* @param targetUrl The URL to open.
|
||||
@@ -28,6 +61,12 @@ class WindowUtil
|
||||
else if (protocol[0] != 'http' && protocol[0] != 'https') throw "openURL can only open http and https links.";
|
||||
|
||||
#if FEATURE_OPEN_URL
|
||||
targetUrl = sanitizeURL(targetUrl);
|
||||
if (targetUrl == '')
|
||||
{
|
||||
throw 'Invalid URL: "$targetUrl"';
|
||||
}
|
||||
|
||||
#if linux
|
||||
Sys.command('/usr/bin/xdg-open $targetUrl &');
|
||||
#else
|
||||
@@ -63,54 +102,6 @@ class WindowUtil
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
* Runs platform-specific code to open a path in the file explorer.
|
||||
* @param targetPath The path to open.
|
||||
*/
|
||||
public static function openFolder(targetPath:String):Void
|
||||
{
|
||||
#if html5
|
||||
throw 'Cannot open URLs on this platform.';
|
||||
#else
|
||||
if (!sys.FileSystem.exists(targetPath) || !sys.FileSystem.isDirectory(targetPath)) throw 'openFolder should only be used to open existing folders.';
|
||||
#if FEATURE_OPEN_URL
|
||||
#if windows
|
||||
Sys.command('explorer', [targetPath.replace('/', '\\')]);
|
||||
#elseif mac
|
||||
Sys.command('open', [targetPath]);
|
||||
#elseif linux
|
||||
Sys.command('open', [targetPath]);
|
||||
#end
|
||||
#else
|
||||
throw 'Cannot open URLs on this platform.';
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs platform-specific code to open a file explorer and select a specific file.
|
||||
* @param targetPath The path of the file to select.
|
||||
*/
|
||||
public static function openSelectFile(targetPath:String):Void
|
||||
{
|
||||
#if html5
|
||||
throw 'Cannot open URLs on this platform.';
|
||||
#else
|
||||
#if FEATURE_OPEN_URL
|
||||
#if windows
|
||||
Sys.command('explorer', ['/select,' + targetPath.replace('/', '\\')]);
|
||||
#elseif mac
|
||||
Sys.command('open', ['-R', targetPath]);
|
||||
#elseif linux
|
||||
// TODO: unsure of the linux equivalent to opening a folder and then "selecting" a file.
|
||||
Sys.command('open', [targetPath]);
|
||||
#end
|
||||
#else
|
||||
throw 'Cannot open URLs on this platform.';
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatched when the game window is closed.
|
||||
*/
|
||||
|
||||
@@ -31,4 +31,11 @@ class VolumePlugin extends FlxBasic
|
||||
else if (PlayerSettings.player1.controls.VOLUME_DOWN) FlxG.sound.changeVolume(-0.1);
|
||||
}
|
||||
}
|
||||
|
||||
override public function destroy():Void
|
||||
{
|
||||
if (FlxG.plugins.list.contains(this)) FlxG.plugins.remove(this);
|
||||
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user