PDA

View Full Version : White Album 2 Bytecode


velocity7
2012-02-15, 12:58
Hello,

I'm looking into how to parse the bytecode for White Album 2's scripts... the script.pak extracts into *.txt and *.bnr files, where the *.bnr looks like the bytecode and *.txt looks like SJIS text that's used for the *.bnr files. The *.bnr files start with LSCR as the first four bytes before the rest of its data. Help would be appreciated!

erengy
2012-02-17, 11:45
I'm not sure if these are correct, but a quick look at 1001.bnr reveals that:


Instructions are separated by 0x06 0x1E
0x03 probably pushes an integer to the stack
Printing a string from relevant .txt file is related with opcodes of 0x83 and 0x84
0x90 changes speaker tag

jonathanasdf
2012-02-17, 19:03
That's gonna be a really useful starting point for us! thanks

jonathanasdf
2012-02-17, 19:47
(Sorry for double posting, can't edit post because I'm not registered)

You say 03 pushes an integer, but I hardly ever see 03 used by itself, it's always 05 03. Does that mean 05 03 is push?

By random trying we found that changing the 08 in address 3F4 in 1001.bnr changes the bgm played from BGM_008_A.ogg (or maybe it's B, the 2 files are identical) to whatever number we put. Thus going from this logic that 06 1E separates opcodes then the instruction starts from 3EC and is

05 03 08 05 03 [FF FF FF FE] 05 03 01 05 03 FF 04 9E

=>

push 8
push -2
push 1
push 255
and then 04 9E. Maybe 9E is the operation for playing the bgm, but either way those 2 operations need to reduce the stack by 4 somehow...

jonathanasdf
2012-02-17, 20:15
Actually 05 03 is probably not one entity because I've seen 03 without 05 in front. 05 and 04 probably mean something.

I was thinking that if 9E calls the function "playSound" or something like that, then maybe it takes parameters like a boolean for repeat (the 1) or the type (whether it's bgm or sfx or voice, so this might be the -2) or something like that.

jonathanasdf
2012-02-17, 20:27
yay, that 1 really did control repeat. setting it to 0 made it stop repeating. I'm think that 04 might indicate that the next thing is a function. Still not quite sure what 05 does.

jonathanasdf
2012-02-17, 20:52
The instruction starting at 360

05 03 00 04 90

does change speaker tag:

00 = Haruki
01 = Kazusa
02 = Yukina
03+ seems to be nothing

However, the instruction at 3C8

05 03 01 06 17 04 90

doesn't seem to be for changing the speaker tag, so it seems 90 is not just for changing speaker tag?

for translation into english ideally we want to figure out how to have it take the speaker tag from the .txt file, so we don't need to modify the executable to change the hardcoded speaker tags.

jonathanasdf
2012-02-17, 21:00
ok I registered to make this easier.

Found out what the 4th argument to the playbgm function does (it doesn't seem to be a general playsound function). It controls the volume, and lies in the range 0-255.

so for the instruction that starts on 3EC, it is

playbgm(8, -2, 1, 255) = playbgm(track, dunno, repeat, volume).

erengy
2012-02-18, 10:11
However, the instruction at 3C8

05 03 01 06 17 04 90

doesn't seem to be for changing the speaker tag, so it seems 90 is not just for changing speaker tag?

That example you gave above clears the tag, so its purpose is still valid.


for translation into english ideally we want to figure out how to have it take the speaker tag from the .txt file, so we don't need to modify the executable to change the hardcoded speaker tags.

63rd instruction in 1001.bnr reads the tag from 1001.txt:
00000003 0000000f 00000004 00000090 00000006 0000001e

0x0F is the 15th value in 1001.txt (which is, "???") so it's possible to translate hard-coded character names by adding new values to .txt files and modifying related instructions. Not too easy, but probably easier than modifying the executable.


By the way, in case you need it, here's my interpretation of .fnc files:

LFScriptFunc.fnc:
type3 SLoad(type5 = 0x80000000, type3 = 0x00000000);
type3 SCall(type5 = 0x80000000, type3 = 0x00000000);
type3 call(type3 = 0x80000000);
type3 run();
type3 print(type6 = 0x80000000);
type3 ret();
type3 _int(type6 = 0x80000000);
type4 _float(type6 = 0x80000000);
type3 rand(type3 = 0x00000000);
type4 sin(type4 = 0x80000000);
type4 cos(type4 = 0x80000000);
type4 tan(type4 = 0x80000000);
type4 asin(type4 = 0x80000000);
type4 acos(type4 = 0x80000000);
type4 atan(type4 = 0x80000000);
type4 atan2(type4 = 0x80000000, type4 = 0x80000000);
type4 pow(type4 = 0x80000000, type4 = 0x80000000);
type4 sqrt(type4 = 0x80000000);
type3 timeGetTime();

LFScriptFuncEx.fnc:
type3 printEx(type6 = 0x80000000);
type3 printEx2(type4 = 0x40b8f5c3, type3 = 0x00000022);
type3 SetMessage(type5 = 0x80000000, type7 = 0x00000002, type3 = 0x80000000);
type3 SetMessageE(type5 = 0x80000000, type7 = 0x00000002, type3 = 0x80000000);
type3 EndMessage(type3 = 0x80000000);
type3 SetMessage2(type5 = 0x80000000, type7 = 0x00000002, type3 = 0x00000001);
type3 WaitMessage2();
type3 K();
type3 SetDemoMode(type3 = 0x80000000, type3 = 0x0000003c);
type3 VI(type7 = 0x00000001, type3 = 0xffffffff);
type3 VV(type3 = 0x80000000, type3 = 0x00000100, type3 = 0x00000000, type3 = 0x00000000, type7 = 0x00000001);
type3 VX(type3 = 0x80000000, type3 = 0x80000000, type3 = 0xffffffff, type3 = 0x00000100, type3 = 0x00000000, type3 = 0x00000000);
type3 VW(type3 = 0x00000000, type3 = 0x00000001);
type3 VS(type3 = 0x00000000, type3 = 0x00000000);
type3 W(type3 = 0x00000000, type3 = 0xffffffff, type3 = 0x00000000);
type3 WR(type3 = 0xffffffff, type3 = 0x00000000);
type3 WN(type6 = 0x80000000);
type3 WNS(type5 = 0x80000000);
type3 B(type3 = 0x00000001, type3 = 0xfffffffe, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x44a00000, type4 = 0x44340000);
type3 BC(type3 = 0x00000001, type3 = 0xfffffffe, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x44a00000, type4 = 0x44340000);
type3 V(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x44a00000, type4 = 0x44340000);
type3 H(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x44a00000, type4 = 0x44340000);
type3 SetShake(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
type3 StopShake();
type3 F(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
type3 FB(type3 = 0xffffffff, type3 = 0x00000080, type3 = 0x00000080, type3 = 0x00000080);
type3 C(type3 = 0x80000000, type3 = 0x80000000, type6 = 0x00000001, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x0000000f, type3 = 0x00000100, type3 = 0x00000080);
type3 CW(type3 = 0x80000000, type3 = 0x80000000, type6 = 0x00000001, type3 = 0x00000000, type3 = 0x00000100, type3 = 0x00000080);
type3 CR(type3 = 0x80000000, type3 = 0x00000000, type3 = 0x0000000f);
type3 CRW(type3 = 0x80000000);
type3 M(type3 = 0x80000000, type3 = 0xfffffffe, type3 = 0x00000001, type3 = 0x000000ff);
type3 MS(type3 = 0x00000000);
type3 MP();
type3 MV(type3 = 0x80000000, type3 = 0xfffffffe);
type3 MW();
type3 MLW();
type3 SE(type3 = 0x80000000, type3 = 0x000000ff);
type3 SEP(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000000, type3 = 0x00000001, type3 = 0x000000ff, type3 = 0x00000000);
type3 SES(type3 = 0x80000000, type3 = 0x00000000);
type3 SEV(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000000);
type3 SEW(type3 = 0x80000000, type3 = 0x00000000);
type3 SEVW(type3 = 0x80000000);
type3 SetTimeMode(type3 = 0x00000000);
type3 SetChromaMode(type3 = 0x000000ff, type3 = 0x00000000);
type3 SetEffctMode(type5 = 0x80000000, type3 = 0x00000000);
type3 SetWeather(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000002, type3 = 0x00000000);
type3 ChangeWeather(type3 = 0xfffffc18, type3 = 0xfffffc18, type3 = 0xfffffc18, type3 = 0xfffffc18, type3 = 0xfffffc18);
type3 ResetWeather();
type3 LoadBmp(type3 = 0x80000000, type5 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type5 = 0x80000000, type3 = 0x80000000, type5 = 0x80000000, type3 = 0x00000001);
type3 LoadBmpAnime(type3 = 0x80000000, type5 = 0x80000000, type3 = 0x80000000, type5 = 0x80000000, type3 = 0x80000000, type5 = 0x80000000, type3 = 0x00000001);
type3 SetBmpAvi(type3 = 0x80000000, type5 = 0x80000000, type3 = 0x80000000);
type3 WaitBmpAvi(type3 = 0x80000000);
type3 ReleaseBmp(type3 = 0x80000000);
type3 WaitBmpAnime(type3 = 0x80000000);
type3 SetBmpAnimePlay(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000001, type3 = 0x00000001);
type3 SetBmpDisp(type3 = 0x80000000, type3 = 0x80000000);
type3 SetBmpLayer(type3 = 0x80000000, type3 = 0x80000000);
type3 SetBmpParam(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000000);
type3 SetBmpRevParam(type3 = 0x80000000, type3 = 0x80000000);
type3 SetBmpBright(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
type3 SetBmpMove(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
type3 SetBmpPos(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
type3 SetBmpZoom(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
type3 SetBmpZoom2(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type4 = 0x80000000);
type3 SetBmpRoll(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
type3 SetMovie(type5 = 0x80000000, type3 = 0x00000000);
type3 Wait(type3 = 0x80000000, type3 = 0x00000000, type3 = 0x00000000);
type3 StartTimer();
type3 WaitTimer(type3 = 0x80000000);
type3 GoTitle();
type3 GetGameFlag(type3 = 0x80000000);
type3 SetGameFlag(type3 = 0x80000000, type3 = 0x80000000);
type3 LogOut(type3 = 0x80000000);
type3 V_Flag(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
type3 H_Flag(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
type3 Calender(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0xffffffff, type3 = 0x00000001);
type3 GetTimer();
type3 GetSkip();
type3 GetClick();
type3 runEX();
type3 SetSelectMess(type5 = 0x80000000, type3 = 0x00000063, type3 = 0x00000000, type3 = 0x00000000);
type3 SetSelect(type7 = 0x00000003);
type3 S(type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000);
type3 Z(type4 = 0x3f800000, type4 = 0x3f800000, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000280, type3 = 0x00000168, type3 = 0x00000000);
type3 R(type4 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000280, type3 = 0x00000168, type3 = 0x00000000);
type3 WSZ();
type3 StopSZR(type3 = 0x80000000, type3 = 0x00000000);
type3 VA(type3 = 0x00000000);
type3 CS(type3 = 0x80000000, type3 = 0x80000000, type6 = 0x00000001, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x0000000f, type3 = 0x00000100, type3 = 0x00000080);
type3 CM(type3 = 0x80000000, type4 = 0x80000000);
type3 CRS(type3 = 0x80000000, type3 = 0x00000000, type3 = 0x0000000f);
type3 SkipOFF();
type3 NevelMode(type3 = 0x80000000);
type3 EroMode(type3 = 0x80000000, type3 = 0x00000000);
type3 GetReplayMode();
type3 WN2(type6 = 0x80000000, type3 = 0xffffffff);
type3 WNS2(type5 = 0x80000000, type3 = 0xffffffff);
type3 B2(type3 = 0x00000001, type3 = 0xfffffffe, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x3f800000, type4 = 0x3f800000, type4 = 0x00000000, type3 = 0x00000001);
type3 BC2(type3 = 0x00000001, type3 = 0xfffffffe, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x3f800000, type4 = 0x3f800000, type4 = 0x00000000, type3 = 0x00000001);
type3 V2(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x3f800000, type4 = 0x3f800000, type4 = 0x00000000, type3 = 0x00000001);
type3 H2(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x3f800000, type4 = 0x3f800000, type4 = 0x00000000, type3 = 0x00000001);
type3 SetWeather2(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000002, type3 = 0x00000000, type3 = 0x00000000);
type3 ChangeWeather2(type3 = 0xfffffc18, type3 = 0xfffffc18, type3 = 0xfffffc18, type3 = 0xfffffc18, type3 = 0xfffffc18, type3 = 0xfffffc18);
type3 SetShake2(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000001);
type3 M2(type3 = 0x80000000, type3 = 0xfffffffe, type3 = 0x00000001, type3 = 0x000000ff, type3 = 0x00000000);
type3 NB(type3 = 0x00000000, type3 = 0x00000001);
type3 NBR(type3 = 0x0000001e, type3 = 0x00000001);
type3 VXV(type3 = 0x000000ff, type3 = 0x0000001e, type3 = 0x00000000);
type3 Wait2(type3 = 0x80000000, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000);
type3 IfSkip();
type3 GetSkip2();

jonathanasdf
2012-02-18, 14:49
Thanks again!

--------------------

So I have a theory.

05 03 xx means push the CONSTANT xx onto the stack

03 xx means push the xx'th value from the corresponding txt file onto the stack

going to test it out now :D

...and.. VERIFIED. This is going to be a big help.

erengy
2012-02-19, 09:18
Looks like 0x06 performs mathematical and logical operations on preceding arguments. Here's a list I extracted from the executable:


0x01: x + y
0x02: y - x
0x03: x * y
0x04: y / x
0x05: y % x
0x06: x & y
0x07: x | y

0x08: x == y
0x09: x < y
0x0A: x > y
0x0B: x <= y
0x0C: x >= y
0x0D: !(!x || !y)
0x0E: if (!(x || y)) return x
0x0F: x != y
0x10: x + y
0x11: x - y
0x12: x * y
0x13: x / y
0x14: x % y
0x15: x & y
0x16: x | y

0x09: x > (float)y
0x0A: x < (float)y
0x0D: !(x == 0.0 || y == 0.0)
0x0E: x != 0.0 || y == 0.0
0x10: x + y
0x11: y - x
0x12: x * y
0x13: y / x

0x08: _mbscmp(x, y) == 0
0x0F: _mbscmp(x, y)
0x10: strcat(x, y)

0x17: -x
0x18: x == 0.0
0x19: x + 1.0
0x1A: x - 1.0

0x17: -x
0x18: x == 0
0x19: x + 1
0x1A: x - 1

0x1B: ?

0x1C: 0
0x1D: 0

0x1E: ? [execute instruction]


In that light:
00000005 00000003 00000001 00000006 00000017 00000004 00000090
= func_0x90(logic_0x17(0x01));
= changename(-1);

jonathanasdf
2012-02-19, 17:40
Thanks, I think we have all the ingredients now, just need to map out what the functions are. Just curious but how are you managing to figure all of these out? How long have you spent analyzing this? You seem really pro compared to us :D

I think I figured out what 0x05 really is. 0x05 0x03 is not one instruction, but 0x05 is the opcode for pushconstant, and as the first argument to pushconstant, 0x03 means integer, and 0x04 means float. Haven't run into the other ones yet.

-----------------------
just summarizing the list we confirmed so far:

opcodes

0x00: statements
0x02: pushArray(idx), idx between 0 and 255.
0x03: push_from_txt(pos), 0-indexed
0x04: call(function)
0x05: pushconst(type, val)
0x06: doOp(type)
functions

0x00: LoadScript(string name, int pos)
0x01: CallScript(string name, int pos)
name is given without extension. Looks for and executes script.pak/name.bnr. Difference between 0x01 and 0x00 is that 0x01 keeps the current .bnr on the stack whereas 0x00 doesn't.
pos must be a value specified in the header of name.bnr. Execution will start at the value associated with that id in the header of name.bnr.
0x02: Call(int id)
the id is one of the values given to the header, which is mapped to the address to start executing from
0x04: Print(int/float/string text)
0x08: Rand(int n)
returns a random number in [0, n)
0x09: Sin(float x)
0x0A: Cos(float x)
0x0B: Tan(float x)
0x0C: Asin(float x)
0x0D: Acos(float x)
0x0E: Atan(float x)
0x0F: Atan2(float y, float x)
0x10: Pow(float b, float p)
0x11: Sqrt(float x)
0x83: ShowText(string text, int line#, ??)
Don't know what the line# is used for, but it's 0-indexed. For the ??, it seems to always be set to 1. I tried setting it to 0 but then the text never shows up and the game crashes
0x87: WaitForClick()
0x8A: PlayVoice(int a, int volume, ??2, ??3, int id)
Plays the file voice.pak/SCENE_id_a.ogg
0x90: ChangeSpeaker(int/string speaker)
-1 hides the speaker. 0 = Haruki, 1 = Kazusa, 2 = Setsuna.
0x92: ChangeBackground(int fadetype, int id, ??, int frames, ??2, ??3, ??4, float width, float height)
Not sure which fade corresponds to which type, but -1 is no fade, meaning it shows immediately. Fade out is done by using this function to fade to a black background.
for the id, not too sure how it works. The 13th instruction on line 234 has id=1060, and it's taking bak.pak/b106010.tga. Changing the value to 1055 makes it take bak.pak/b105510.tga.
0x9E: PlayBgm(int id, ??, bool repeat, int volume)
0x9F: MuteSound(int fadeduration)
0xA5: PlaySound(int channel, int id, ??, bool repeat, int volume, ??2)
Only 1 sound can be playing on 1 channel at once. For ??2, setting the value to 2 causes "サウンド構造体を初期化せずにSEP拡張命令を使いました。"
0xAB: MakeGrayscale(int lambda, ??)
Affects all backgrounds that come after the call. lambda=0 makes the images full grayscale, lambda=255 full color.
0xAC: ApplyFilter(string name, ??)
Empty string to clear.
Example: in 1001.txt the 5rd value is sepia.AMP. Thus, 0x03 0x04 0x05 0x03 0x00 0x04 0xAC will apply grp.pak/sepia.AMP to all backgrounds after the call.
0xAD: SetWeather(int type, ??, ??2, ??3, ??4, ??5)
Didn't bother to investigate too much into this, we probably don't need to worry about it. The ??s are probably effect parameters and probably depend on the effect. They all seem to take the same number of parameters though
Types: 0 = rain, 1 = sakura, 2 = fireflies, 3 = snow along x-y plane, 4 = snow along z-axis coming towards you, 5 = snow along z-axis going away from you, 6 = snow along z-axis coming towards you and spiralling.
0xC1: PlayMovie(string id, int ??)
0xC2: Wait(int frames, ??, ??2)
0xCB: Calendar(int year, int month, int day, ??, ??)
0xD0: AddChoice(string text, ??, ??2, ??3)
for ??2 and ??3:
0 0 - visible and selectable
1 0 - invisible
0 1 - visible and selectable
1 1 - visible and not selectable
0xD1: GetSelected(??)
Waits until the user makes a selection, then returns the 0-based index of the choice
How do you make the code box not need to be scrolled..

erengy
2012-02-20, 15:27
Thanks for the compliment, but I'm far from being a pro. I just love learning how things work :) I think I spent around 2-4 hours for each of my messages above.

That said, here's some more info dump:

0x01, 0x02 and 0x03 are followed by 8-bit, 16-bit and 32-bit integers respectively, and 0x04 is for floats as you said.

In case you haven't discovered it yet, each binary (.bnr) file has a header portion. Magic text ("LSCR", as in Leaf SCRipt) is followed by an unknown value, the count of items in the header, and items. Each item is a pair of integers.

I haven't been able to try this but looks like the engine supports patches. That is, you can pack the English patch into a "patch.pak" and it will check that file before any other package. That way you don't need to distribute raw scripts or overwrite the original script.pak.

Since lines are separated by commas in .txt files, when you need to use a comma literally, you need to use a tilde instead. Though I'd suggest deciding on a more translation-friendly text format first, and handling the back and forth conversion with some tool.

These characters are automatically converted into the latter by the engine: "^" → " ", "`" → "'", "~" → ","
And these characters need to be escaped with a backslash if you want to use them literally: <>\^|~
"\n" serves as a line break, "\k" waits for a mouse-click before printing the rest of the line.

The engine supports some special tags:

<B#text> changes font color (only to light blue?).
<C#text> changes font color (C0 is white, C1 is light gray, C2 is dark gray, C3 is black).
<F#text> changes font size: <F14「なら最初から仕切ればいいのに…」>
<Rtext|ruby> is for ruby text: <R空港|ここ>
<S#text> changes text speed (S0 is the slowest, S9 is instant).
<W#> waits for a given time: <W100> (waits 1 second)

You can use tags within tags: <S0何で言っちゃうんだよ。<W120>\n><S0俺の…馬鹿。>
Lowercase letters (<w> instead of <W>) work as well.
And there are these <A>, <D> and <K> but not sure about their purpose.

Finally, according to my calculations, line count and MB size values are a bit different than the ones listed at TLwiki:

White Album 2 ~introductory chapter~

Line count: 9095 (listed as 10687)
MB size: 0.51 (listed as 0.50)

White Album 2 ~closing chapter~

Line count: 59200 (listed as 60618)
MB size: 3.70 (listed as 3.33)

jonathanasdf
2012-02-20, 18:33
In case you haven't discovered it yet, each binary (.bnr) file has a header portion. Magic text ("LSCR", as in Leaf SCRipt) is followed by an unknown value, the count of items in the header, and items. Each item is a pair of integers.Hm, can you expand on this? In particular, do you have any idea what the items are used for?

Since lines are separated by commas in .txt files, when you need to use a comma literally, you need to use a tilde instead. Though I'd suggest deciding on a more translation-friendly text format first, and handling the back and forth conversion with some tool.Ah, nice find! We were planning to use a full-width comma as a replacement for a comma+space (visually it looks quite fine) but this is probably better. Yes, we're currently in the process of writing a tool to convert between .bnr and some format we're going to invent that consists of an .xml file with the commands and a .txt file with pure text content, which will further be converted to some wiki format for posting for the public to translate (we're going to be hosting this on BakaTsuki not TLWiki)

0x0E: if (!(x || y)) return xuhm, what exactly happens if the condition evaluates to false?

0x06 0x1E doesn't seem to mean "execute". It seems to have the effect of clearing the stack, but I don't know if it has any other effects.
0x04 doing a function call seems to take the amount of parameters from the top of the stack it expects, and then clears the stack.

velocity7's theory is that 0x00 is nop, and 0x01 is eof, I'm testing those now (though I don't know how I should test whether 0x00 is nop or just something whose behaviour can't be seen...)
Ok, 0x01 isn't eof, there is no opcode 0x01. So, 0x00 0x01 is one unit (0x00 is the opcode, and 0x01 is the argument). But even that isn't eof. There's also 0x00 0x00. I wonder what the two of them do..
Oh, 0x00 0x0A seems to be a break statement, so maybe 0x00 are statements (if, else, break, continue, case, etc)

Just wanted to point out that I found that the function 0x04 is alert!! Will help a lot in debugging I hope. By the way, if you want access to our tools to help make this a bit easier, we can give you access to the repository.

erengy
2012-02-21, 10:47
Hm, can you expand on this? In particular, do you have any idea what the items are used for?
Sorry, haven't got a clue. They may very well be offset labels though.

We were planning to use a full-width comma as a replacement for a comma+space (visually it looks quite fine) but this is probably better.
You may want to edit the font file (fnt.pak) for better-looking Latin characters. To Heart 2 is based on the same engine (probably an older version, haven't checked) and looks like its translation team did just that. Oh, wait. You probably already know that since you're working with velocity7.

uhm, what exactly happens if the condition evaluates to false?
Simply returns false.

0x06 0x1E doesn't seem to mean "execute". It seems to have the effect of clearing the stack, but I don't know if it has any other effects.
0x04 doing a function call seems to take the amount of parameters from the top of the stack it expects, and then clears the stack.
Could be. I always assumed that the stack is automatically cleared after a function call, not being sure on what 0x1E does.

Oh, 0x00 0x0A seems to be a break statement, so maybe 0x00 are statements (if, else, break, continue, case, etc)
0x00 is very much likely to be for statements. For the following value, options from 0x00 through 0x10 are available, where 0x00 0x00 returns 0, and 0x00 0x01 returns 1. Others do various operations that I couldn't make any sense of. 0x08-0x09 seem to output "実行時にcaseの処理スルー" and "実行時にdefaultの処理スルー" debug messages.

jonathanasdf
2012-02-21, 12:17
So that means...
0x0E: if (!(x || y)) return x
will ALWAYS return 0 :\

From 9999.bnr it seems 0x07 is switch, 0x08 is case, 0x09 is default, and 0x0A is break. From what you say there, I guess 0x00 is false and 0x01 is true.
Edit: hm, it seems that if it reaches a statement 0x00 0x00 then it crashes. Also, it seems that the stack defaults to giving a value of 0 if you try to access it and it's empty.

Yes we are planning to change the font eventually.

Sorry, haven't got a clue. They may very well be offset labels though.That's the best theory I've heard so far. Makes sense, since I was trying to call bnr scripts and passing values to the 2nd parameter thinking it might've been an offset into the script, but all that did was crash the game.

erengy
2012-02-21, 13:44
Oh, you're right, my mistake. Now that I take another look, it's actually like this:

if (x || y) {
return 1;
} else {
return x;
}

jonathanasdf
2012-02-21, 21:16
So that's just a normal or.

I think that for 0x06 0x01 to 0x07 it's not what you have written there, but rather x = x+y, x = y-x, etc. At least, when I try with

05 03 05 05 03 07 06 01 04 04 06 1E

etc, it gives the error 変数以外に=式は使えません
Which also implies that there's the concept of variables.

for 0x06 0x08 onwards it's right.

erengy
2012-02-22, 12:57
Well, at least one of the parameters is bound to be a variable. Otherwise there would be no point in adding two constant numbers; you would just pass the result directly to the function. Though it is still possible to do so I guess, using 0x06 0x10 instead of 0x06 0x01, for example.

I think I got the function mapping pattern by the way, which turned out to be quite simpler than I imagined. LFScriptFunc.fnc includes function codes between 0x00-0x12: Your "callBnr" is actually "SLoad", "callBnrAndReturn" is "SCall", "alert" is "print", and so on. As for LFScriptFuncEx.fnc, the first one is 0x80, so you just need to add 128 to a function's zero-based index to get its code. The ones you've confirmed so far through 0x83-0xC2 seem to fit this pattern at least:



0x83: showText(string text, int line#, ??)
type3 SetMessage2(type5 = 0x80000000, type7 = 0x00000002, type3 = 0x00000001);

0x84: ????(??)
type3 WaitMessage2();

0x90: chgSpeaker(int/string speaker)
type3 WN(type6 = 0x80000000);

0x92: chgBackground(int fadetype, int id, ??, int frames, ??2, ??3, ??4, float width, float height)
type3 BC(type3 = 0x00000001, type3 = 0xfffffffe, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x44a00000, type4 = 0x44340000);

0x9E: playBgm(int id, ??, bool repeat, int volume)
type3 M(type3 = 0x80000000, type3 = 0xfffffffe, type3 = 0x00000001, type3 = 0x000000ff);

0xA5: playSnd(int channel, int id, ??, bool repeat, int volume, ??2)
type3 SEP(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000000, type3 = 0x00000001, type3 = 0x000000ff, type3 = 0x00000000);

0xAB: makeGrayscale(int lambda, ??)
type3 SetChromaMode(type3 = 0x000000ff, type3 = 0x00000000);

0xAC: applyFilter(string name, ??)
type3 SetEffctMode(type5 = 0x80000000, type3 = 0x00000000);

0xAD: showWeather(int type, ??, ??2, ??3, ??4, ??5)
type3 SetWeather(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000002, type3 = 0x00000000);

0xC1: playMovie(string id, int ??)
type3 SetMovie(type5 = 0x80000000, type3 = 0x00000000);

0xC2: sleep(int frames, ??, ??2)
type3 Wait(type3 = 0x80000000, type3 = 0x00000000, type3 = 0x00000000);


This also explains your comment on 0x84:
This does not seem to be needed to show the text or the backlog, but 0x84 always follows 0x83. It takes 1 parameter, but changing it doesn't seem to affect anything.
0x84 (WaitMessage2) doesn't actually take any parameter, but it's there for whatever reason anyway.

jonathanasdf
2012-02-22, 17:42
Nice! too bad that doesn't tell us what the parameters do :(
But at least the function names seem logical, so that should aid a lot in determining what the function does. For the tool though I'd like to stick to our names since for the most cases it's more informative.

My interpretations:

WN = Window Name
BC = Background Change
M = Music
SEP = Sound Effect Play


And I might as well list down the functions from the script files here then, for simplifying future lookup (this is copied from your previous post).

Actually, 0x83 is SetMessageE and 0x84 is EndMessage, and 0x84 does take 1 parameter.


type1 = int8
type2 = int16
type3 = int32
type4 = float
type5 = string
type6 = any
type7 = ???

0x00: type3 SLoad(type5 = 0x80000000, type3 = 0x00000000);
0x01: type3 SCall(type5 = 0x80000000, type3 = 0x00000000);
0x02: type3 call(type3 = 0x80000000);
0x03: type3 run();
0x04: type3 print(type6 = 0x80000000);
0x05: type3 ret();
0x06: type3 _int(type6 = 0x80000000);
0x07: type4 _float(type6 = 0x80000000);
0x08: type3 rand(type3 = 0x00000000);
0x09: type4 sin(type4 = 0x80000000);
0x0A: type4 cos(type4 = 0x80000000);
0x0B: type4 tan(type4 = 0x80000000);
0x0C: type4 asin(type4 = 0x80000000);
0x0D: type4 acos(type4 = 0x80000000);
0x0E: type4 atan(type4 = 0x80000000);
0x0F: type4 atan2(type4 = 0x80000000, type4 = 0x80000000);
0x10: type4 pow(type4 = 0x80000000, type4 = 0x80000000);
0x11: type4 sqrt(type4 = 0x80000000);
0x12: type3 timeGetTime();

0x80: type3 printEx(type6 = 0x80000000);
0x81: type3 printEx2(type4 = 0x40b8f5c3, type3 = 0x00000022);
0x82: type3 SetMessage(type5 = 0x80000000, type7 = 0x00000002, type3 = 0x80000000);
0x83: type3 SetMessageE(type5 = 0x80000000, type7 = 0x00000002, type3 = 0x80000000);
0x84: type3 EndMessage(type3 = 0x80000000);
0x85: type3 SetMessage2(type5 = 0x80000000, type7 = 0x00000002, type3 = 0x00000001);
0x86: type3 WaitMessage2();
0x87: type3 K();
0x88: type3 SetDemoMode(type3 = 0x80000000, type3 = 0x0000003c);
0x89: type3 VI(type7 = 0x00000001, type3 = 0xffffffff);
0x8A: type3 VV(type3 = 0x80000000, type3 = 0x00000100, type3 = 0x00000000, type3 = 0x00000000, type7 = 0x00000001);
0x8B: type3 VX(type3 = 0x80000000, type3 = 0x80000000, type3 = 0xffffffff, type3 = 0x00000100, type3 = 0x00000000, type3 = 0x00000000);
0x8C: type3 VW(type3 = 0x00000000, type3 = 0x00000001);
0x8D: type3 VS(type3 = 0x00000000, type3 = 0x00000000);
0x8E: type3 W(type3 = 0x00000000, type3 = 0xffffffff, type3 = 0x00000000);
0x8F: type3 WR(type3 = 0xffffffff, type3 = 0x00000000);
0x90: type3 WN(type6 = 0x80000000);
0x91: type3 WNS(type5 = 0x80000000);
0x92: type3 B(type3 = 0x00000001, type3 = 0xfffffffe, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x44a00000, type4 = 0x44340000);
0x93: type3 BC(type3 = 0x00000001, type3 = 0xfffffffe, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x44a00000, type4 = 0x44340000);
0x94: type3 V(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x44a00000, type4 = 0x44340000);
0x95: type3 H(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x44a00000, type4 = 0x44340000);
0x96: type3 SetShake(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
0x97: type3 StopShake();
0x98: type3 F(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
0x99: type3 FB(type3 = 0xffffffff, type3 = 0x00000080, type3 = 0x00000080, type3 = 0x00000080);
0x9A: type3 C(type3 = 0x80000000, type3 = 0x80000000, type6 = 0x00000001, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x0000000f, type3 = 0x00000100, type3 = 0x00000080);
0x9B: type3 CW(type3 = 0x80000000, type3 = 0x80000000, type6 = 0x00000001, type3 = 0x00000000, type3 = 0x00000100, type3 = 0x00000080);
0x9C: type3 CR(type3 = 0x80000000, type3 = 0x00000000, type3 = 0x0000000f);
0x9D: type3 CRW(type3 = 0x80000000);
0x9E: type3 M(type3 = 0x80000000, type3 = 0xfffffffe, type3 = 0x00000001, type3 = 0x000000ff);
0x9F: type3 MS(type3 = 0x00000000);
0xA0: type3 MP();
0xA1: type3 MV(type3 = 0x80000000, type3 = 0xfffffffe);
0xA2: type3 MW();
0xA3: type3 MLW();
0xA4: type3 SE(type3 = 0x80000000, type3 = 0x000000ff);
0xA5: type3 SEP(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000000, type3 = 0x00000001, type3 = 0x000000ff, type3 = 0x00000000);
0xA6: type3 SES(type3 = 0x80000000, type3 = 0x00000000);
0xA7: type3 SEV(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000000);
0xA8: type3 SEW(type3 = 0x80000000, type3 = 0x00000000);
0xA9: type3 SEVW(type3 = 0x80000000);
0xAA: type3 SetTimeMode(type3 = 0x00000000);
0xAB: type3 SetChromaMode(type3 = 0x000000ff, type3 = 0x00000000);
0xAC: type3 SetEffctMode(type5 = 0x80000000, type3 = 0x00000000);
0xAD: type3 SetWeather(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000002, type3 = 0x00000000);
0xAE: type3 ChangeWeather(type3 = 0xfffffc18, type3 = 0xfffffc18, type3 = 0xfffffc18, type3 = 0xfffffc18, type3 = 0xfffffc18);
0xAF: type3 ResetWeather();
0xB0: type3 LoadBmp(type3 = 0x80000000, type5 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type5 = 0x80000000, type3 = 0x80000000, type5 = 0x80000000, type3 = 0x00000001);
0xB1: type3 LoadBmpAnime(type3 = 0x80000000, type5 = 0x80000000, type3 = 0x80000000, type5 = 0x80000000, type3 = 0x80000000, type5 = 0x80000000, type3 = 0x00000001);
0xB2: type3 SetBmpAvi(type3 = 0x80000000, type5 = 0x80000000, type3 = 0x80000000);
0xB3: type3 WaitBmpAvi(type3 = 0x80000000);
0xB4: type3 ReleaseBmp(type3 = 0x80000000);
0xB5: type3 WaitBmpAnime(type3 = 0x80000000);
0xB6: type3 SetBmpAnimePlay(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000001, type3 = 0x00000001);
0xB7: type3 SetBmpDisp(type3 = 0x80000000, type3 = 0x80000000);
0xB8: type3 SetBmpLayer(type3 = 0x80000000, type3 = 0x80000000);
0xB9: type3 SetBmpParam(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000000);
0xBA: type3 SetBmpRevParam(type3 = 0x80000000, type3 = 0x80000000);
0xBB: type3 SetBmpBright(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
0xBC: type3 SetBmpMove(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
0xBD: type3 SetBmpPos(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
0xBE: type3 SetBmpZoom(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
0xBF: type3 SetBmpZoom2(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type4 = 0x80000000);
0xC0: type3 SetBmpRoll(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
0xC1: type3 SetMovie(type5 = 0x80000000, type3 = 0x00000000);
0xC2: type3 Wait(type3 = 0x80000000, type3 = 0x00000000, type3 = 0x00000000);
0xC3: type3 StartTimer();
0xC4: type3 WaitTimer(type3 = 0x80000000);
0xC5: type3 GoTitle();
0xC6: type3 GetGameFlag(type3 = 0x80000000);
0xC7: type3 SetGameFlag(type3 = 0x80000000, type3 = 0x80000000);
0xC8: type3 LogOut(type3 = 0x80000000);
0xC9: type3 V_Flag(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
0xCA: type3 H_Flag(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000);
0xCB: type3 Calender(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0xffffffff, type3 = 0x00000001);
0xCC: type3 GetTimer();
0xCD: type3 GetSkip();
0xCE: type3 GetClick();
0xCF: type3 runEX();
0xD0: type3 SetSelectMess(type5 = 0x80000000, type3 = 0x00000063, type3 = 0x00000000, type3 = 0x00000000);
0xD1: type3 SetSelect(type7 = 0x00000003);
0xD2: type3 S(type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000);
0xD3: type3 Z(type4 = 0x3f800000, type4 = 0x3f800000, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000280, type3 = 0x00000168, type3 = 0x00000000);
0xD4: type3 R(type4 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000280, type3 = 0x00000168, type3 = 0x00000000);
0xD5: type3 WSZ();
0xD6: type3 StopSZR(type3 = 0x80000000, type3 = 0x00000000);
0xD7: type3 VA(type3 = 0x00000000);
0xD8: type3 CS(type3 = 0x80000000, type3 = 0x80000000, type6 = 0x00000001, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x0000000f, type3 = 0x00000100, type3 = 0x00000080);
0xD9: type3 CM(type3 = 0x80000000, type4 = 0x80000000);
0xDA: type3 CRS(type3 = 0x80000000, type3 = 0x00000000, type3 = 0x0000000f);
0xDB: type3 SkipOFF();
0xDC: type3 NevelMode(type3 = 0x80000000);
0xDD: type3 EroMode(type3 = 0x80000000, type3 = 0x00000000);
0xDE: type3 GetReplayMode();
0xDF: type3 WN2(type6 = 0x80000000, type3 = 0xffffffff);
0xE0: type3 WNS2(type5 = 0x80000000, type3 = 0xffffffff);
0xE1: type3 B2(type3 = 0x00000001, type3 = 0xfffffffe, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x3f800000, type4 = 0x3f800000, type4 = 0x00000000, type3 = 0x00000001);
0xE2: type3 BC2(type3 = 0x00000001, type3 = 0xfffffffe, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x3f800000, type4 = 0x3f800000, type4 = 0x00000000, type3 = 0x00000001);
0xE3: type3 V2(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x3f800000, type4 = 0x3f800000, type4 = 0x00000000, type3 = 0x00000001);
0xE4: type3 H2(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000000, type3 = 0x0000001e, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000, type4 = 0x3f800000, type4 = 0x3f800000, type4 = 0x00000000, type3 = 0x00000001);
0xE5: type3 SetWeather2(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000002, type3 = 0x00000000, type3 = 0x00000000);
0xE6: type3 ChangeWeather2(type3 = 0xfffffc18, type3 = 0xfffffc18, type3 = 0xfffffc18, type3 = 0xfffffc18, type3 = 0xfffffc18, type3 = 0xfffffc18);
0xE7: type3 SetShake2(type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x80000000, type3 = 0x00000001);
0xE8: type3 M2(type3 = 0x80000000, type3 = 0xfffffffe, type3 = 0x00000001, type3 = 0x000000ff, type3 = 0x00000000);
0xE9: type3 NB(type3 = 0x00000000, type3 = 0x00000001);
0xEA: type3 NBR(type3 = 0x0000001e, type3 = 0x00000001);
0xEB: type3 VXV(type3 = 0x000000ff, type3 = 0x0000001e, type3 = 0x00000000);
0xEC: type3 Wait2(type3 = 0x80000000, type3 = 0x00000000, type3 = 0x00000000, type3 = 0x00000000);
0xED: type3 IfSkip();
0xEE: type3 GetSkip2();

erengy
2012-02-22, 19:20
Actually, 0x83 is SetMessageE and 0x84 is EndMessage, and 0x84 does take 1 parameter.
Oh, right. Looks like I mixed them up.

Nice! too bad that doesn't tell us what the parameters do :(
Haha, that would be great, wouldn't it. What we have here though, is more than one could hope for. I wonder if the game still runs without the .fnc files... Nah, it doesn't. Tried it now, threw out a couple of errors and crashed.

Mapping function codes with their names was actually the last distinct objective I had in mind, but now that I think about it, there are these parameter types. We already know about type3 (int32), type4 (float) and type5 (string index). For the remaining ones, here are all the examples:

type6:

type3 printEx(type6 = 0x80000000);
type3 WN(type6 = 0x80000000);
type3 C(type3, type3, type6 = 0x00000001, type3, type3, type3, type3, type3);
type3 CW(type3, type3, type6 = 0x00000001, type3, type3, type3);
type3 CS(type3, type3, type6 = 0x00000001, type3, type3, type3, type3, type3);
type3 WN2(type6 = 0x80000000, type3);

type7:

type3 SetMessage(type5, type7 = 0x00000002, type3);
type3 SetMessageE(type5, type7 = 0x00000002, type3);
type3 SetMessage2(type5, type7 = 0x00000002, type3);
type3 VI(type7 = 0x00000001, type3);
type3 VV(type3, type3, type3, type3, type7 = 0x00000001);
type3 SetSelect(type7 = 0x00000003);

I think type6 is either signed integer (assuming type3 is unsigned) as WN() can take -1 to clear the speaker tag, or an indicator that the value can be type3 or type5. Second one sounds more likely.

As for type7, we know that second parameter of SetMessageE() is some incremental number. One thing that comes to mind is it could be some kind of index, a variable index perhaps. It could be for keeping track of which lines are read and which of them or not, and to keep them in save files. We may be able to test this theory by changing some values and checking if it breaks line skipping.

As you can see, assuming that my interpretation was correct, some parameters have default values. The default value for SEP()'s volume parameter is 0xFF, for instance. But how are they used? I think either all the functions in .bnr files have their proper arguments, or there is some other opcode we're currently unaware of to push the default argument onto stack.

jonathanasdf
2012-02-22, 19:50
My theory is that type6 means anything, because print can take a float as well.
Your idea about line skipping is interesting, maybe 0x84 has to do with that?

jonathanasdf
2012-02-24, 11:05
Still, I wonder how do you create or use a variable? Also, what do the opcodes 0x01 and 0x02 do...

erengy
2012-02-24, 12:48
0x01 and 0x02 should be for int8 and int16 as I said before.

Not sure if you can define variables within .bnr files, but there are some global ones in GFLAG.dat and Global.vrb:

GFLAG.dat:

0x0000 = _GFLAG_EV_序章クリア
0x0001 = _GFLAG_EV_千晶ノーマルエンド
0x0002 = _GFLAG_EV_2周目以降
0x0003 = _GFLAG_EV_二章クリア
0x0004 = _GFLAG_EV_終章クリア
0x0005 = _GFLAG_EV_三章入った
0x000A = _GFLAG_EV_かずさ小説クリア
0x000B = _GFLAG_EV_雪菜小説クリア
0x0014 = _GFLAG_EV_2部ノーマルED
0x0015 = _GFLAG_EV_2部雪菜ED
0x0016 = _GFLAG_EV_2部小春ED
0x0017 = _GFLAG_EV_2部千晶ED
0x0018 = _GFLAG_EV_2部千晶ノーマルED
0x0019 = _GFLAG_EV_2部麻理ED
0x001E = _GFLAG_EV_3部ノーマルED
0x001F = _GFLAG_EV_3部かずさED
0x0020 = _GFLAG_EV_3部かずさ浮気ED
0x0021 = _GFLAG_EV_3部雪菜ED
0x0063 = _GFLAG_選択肢用(システム)
0x0064 = _GFLAG_音楽
0x00C8 = _GFLAG_動画_リーフロゴ
0x00C9 = _GFLAG_動画_プロローグ
0x00CA = _GFLAG_å‹•ç”»_OP
0x00CB = _GFLAG_å‹•ç”»_èµ°ã‚‹
0x00CF = _GFLAG_動画_雪菜モノローグ
0x00D0 = _GFLAG_動画_予告
0x00D1 = _GFLAG_å‹•ç”»_ED
0x00D2 = _GFLAG_動画_2部以降予約
0x00D2 = _GFLAG_動画_2部OP
0x00D3 = _GFLAG_動画_2部ノーマルED
0x00D4 = _GFLAG_動画_2部雪菜ED
0x00D5 = _GFLAG_動画_2部各キャラED
0x00DC = _GFLAG_動画_3部OP
0x00DD = _GFLAG_動画_3部ノーマルED
0x00DE = _GFLAG_動画_3部かずさED
0x00DF = _GFLAG_動画_3部かずさ浮気ED
0x00E0 = _GFLAG_動画_3部雪菜ED
0x0050 = _GFLAG_エロシーンかずさ序章
0x012C = _GFLAG_エロシーン雪菜1
0x012D = _GFLAG_エロシーン雪菜2
0x012E = _GFLAG_エロシーン雪菜3
0x012F = _GFLAG_エロシーン小春1
0x0130 = _GFLAG_エロシーン小春2
0x0131 = _GFLAG_エロシーン小春3
0x0132 = _GFLAG_エロシーン千晶1
0x0133 = _GFLAG_エロシーン千晶2
0x0134 = _GFLAG_エロシーン千晶3
0x0135 = _GFLAG_エロシーン麻理1
0x0136 = _GFLAG_エロシーン麻理2
0x0137 = _GFLAG_エロシーン麻理3
0x0138 = _GFLAG_エロシーン麻理4
0x0139 = _GFLAG_エロシーン雪菜4
0x013A = _GFLAG_エロシーン雪菜5
0x013B = _GFLAG_エロシーン雪菜6
0x013C = _GFLAG_エロシーンかずさ3
0x013D = _GFLAG_エロシーンかずさ4
0x013E = _GFLAG_エロシーンかずさ5
0x013F = _GFLAG_エロシーンかずさ6
0x0140 = _GFLAG_エロシーンかずさ7
0x0141 = _GFLAG_エロシーンかずさ8
0x0142 = _GFLAG_エロシーンかずさ9
0x0384 = _GFLAG_選択肢予約

Global.vrb:

3,FLG_00,0
3,SubWait_timer,0
3,SubWhiteIn_timer,0
3,SubWhiteOut_timer,0
3,FLG_章管理サブ,0
3,FLG_かずさ本気度,0
3,FLG_かずさ浮気度,0
3,FLG_雪菜好意度,0
3,FLG_小春好意度,0
3,FLG_千晶好意度,0
3,FLG_麻理好意度,0
3,FLG_小春ルート消滅,0
3,FLG_千晶ルート消滅,0
3,FLG_麻理ルート消滅,0
3,FLG_第2部03選択肢1,0
3,FLG_第2部03選択肢2,0
3,FLG_第2部13選択肢1,0
3,FLG_第2部14選択肢1,0
3,FLG_第2部15選択肢1,0
3,FLG_第2部18選択肢1,0
3,FLG_第3部05選択肢1,0
3,FLG_第3部09選択肢1,0
3,FLG_第3部13条件1,0
3,FLG_第3部13選択肢1,0
3,FLG_第3部13選択肢2,0
3,FLG_第3部14選択肢1,0
3,FLG_第3部15条件2,0
3,FLG_第3部16条件3,0
3,FLG_第3部16選択肢2,0

velocity7
2012-02-24, 16:48
Should probably note that the names of some of those variables are spoilers for anyone who hasn't played WA2 yet. ;)

jonathanasdf
2012-02-25, 17:20
0x01 and 0x02 should be for int8 and int16 as I said before.

Is that not for the argument to 0x05, which means push constant? eg 0x05 0x01 xx means push int8? I'm talking about the opcodes 0x01 and 0x02.

So far what we know is that
0x00 is various statements
0x03 is push line from corresponding .txt file
0x04 is call function
0x05 is push constant
0x06 is perform various operations

just 0x01 and 0x02 are missing.

jonathanasdf
2012-02-28, 12:48
Some more updates:

The unknown value before the header seems to be the address of the first thing after the header. Seems redundant though if header size is there too :\

You CAN jump into the middle of a .bnr file using LoadScript/CallScript referencing the header of the file you're calling

0x06 0x00 is assign (x = y)

0x02 0xaa seems to be push arr_aa where arr_aa is a local array.

0x06 0x1B seems to be array dereferencing.
0x02 0x01 0x05 0x03 0x00 0x06 0x1B seems to give you the variable arr_01[0].

jonathanasdf
2012-03-06, 14:19
Ok, we are now done deciphering the .bnr format. The only problems now are some unrecognized game flag ids (we have figured out that they are set when a song is unlocked for bgm mode), and that there are lots of functions that we don't know what they do, but these problems are not important for translation. Now we can start writing a tool to import/export .bnrs and start translation!