% EGS: Erlang Game Server % Copyright (C) 2010 Loic Hoguin % % This file is part of EGS. % % EGS is free software: you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by % the Free Software Foundation, either version 3 of the License, or % (at your option) any later version. % % EGS is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details. % % You should have received a copy of the GNU General Public License % along with EGS. If not, see . -module(psu_parser). -export([run/0]). -include("include/maps.hrl"). -define(NBL, "./nbl"). run() -> List = [parse_zone(QuestID, Filename) || {[QuestID, _ZoneID], [{file, Filename}]} <- ?ZONES, QuestID < 1100000], Begin = "%% This file is automatically generated by EGS. %% Please do not edit it manually, as you would risk losing your changes. -define(MISSIONS, ", End = "). ", Missions = io_lib:format("~s~p~s", [Begin, List, End]), file:write_file("include/missions.hrl", Missions). parse_zone(QuestID, NblFilename) -> Files = nbl_list_files(NblFilename), log("~p", [Files]), nbl_extract_files(NblFilename), Filename = "set_r3.rel", BasePtr = calc_base_ptr(Filename, Files, 0), {ok, << $N, $X, $R, 0, EndRelPtr:32/little-unsigned-integer, AreaIDListRelPtr:32/little-unsigned-integer, 0:32, Data/bits >>} = file:read_file(io_lib:format("tmp/~s", [Filename])), log("header: end ptr(~b) areaid list ptr(~b)", [EndRelPtr, AreaIDListRelPtr]), {ok, _AreaCode, NbMaps, MapsListPtr} = parse_areaid_list(Data, AreaIDListRelPtr - 16), MapList = parse_mapnumbers_list(Data, NbMaps, MapsListPtr - BasePtr - 16), ObjList = {QuestID, [{MapID, parse_object_list_headers(BasePtr, Data, NbHeaders, ObjListHeadersPtr - BasePtr - 16)} || {MapID, NbHeaders, ObjListHeadersPtr} <- MapList]}, nbl_cleanup(), ObjList. nbl_list_files(NblFilename) -> StdOut = os:cmd(io_lib:format("~s -t ~s", [?NBL, NblFilename])), re:split(StdOut, "\n", [{return, list}]). nbl_extract_files(NblFilename) -> filelib:ensure_dir("tmp/"), os:cmd(io_lib:format("~s -o tmp/ ~s", [?NBL, NblFilename])), ok. nbl_cleanup() -> os:cmd("rm -rf tmp/"), ok. calc_base_ptr(Filename, [Current|_Tail], Ptr) when Filename =:= Current -> Ptr; calc_base_ptr(Filename, [Current|Tail], Ptr) -> FileSize = filelib:file_size(io_lib:format("tmp/~s", [Current])), RoundedSize = case FileSize rem 32 of 0 -> FileSize; _ -> 32 * (1 + (FileSize div 32)) end, calc_base_ptr(Filename, Tail, Ptr + RoundedSize). parse_areaid_list(Data, Ptr) -> Bits = Ptr * 8, << _:Bits/bits, AreaCode:16/little-unsigned-integer, NbMaps:16/little-unsigned-integer, MapsListPtr:32/little-unsigned-integer, _/bits >> = Data, log("areaid list: area code(~b) nb maps(~b) maps list ptr(~b)", [AreaCode, NbMaps, MapsListPtr]), {ok, AreaCode, NbMaps, MapsListPtr}. parse_mapnumbers_list(Data, NbMaps, Ptr) -> IgnoredBits = Ptr * 8, MapBits = NbMaps * 12 * 8, << _:IgnoredBits/bits, MapList:MapBits/bits, _/bits >> = Data, parse_mapnumbers_list_rec(MapList, NbMaps, []). parse_mapnumbers_list_rec(_Data, 0, Acc) -> lists:reverse(Acc); parse_mapnumbers_list_rec(Data, NbMaps, Acc) -> << MapID:16/little-unsigned-integer, NbHeaders:16/little-unsigned-integer, ObjListHeadersPtr:32/little-unsigned-integer, 0:32, Rest/bits >> = Data, log("mapnumbers list: mapid(~b) nbheaders(~b) object headers ptr(~b)", [MapID, NbHeaders, ObjListHeadersPtr]), parse_mapnumbers_list_rec(Rest, NbMaps - 1, [{MapID, NbHeaders, ObjListHeadersPtr}|Acc]). parse_object_list_headers(BasePtr, Data, NbHeaders, Ptr) -> Bits = Ptr * 8, << _:Bits/bits, Rest/bits >> = Data, List = parse_object_list_headers_rec(Rest, NbHeaders, []), [parse_object_list(BasePtr, Data, NbObjects, ObjListPtr - BasePtr - 16) || {_ObjListNumber, NbObjects, ObjListPtr} <- List]. parse_object_list_headers_rec(_Data, 0, Acc) -> lists:reverse(Acc); parse_object_list_headers_rec(Data, NbHeaders, Acc) -> << 16#ffffffff:32, UnknownBinary:144/bits, 16#ffff:16, 0:32, ObjListNumber:16/little-unsigned-integer, 0:32, NbObjects:16/little-unsigned-integer, ObjListPtr:32/little-unsigned-integer, Rest/bits >> = Data, log("object list headers: bin(~p) list nb(~b) nb obj(~b) obj list ptr(~b)", [UnknownBinary, ObjListNumber, NbObjects, ObjListPtr]), parse_object_list_headers_rec(Rest, NbHeaders - 1, [{ObjListNumber, NbObjects, ObjListPtr}|Acc]). parse_object_list(BasePtr, Data, NbObjects, Ptr) -> Bits = Ptr * 8, << _:Bits/bits, Rest/bits >> = Data, List = parse_object_list_rec(Rest, NbObjects, []), [parse_object_args(ObjType, Params, Data, ArgPtr - BasePtr - 16) || {ObjType, Params, _ArgSize, ArgPtr} <- List]. parse_object_list_rec(_Data, 0, Acc) -> lists:reverse(Acc); parse_object_list_rec(Data, NbObjects, Acc) -> % todo x z y ?? << 16#ffffffff:32, UnknownA:32/little-unsigned-integer, 16#ffffffff:32, 16#ffff:16, ObjType:16/little-unsigned-integer, 0:32, PosX:32/little-float, PosY:32/little-float, PosZ:32/little-float, RotX:32/little-float, RotY:32/little-float, RotZ:32/little-float, ArgSize:32/little-unsigned-integer, ArgPtr:32/little-unsigned-integer, Rest/bits >> = Data, log("object entry: a(~b) nb(~b) pos[x(~p) y(~p) z(~p)] rot[x(~p) y(~p) z(~p)] argsize(~b) argptr(~b)", [UnknownA, ObjType, PosX, PosY, PosZ, RotX, RotY, RotZ, ArgSize, ArgPtr]), parse_object_list_rec(Rest, NbObjects - 1, [{ObjType, {params, {pos, PosX, PosY, PosZ}, {rot, RotX, RotY, RotZ}}, ArgSize, ArgPtr}|Acc]). parse_object_args(ObjType, Params, Data, Ptr) -> Bits = Ptr * 8, << _:Bits/bits, Rest/bits >> = Data, parse_object_args(ObjType, Params, Rest). parse_object_args(4, _Params, _Data) -> static_model; parse_object_args(10, _Params, _Data) -> invisible_block; parse_object_args(12, _Params, Data) -> << Model:16/little-unsigned-integer, UnknownA:16/little-unsigned-integer, UnknownB:32/little-unsigned-integer, UnknownC:16/little-unsigned-integer, Scale:16/little-unsigned-integer, UnknownD:16/little-unsigned-integer, 16#ff00:16, UnknownE:16/little-unsigned-integer, UnknownF:16/little-unsigned-integer, 16#ffffffff:32, 16#ffffffff:32, 16#ffffffff:32, RawTrigEvent:16/little-unsigned-integer, 16#ffffffff:32, 16#ffffffff:32, 16#ffffffff:32, 16#ffffffff:32, 16#ffffffff:32, 16#ffffffff:32, 16#ffffffff:32, 16#ffffffff:32, 0:16, _/bits >> = Data, Breakable = case UnknownB of 0 -> false; 1 -> true end, TrigEvent = convert_eventid(RawTrigEvent), log("box: model(~b) a(~b) breakable(~p) c(~b) scale(~b) d(~b) e(~b) f(~b) trigevent(~p)", [Model, UnknownA, Breakable, UnknownC, Scale, UnknownD, UnknownE, UnknownF, TrigEvent]), {box, Model, Breakable, TrigEvent}; parse_object_args(14, {params, {pos, PosX, PosY, PosZ}, _Rot}, Data) -> << _:96, DiffX:32/little-float, DiffY:32/little-float, DiffZ:32/little-float, DestDir:32/little-float, _/bits >> = Data, log("warp: diffpos[x(~p) y(~p) z(~p)] destdir(~p)", [DiffX, DiffY, DiffZ, DestDir]), {warp, PosX + DiffX, PosY + DiffY, PosZ + DiffZ, DestDir}; parse_object_args(17, _Params, _Data) -> fence; parse_object_args(20, _Params, _Data) -> door; parse_object_args(22, _Params, Data) -> << UnknownA:8, 0, KeySet:8, 1, 0:16, UnknownB:8, 0, 0:16, UnknownC:16/little-unsigned-integer, RawReqKey1Event:16/little-unsigned-integer, RawReqKey2Event:16/little-unsigned-integer, RawReqKey3Event:16/little-unsigned-integer, RawReqKey4Event:16/little-unsigned-integer, 16#ffffffff:32, 16#ffffffff:32, RawTrigEvent:16/little-unsigned-integer, UnknownD:16/little-unsigned-integer, 16#ffffffff:32, 16#ffffffff:32, 16#ffffffff:32, _/bits >> = Data, ReqKeyEvents = [convert_eventid(RawReqKey1Event), convert_eventid(RawReqKey2Event), convert_eventid(RawReqKey3Event), convert_eventid(RawReqKey4Event)], TrigEvent = convert_eventid(RawTrigEvent), log("key_console: a(~b) keyset(~b) b(~b) c(~b) reqkeyevents(~p) trigevent(~p) d(~b)", [UnknownA, KeySet, UnknownB, UnknownC, ReqKeyEvents, TrigEvent, UnknownD]), {key_console, KeySet, ReqKeyEvents, TrigEvent}; %% @todo Find out what this object number is. parse_object_args(23, _Params, _Data) -> unknown_object; parse_object_args(24, _Params, Data) -> %% @todo return meaningful information << _:704, UnknownA:32/little-unsigned-integer, RawTrigEvent:16/little-unsigned-integer, RawReqEvent:16/little-unsigned-integer, 16#ffff:16, UnknownB:8, SpawnNb:8, _/bits >> = Data, TrigEvent = convert_eventid(RawTrigEvent), ReqEvent = convert_eventid(RawReqEvent), log("spawn: a(~b) trigevent(~p) reqevent(~p) b(~b) spawnnb(~b)", [UnknownA, TrigEvent, ReqEvent, UnknownB, SpawnNb]), {'spawn', TrigEvent, ReqEvent}; parse_object_args(26, _Params, _Data) -> entrance; parse_object_args(27, _Params, _Data) -> 'exit'; parse_object_args(31, _Params, Data) -> << KeySet:8, UnknownB:8, 16#0001:16, 16#ffff:16, RawTrigEvent:16/little-unsigned-integer, RawReqEvent:16/little-unsigned-integer, 16#ffff:16, 16#ffffffff:32, 16#ffffffff:32, 16#ffffffff:32, _/bits >> = Data, TrigEvent = convert_eventid(RawTrigEvent), ReqEvent = convert_eventid(RawReqEvent), log("key: keyset(~b) b(~b) trigevent(~p) reqevent(~p)", [KeySet, UnknownB, TrigEvent, ReqEvent]), {key, KeySet, TrigEvent, ReqEvent}; parse_object_args(35, _Params, _Data) -> boss; parse_object_args(49, _Params, _Data) -> crystal; parse_object_args(50, _Params, _Data) -> healing_pad; parse_object_args(53, _Params, _Data) -> label; parse_object_args(62, _Params, _Data) -> pp_cube; parse_object_args(64, _Params, _Data) -> hidden_minimap_section. convert_eventid(16#ffff) -> false; convert_eventid(RawEventID) -> RawEventID. %% @doc Log message to the console. log(_Message) -> ok. %~ io:format("~s~n", [_Message]). log(Message, Format) -> FormattedMessage = io_lib:format(Message, Format), log(FormattedMessage).