228 lines
9.7 KiB
Erlang
228 lines
9.7 KiB
Erlang
% 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 < 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).
|