Created
October 6, 2024 09:33
-
-
Save jakubtomsu/c7ae9f9a222b31f52cb5e6f35f04d226 to your computer and use it in GitHub Desktop.
Very simple parser for quake .map readable level data format (Valve version). I didn't fully test it.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package qmap | |
import "core:fmt" | |
import "core:os" | |
import "core:strconv" | |
import "core:strings" | |
// https://developer.valvesoftware.com/wiki/MAP_(file_format) | |
File :: struct { | |
version: int, | |
wad: string, | |
entities: [dynamic]Entity, | |
} | |
Entity :: struct { | |
classname: string, | |
values: map[string]string, | |
brushes: [dynamic]Brush, | |
} | |
Brush :: struct { | |
planes: [dynamic]Plane, | |
} | |
Plane :: struct { | |
points: [3]Vec3, | |
texture: string, | |
axis: [2]Vec3, | |
offset: [2]f32, | |
rotation: f32, | |
scale: [2]f32, | |
} | |
Vec3 :: [3]f32 | |
Color :: [3]f32 | |
Version :: enum { | |
Invalid, | |
Quake1, | |
Valve220, | |
} | |
version_from_int :: proc(v: int) -> Version { | |
switch v { | |
case 220: | |
return .Valve220 | |
case: | |
return .Invalid | |
} | |
} | |
load :: proc(file_name: string, allocator := context.allocator) -> (result: File, ok: bool) { | |
context.allocator = allocator | |
data := os.read_entire_file_from_filename(file_name) or_return | |
return parse(string(data)), true | |
} | |
Parser :: struct { | |
iter: string, | |
} | |
parse :: proc(data: string, allocator := context.allocator) -> (result: File) { | |
context.allocator = allocator | |
entity: Entity | |
parser := Parser { | |
iter = data, | |
} | |
for line in strings.split_lines_iterator(&parser.iter) { | |
switch line[0] { | |
case '/': | |
continue | |
case '{': | |
entity := parse_entity(&parser) | |
append(&result.entities, entity) | |
} | |
} | |
return result | |
} | |
parse_entity :: proc(parser: ^Parser) -> (result: Entity) { | |
for line in strings.split_lines_iterator(&parser.iter) { | |
switch line[0] { | |
case '/': | |
continue | |
case '{': | |
brush := parse_brush(parser) | |
append(&result.brushes, brush) | |
case '}': | |
break | |
case '"': | |
temp := line[1:] | |
key_end_index := strings.index(temp, "\"") | |
assert(key_end_index > 0) | |
key := temp[:key_end_index] | |
val := temp[key_end_index + 1:] | |
val = strings.trim_left_space(val) | |
assert(val[0] == '\"' && val[len(val) - 1] == '\"') | |
val = val[1:][:len(val) - 2] | |
result.values[key] = val | |
} | |
} | |
return result | |
} | |
parse_brush :: proc(parser: ^Parser) -> (result: Brush) { | |
for line in strings.split_lines_iterator(&parser.iter) { | |
switch line[0] { | |
case '/': | |
continue | |
case '}': | |
break | |
case '(': | |
plane: Plane | |
temp := line | |
for i in 0 ..< 3 { | |
assert(temp[0] == '(') | |
temp = strings.trim_left_space(temp[1:]) | |
for j in 0 ..< 3 { | |
val, num, _ := strconv.parse_f32_prefix(temp) | |
plane.points[i][j] = val | |
temp = temp[num + 1:] | |
} | |
assert(temp[0] == ')') | |
assert(temp[1] == ' ') | |
temp = temp[2:] | |
} | |
plane.texture = split_first(temp) | |
temp = temp[len(plane.texture) + 1:] | |
if temp[0] == '[' { | |
for i in 0 ..< 2 { | |
assert(temp[0] == '[') | |
assert(temp[1] == ' ') | |
temp = temp[2:] | |
for j in 0 ..< 4 { | |
val, num, _ := strconv.parse_f32_prefix(temp) | |
if j == 3 { | |
plane.offset[i] = val | |
} else { | |
plane.axis[i][j] = val | |
} | |
temp = temp[num + 1:] | |
} | |
assert(temp[0] == ']') | |
assert(temp[1] == ' ') | |
temp = temp[2:] | |
} | |
} else { | |
unimplemented() | |
} | |
val: f32 | |
num: int | |
val, num, _ = strconv.parse_f32_prefix(temp) | |
plane.rotation = val | |
temp = temp[num + 1:] | |
val, num, _ = strconv.parse_f32_prefix(temp) | |
plane.scale[0] = val | |
temp = temp[num + 1:] | |
val, num, _ = strconv.parse_f32_prefix(temp) | |
plane.scale[1] = val | |
append(&result.planes, plane) | |
} | |
} | |
return result | |
} | |
split_first :: proc(s: string, sep: rune = ' ') -> string { | |
for r, i in s { | |
if r == sep { | |
return s[:i] | |
} | |
} | |
return s | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment