egs/src/psu_parser.erl

243 lines
10 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() ->
2010-07-17 04:57:48 +08:00
List = [parse_zone(QuestID, Filename) || {[QuestID, ZoneID], [{file, Filename}]} <- ?ZONES, ZoneID =:= 0, 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(5, _Params, _Data) ->
floor_button;
parse_object_args(6, _Params, _Data) ->
fog;
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(43, _Params, _Data) ->
shoot_button;
parse_object_args(44, _Params, _Data) ->
trap;
2010-07-17 04:57:48 +08:00
parse_object_args(48, _Params, _Data) ->
boss_gate;
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).