Moving level loading into the package
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:wolf_3d_data/src/classes/wolf_level.dart';
|
||||
|
||||
import 'classes/sprite.dart';
|
||||
|
||||
class WLParser {
|
||||
@@ -110,6 +113,153 @@ class WLParser {
|
||||
cmdOffset += 6; // Move to the next 6-byte instruction
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses MAPHEAD and GAMEMAPS to extract the raw level data.
|
||||
static List<WolfLevel> parseMaps(
|
||||
ByteData mapHead,
|
||||
ByteData gameMaps, {
|
||||
bool isShareware = true,
|
||||
}) {
|
||||
List<WolfLevel> levels = [];
|
||||
int rlewTag = mapHead.getUint16(0, Endian.little);
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
int mapOffset = mapHead.getUint32(2 + (i * 4), Endian.little);
|
||||
if (mapOffset == 0) continue;
|
||||
|
||||
int plane0Offset = gameMaps.getUint32(mapOffset + 0, Endian.little);
|
||||
int plane1Offset = gameMaps.getUint32(mapOffset + 4, Endian.little);
|
||||
|
||||
int plane0Length = gameMaps.getUint16(mapOffset + 12, Endian.little);
|
||||
int plane1Length = gameMaps.getUint16(mapOffset + 14, Endian.little);
|
||||
|
||||
List<int> nameBytes = [];
|
||||
for (int n = 0; n < 16; n++) {
|
||||
int charCode = gameMaps.getUint8(mapOffset + 22 + n);
|
||||
if (charCode == 0) break;
|
||||
nameBytes.add(charCode);
|
||||
}
|
||||
String name = ascii.decode(nameBytes);
|
||||
|
||||
// --- DECOMPRESS PLANES ---
|
||||
final compressedWallData = gameMaps.buffer.asUint8List(
|
||||
plane0Offset,
|
||||
plane0Length,
|
||||
);
|
||||
Uint16List carmackExpandedWalls = _expandCarmack(compressedWallData);
|
||||
List<int> flatWallGrid = _expandRlew(carmackExpandedWalls, rlewTag);
|
||||
|
||||
final compressedObjectData = gameMaps.buffer.asUint8List(
|
||||
plane1Offset,
|
||||
plane1Length,
|
||||
);
|
||||
Uint16List carmackExpandedObjects = _expandCarmack(compressedObjectData);
|
||||
List<int> flatObjectGrid = _expandRlew(carmackExpandedObjects, rlewTag);
|
||||
|
||||
// --- BUILD GRIDS ---
|
||||
List<List<int>> wallGrid = [];
|
||||
List<List<int>> objectGrid = [];
|
||||
|
||||
for (int y = 0; y < 64; y++) {
|
||||
List<int> wallRow = [];
|
||||
List<int> objectRow = [];
|
||||
for (int x = 0; x < 64; x++) {
|
||||
wallRow.add(flatWallGrid[y * 64 + x]);
|
||||
objectRow.add(flatObjectGrid[y * 64 + x]);
|
||||
}
|
||||
wallGrid.add(wallRow);
|
||||
objectGrid.add(objectRow);
|
||||
}
|
||||
|
||||
levels.add(
|
||||
WolfLevel(
|
||||
name: name,
|
||||
wallGrid: wallGrid,
|
||||
objectGrid: objectGrid,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return levels;
|
||||
}
|
||||
|
||||
// --- ALGORITHM 1: CARMACK EXPANSION ---
|
||||
static Uint16List _expandCarmack(Uint8List compressed) {
|
||||
ByteData data = ByteData.sublistView(compressed);
|
||||
|
||||
// The first 16-bit word is the total length of the expanded data in BYTES.
|
||||
int expandedLengthBytes = data.getUint16(0, Endian.little);
|
||||
int expandedLengthWords = expandedLengthBytes ~/ 2;
|
||||
Uint16List expanded = Uint16List(expandedLengthWords);
|
||||
|
||||
int inIdx = 2; // Skip the length word we just read
|
||||
int outIdx = 0;
|
||||
|
||||
while (outIdx < expandedLengthWords && inIdx < compressed.length) {
|
||||
int word = data.getUint16(inIdx, Endian.little);
|
||||
inIdx += 2;
|
||||
|
||||
int highByte = word >> 8;
|
||||
int lowByte = word & 0xFF;
|
||||
|
||||
// 0xA7 and 0xA8 are the Carmack Pointer Tags
|
||||
if (highByte == 0xA7 || highByte == 0xA8) {
|
||||
if (lowByte == 0) {
|
||||
// Exception Rule: If the length (lowByte) is 0, it's not a pointer.
|
||||
// It's literally just the tag byte followed by another byte.
|
||||
int nextByte = data.getUint8(inIdx++);
|
||||
expanded[outIdx++] = (nextByte << 8) | highByte;
|
||||
} else if (highByte == 0xA7) {
|
||||
// 0xA7 = Near Pointer (look back a few spaces)
|
||||
int offset = data.getUint8(inIdx++);
|
||||
int copyFrom = outIdx - offset;
|
||||
for (int i = 0; i < lowByte; i++) {
|
||||
expanded[outIdx++] = expanded[copyFrom++];
|
||||
}
|
||||
} else if (highByte == 0xA8) {
|
||||
// 0xA8 = Far Pointer (absolute offset from the very beginning)
|
||||
int offset = data.getUint16(inIdx, Endian.little);
|
||||
inIdx += 2;
|
||||
for (int i = 0; i < lowByte; i++) {
|
||||
expanded[outIdx++] = expanded[offset++];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Normal, uncompressed word
|
||||
expanded[outIdx++] = word;
|
||||
}
|
||||
}
|
||||
return expanded;
|
||||
}
|
||||
|
||||
// --- ALGORITHM 2: RLEW EXPANSION ---
|
||||
static List<int> _expandRlew(Uint16List carmackExpanded, int rlewTag) {
|
||||
// The first word is the expanded length in BYTES
|
||||
int expandedLengthBytes = carmackExpanded[0];
|
||||
int expandedLengthWords = expandedLengthBytes ~/ 2;
|
||||
List<int> rlewExpanded = List<int>.filled(expandedLengthWords, 0);
|
||||
|
||||
int inIdx = 1; // Skip the length word
|
||||
int outIdx = 0;
|
||||
|
||||
while (outIdx < expandedLengthWords && inIdx < carmackExpanded.length) {
|
||||
int word = carmackExpanded[inIdx++];
|
||||
|
||||
if (word == rlewTag) {
|
||||
// We found an RLEW tag!
|
||||
// The next word is the count, the word after that is the value.
|
||||
int count = carmackExpanded[inIdx++];
|
||||
int value = carmackExpanded[inIdx++];
|
||||
for (int i = 0; i < count; i++) {
|
||||
rlewExpanded[outIdx++] = value;
|
||||
}
|
||||
} else {
|
||||
// Normal word
|
||||
rlewExpanded[outIdx++] = word;
|
||||
}
|
||||
}
|
||||
return rlewExpanded;
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper class to parse and store redundant VSWAP header data
|
||||
|
||||
Reference in New Issue
Block a user