135 lines
3.7 KiB
PHP
135 lines
3.7 KiB
PHP
<?php
|
|
|
|
use Doom\Texture\TextureResolver;
|
|
use Doom\Wad\File;
|
|
use Doom\Wad\Types\Lump;
|
|
|
|
function s16(int $value): string
|
|
{
|
|
return pack('v', $value & 0xFFFF);
|
|
}
|
|
|
|
/**
|
|
* @param array<int, array{originX:int, originY:int, patch:int, stepDir:int, colorMap:int}> $patches
|
|
*/
|
|
function textureRecord(string $name, bool $masked, int $width, int $height, array $patches): string
|
|
{
|
|
$data = pack('a8VvvVv', $name, $masked ? 1 : 0, $width, $height, 0, count($patches));
|
|
|
|
foreach ($patches as $patch) {
|
|
$data .= s16($patch['originX']);
|
|
$data .= s16($patch['originY']);
|
|
$data .= pack('v', $patch['patch']);
|
|
$data .= pack('v', $patch['stepDir']);
|
|
$data .= pack('v', $patch['colorMap']);
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* @param string[] $records
|
|
*/
|
|
function textureXData(array $records): string
|
|
{
|
|
$count = count($records);
|
|
$offset = 4 + ($count * 4);
|
|
$offsets = [];
|
|
$body = '';
|
|
|
|
foreach ($records as $record) {
|
|
$offsets[] = $offset;
|
|
$body .= $record;
|
|
$offset += strlen($record);
|
|
}
|
|
|
|
$data = pack('V', $count);
|
|
foreach ($offsets as $itemOffset) {
|
|
$data .= pack('V', $itemOffset);
|
|
}
|
|
|
|
return $data . $body;
|
|
}
|
|
|
|
/**
|
|
* @param string[] $names
|
|
*/
|
|
function pnamesData(array $names): string
|
|
{
|
|
$data = pack('V', count($names));
|
|
foreach ($names as $name) {
|
|
$data .= pack('a8', $name);
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
function appendLump(File $wad, string $name, string $data): void
|
|
{
|
|
$index = $wad->lumps->count();
|
|
|
|
$wad->lumps->push(new Lump(
|
|
offset: 0,
|
|
size: strlen($data),
|
|
name: strtoupper($name),
|
|
data: $data,
|
|
index: $index,
|
|
));
|
|
}
|
|
|
|
test('resolves textures using PNAMES and patch lumps', function (): void {
|
|
$wad = new File();
|
|
|
|
appendLump($wad, 'PNAMES', pnamesData(['PATCHA', 'PATCHB']));
|
|
|
|
appendLump($wad, 'TEXTURE1', textureXData([
|
|
textureRecord('TEXA', false, 64, 64, [
|
|
['originX' => 0, 'originY' => 0, 'patch' => 0, 'stepDir' => 0, 'colorMap' => 0],
|
|
]),
|
|
textureRecord('TEXB', false, 32, 16, [
|
|
['originX' => 8, 'originY' => -2, 'patch' => 0, 'stepDir' => 0, 'colorMap' => 0],
|
|
]),
|
|
]));
|
|
|
|
appendLump($wad, 'TEXTURE2', textureXData([
|
|
textureRecord('TEXA', true, 128, 32, [
|
|
['originX' => 4, 'originY' => 5, 'patch' => 1, 'stepDir' => 0, 'colorMap' => 0],
|
|
['originX' => 0, 'originY' => 0, 'patch' => 5, 'stepDir' => 0, 'colorMap' => 0],
|
|
]),
|
|
]));
|
|
|
|
appendLump($wad, 'PATCHA', '');
|
|
|
|
$resolved = $wad->resolveTextures();
|
|
|
|
expect($resolved)->toHaveCount(2);
|
|
expect($resolved)->toHaveKey('TEXA');
|
|
expect($resolved)->toHaveKey('TEXB');
|
|
|
|
$texA = $resolved['TEXA'];
|
|
expect($texA->masked)->toBeTrue();
|
|
expect($texA->width)->toBe(128);
|
|
expect($texA->height)->toBe(32);
|
|
expect($texA->patches)->toHaveCount(2);
|
|
expect($texA->patches[0]->patchName)->toBe('PATCHB');
|
|
expect($texA->patches[0]->lumpIndex)->toBeNull();
|
|
expect($texA->patches[0]->isResolved())->toBeFalse();
|
|
expect($texA->patches[1]->patchName)->toBeNull();
|
|
|
|
$texB = TextureResolver::forWad($wad)->resolveByName('texb');
|
|
expect($texB)->not->toBeNull();
|
|
expect($texB->patches[0]->patchName)->toBe('PATCHA');
|
|
expect($texB->patches[0]->lumpIndex)->toBe(3);
|
|
expect($texB->patches[0]->isResolved())->toBeTrue();
|
|
});
|
|
|
|
test('throws when PNAMES lump is missing', function (): void {
|
|
$wad = new File();
|
|
appendLump($wad, 'TEXTURE1', textureXData([
|
|
textureRecord('TEXA', false, 16, 16, [
|
|
['originX' => 0, 'originY' => 0, 'patch' => 0, 'stepDir' => 0, 'colorMap' => 0],
|
|
]),
|
|
]));
|
|
|
|
expect(fn () => $wad->resolveTextures())->toThrow(RuntimeException::class);
|
|
});
|