This commit is contained in:
PurSnake
2025-10-08 17:42:18 -04:00
committed by Eric
parent 204dae213c
commit a7fc70a5c9
2 changed files with 218 additions and 0 deletions
+5
View File
@@ -104,6 +104,11 @@ class Paths
return getPath('data/$key.json', TEXT, library);
}
public static function srt(key:String, ?library:String, ?directory:String = "data/"):String
{
return getPath('$directory$key.srt', TEXT, library);
}
public static function sound(key:String, ?library:String):String
{
return getPath('sounds/$key.${Constants.EXT_SOUND}', SOUND, library);
+213
View File
@@ -0,0 +1,213 @@
package funkin.util;
import flixel.math.FlxMath;
import flixel.util.FlxStringUtil;
using StringTools;
class SubtitleEntry
{
public var id:Int;
public var start:Float; // ms
public var end:Float; // ms
public var text:String;
public function new(number:Int = 0, start:Float = 0, end:Float = 0, text:String = "")
{
this.id = number;
this.start = start;
this.end = end;
this.text = text;
}
public function toString():String
{
return FlxStringUtil.getDebugString([
LabelValuePair.weak("index", id),
LabelValuePair.weak("range", [start, end]),
LabelValuePair.weak("text", text)
]);
}
}
class SRTParser
{
/**
* Parse SRT content from a raw string
*/
public static function parseFromString(s:String):Array<SubtitleEntry>
{
if (s == null) return [];
// normalize newlines
var normalized = s.replace("\r\n", "\n").replace("\r", "\n");
// strip BOM if present (this fixes the case when the very first line contains invisible U+FEFF)
if (normalized.length > 0 && normalized.charCodeAt(0) == 0xFEFF)
normalized = normalized.substr(1);
// split by double newlines
final blocks = normalized.split("\n\n");
final out:Array<SubtitleEntry> = [];
var _timmed = null;
var _lines = null;
for (block in blocks)
{
_timmed = block.trim();
if (_timmed.length == 0) continue;
_lines = _timmed.split("\n");
var idx = 0;
var number = 0;
var timeLine:String = null;
if (_lines.length > 0 && SubtitleUtils.isNumeric(_lines[0]))
{
number = Std.parseInt(_lines[0]);
idx = 1;
}
if (idx < _lines.length)
{
timeLine = _lines[idx];
idx++;
}
if (timeLine == null) continue;
var times = SRTParser.parseTimeLine(timeLine);
if (times == null) continue;
var textLines = _lines.slice(idx, _lines.length);
var text = textLines.join("\n");
out.push(new SubtitleEntry(number, times.start, times.end, text));
_lines = null;
}
out.sort(SubtitleUtils.sortLines);
return out;
}
public static function parseFromFile(name:String, ?library:String, ?dir:String):Array<SubtitleEntry>
{
var rawSRTData:String = "";
try
rawSRTData = funkin.Assets.getText(Paths.srt(name, library, dir))
catch (e)
trace(e);
return SRTParser.parseFromString(rawSRTData);
}
/**
* Parse line like: 00:01:23,456 --> 00:01:25,678
*/
static final timeArrowPatterns = ["-->", "->", "—>", "–>"];
public static function parseTimeLine(line:String):{start:Float, end:Float}
{
var left:String = null;
var right:String = null;
for (a in timeArrowPatterns)
{
var parts = line.split(a);
if (parts.length == 2)
{
left = parts[0].trim();
right = parts[1].trim();
break;
}
}
if (left == null || right == null) return null;
var s = parseTimecode(left);
var e = parseTimecode(right);
if (s < 0 || e < 0) return null;
return {start: s, end: e};
}
/**
* Parse a single timecode like 01:02:03,456 or 01:02:03.456
*/
public static function parseTimecode(t:String):Float
{
var clean = t.trim().replace(",", ".");
var parts = clean.split(":");
if (parts.length != 3) throw "Invalid timecode: " + t;
var hh = Std.parseInt(parts[0]);
var mm = Std.parseInt(parts[1]);
var ssf = parts[2];
var secParts = ssf.split(".");
var ss = Std.parseInt(secParts[0]);
var ms = 0;
if (secParts.length > 1)
{
var frac = secParts[1];
if (frac.length > 3) frac = frac.substr(0, 3);
while (frac.length < 3)
frac += "0";
ms = Std.parseInt(frac);
}
return (hh * 3600 + mm * 60 + ss + (ms / 1000.0)) * 1000;
}
}
/**
* Utility helpers for using subtitles at runtime
*/
class SubtitleUtils
{
/**
* Finds subtitle entry for the current time.
*/
public static function findActive(list:Array<SubtitleEntry>, time:Float):SubtitleEntry
{
for (s in list)
{
if (time >= s.start && time <= s.end) return s;
}
return null;
}
/**
* Finds subtitle entry id for the current time.
*/
public static function findActiveIndex(list:Array<SubtitleEntry>, time:Float):Int
{
var lo = 0;
var hi = list.length - 1;
while (lo <= hi)
{
var mid = (lo + hi) >> 1;
var s = list[mid];
if (time < s.start) hi = mid - 1;
else if (time > s.end) lo = mid + 1;
else
return mid;
}
return -1;
}
public static function isNumeric(s:String):Bool
{
if (s == null) return false;
var t = s.trim();
if (t.length == 0) return false;
for (i in 0...t.length)
{
var code = t.charCodeAt(i);
if (code < '0'.code || code > '9'.code) return false;
}
return true;
}
/**
* Sort lines by their id.
*/
public static function sortLines(line1:SubtitleEntry, line2:SubtitleEntry):Int
{
return FlxMath.numericComparison(line1.id, line2.id);
}
}