egs/src/psu_parser.erl

205 lines
8.4 KiB
Erlang
Raw Normal View History

% 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 <http://www.gnu.org/licenses/>.
-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 =< 1000024],
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, lists:flatten(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, 0:16, UnknownA:16/little-unsigned-integer, 0:80, UnknownB:32/little-unsigned-integer, 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: a(~b) b(~b) list nb(~b) nb obj(~b) obj list ptr(~b)", [UnknownA, UnknownB, 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, Data, ArgPtr - BasePtr - 16) || {ObjType, _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, Unknown:32/little-unsigned-integer, 16#ffffffff:32, 16#ffff:16, ObjType:16/little-unsigned-integer, 0:32,
_PosX:32, _PosZ:32, _PosY:32, _RotX:32, _RotZ:32, _RotY:32, ArgSize:32/little-unsigned-integer, ArgPtr:32/little-unsigned-integer, Rest/bits >> = Data,
log("object entry: unknown(~b) nb(~b) argsize(~b) argptr(~b)", [Unknown, ObjType, ArgSize, ArgPtr]),
parse_object_list_rec(Rest, NbObjects - 1, [{ObjType, ArgSize, ArgPtr}|Acc]).
parse_object_args(ObjType, Data, Ptr) ->
Bits = Ptr * 8,
<< _:Bits/bits, Rest/bits >> = Data,
parse_object_args(ObjType, Rest).
parse_object_args(4, _Data) ->
static_model;
parse_object_args(10, _Data) ->
invisible_block;
parse_object_args(12, 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,
16#0000ff00:32, 16#ffffffff:32, 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) trigevent(~p)", [Model, UnknownA, Breakable, UnknownC, Scale, TrigEvent]),
{box, Model, Breakable, TrigEvent};
parse_object_args(17, _Data) ->
fence;
parse_object_args(20, _Data) ->
door;
parse_object_args(22, Data) ->
<< 16#00000101:32, 16#00000100:32, 0:16, UnknownA:16/little-unsigned-integer, RawNoKeyEvent:16/little-unsigned-integer, 16#ffff:16,
16#ffffffff:32, 16#ffffffff:32, 16#ffffffff:32, RawTrigEvent:16/little-unsigned-integer, UnknownB:16/little-unsigned-integer,
16#ffffffff:32, 16#ffffffff:32, 16#ffffffff:32, _/bits >> = Data,
NoKeyEvent = convert_eventid(RawNoKeyEvent),
TrigEvent = convert_eventid(RawTrigEvent),
log("key_console: a(~b) nokeyevent(~p) trigevent(~p) b(~b)", [UnknownA, NoKeyEvent, TrigEvent, UnknownB]),
{key_console, NoKeyEvent, TrigEvent};
parse_object_args(24, 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, _Data) ->
entrance;
parse_object_args(27, _Data) ->
'exit';
parse_object_args(31, Data) ->
<< 16#01000001:32, 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: trigevent(~p) reqevent(~p)", [TrigEvent, ReqEvent]),
{key, TrigEvent, ReqEvent};
parse_object_args(35, _Data) ->
boss;
parse_object_args(49, _Data) ->
crystal;
parse_object_args(53, _Data) ->
label.
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).