December 26, 2024, 02:42:47 PM

Username
Password

Pages: [1] 2   Go Down
Print
Author Topic: Cut-Scene Modding  (Read 33213 times)
0 Members and 1 Guest are viewing this topic.
aqrit
Developer
*
Offline Offline

Posts: 85



View Profile WWW
« on: November 29, 2013, 08:41:02 AM »

What is the current state of cut-scene modding?

Is the wiki up to date?
http://wiki.dark-omen.org/do/DO/WHMTG

Has anyone given thought to externalizing the .MTG file?
Logged
Ghabry
Developer
*
Offline Offline

Posts: 1020



View Profile
« Reply #1 on: November 29, 2013, 01:58:02 PM »

I had some thoughts about this. But havnt started working on this yet Wink

My idea was to read some script.whmtg file using Mod Selector.
The file structure should look like this http://pastebin.com/9mf0NfwD without the line count at the beginning.
That file is assembled on mod loading and then written in WHMTG-binary format to memory (the memory location must be the same everytime because savegames reference it, e.g. using VirtualAllocEx). And then the hardcoded stuff pointing to WHMTG (e.g. New Campaign, Tutorial, Jump to Chapter x cheat) should be repointed to the new one.
Logged

aqrit
Developer
*
Offline Offline

Posts: 85



View Profile WWW
« Reply #2 on: December 01, 2013, 08:51:54 AM »

Looks pretty much all figured out.

I doubt that the save file would have an absolute address store in it.
my guess is that "current position" in the script would be relative to "script start"
so I think it will not be necessary to load at the same memory address every time.

since pastebin has apparently banned my IP address from posting...
here is what things look like to me at this point in time.

//xrefs
004C3C48 // start of script ( 4C3C20 thru 4C3C244 == dead code? )
004CCD28 // end of script
004C3D48 // tutorial
004C3D68 // new campaign
004C3DA8 // Start game at chapter 2
004C3E40 // Start game at chapter 3
004C3F28 // Start game at chapter 4
004C4060 // Start game at chapter 5


//VA func, name, argc, args w/fixups
/////////////////////////////////////////////////////////////////////////
0x0041DB60, "WH_GOTO", 1, { 1 } // label
0x0041DB80, "WH_IF", 2, { }
0x0041DC30, "WH_ENDIF", 0, { }
0x0041DC60, "WH_ELSE", 0, { }
0x0041DC80, "WH_GOSUB", 1, { 1 } // label
0x0041DCD0, "WH_RETURN", 0, { }
0x0041DDF0, "WH_PUSHLV", 0, { }
0x0041DE10, "WH_POPLV", 0, { }
0x0041DD00, "WH_REPEAT", 0, { }
0x0041DD20, "WH_UNTIL", 2, { }
0x0041DED0, "WH_END", 0, { }
0x0041C920, "WH_PlayMovie", 1, { 1 } // ".tgq"
0x0041C970, "WH_MeetingPoint", 2, { 1, 2 } // ".bmp", label
0x0041CA00, "WH_TravelMap", 10, { 1, 2, 3 } // ".bmp", ".spr", ".dot"
0x0041CB10, "WH_Deploy", 0, { }
0x0041CB20, "WH_Battle", 2, { 1 } // "B1_01"
0x0041CDC0, "WH_GetUnitStatus", 1, { }
0x0041CE20, "WH_GetUnitHireStatus", 1, { }
0x0041CE80, "WH_AddUnit", 1, { }
0x0041CED0, "WH_RemoveUnit", 1, { }
0x0041DEE0, "WH_SetVariable", 2, { 1 } // var
0x0041DF00, "WH_ReadVariable", 1, { 1 } // var
0x0041D620, "WH_AddCash", 1, { }
0x0041D970, "WH_GameOver", 0, { }
0x0041DF20, "WH_ClearVariables", 2, { 1 } // var
0x0041CFB0, "WH_ForceUnit", 1, { }
0x0041CFF0, "WH_UnForceUnit", 1, { }
0x0041D040, "WH_ExcludeUnit", 1, { }
0x0041D080, "WH_IncludeUnit", 1, { }
0x0041D0D0, "WH_TemporyUnitSet", 1, { }
0x0041D120, "WH_TemporyUnitClear", 1, { }
0x0041D170, "WH_UnitIsGoingSet", 1, { }
0x0041D1C0, "WH_UnitIsGoingClear", 1, { }
0x0041CAB0, "WH_Book", 0, { }
0x0041C9D0, "WH_MeetingWait", 0, { }
0x0041CB40, "WH_InitDebrief", 2, { }
0x0041CB70, "WH_Debrief", 0, { }
0x0041CAE0, "WH_SaveGame", 0, { }
0x0041CC00, "WH_Delay", 1, { }
0x0041CCE0, "WH_Pause", 1, { }
0x0041CBE0, "WH_SetDeafultSaveName", 1, { 1 } // "text"
0x0041D640, "WH_AddMagic", 1, { }
0x0041D870, "WHMTG_DisplayBitmap", 3, { 1 } // ".bmp"
0x0041D8A0, "WHMTG_RemoveBitmap", 0, { }
0x0041D210, "WH_CheckObjective", 1, { }
0x0041D550, "WHMTG_Voice", 2, { 1 } // "VC001"
0x0041D8B0, "WHMTG_SpotAnim", 4, { }
0x0041D950, "WHMTG_ChooseInit", 0, { }
0x0041D740, "WHMTG_PlaySFX", 6, { 1, 2, 5 } // "stay", "continue", "H_KZ001"
0x0041D720, "WHMTG_StopSFX", 1, { }
0x0041D980, "WHMTG_StopAllSFX", 2, { }
0x0041D9A0, "WHMTG_PlayMusic", 1, { }
0x0041D9C0, "WHMTG_SetMusic", 1, { 1 } // ".fsm"
0x0041D9E0, "WHMTG_SetBackground", 0, { }
0x0041D2A0, "WHMTG_Speak", * // var arg???
0x0041D360, "WHMTG_SpeakNoWait", 1, { }
0x0041D420, "WHMTG_Narrate", 5, { 3 } // ".mad"
0x0041D410, "WHMTG_Wait", 1, { }


// not used ( guessed at args # )
0x0041DE30, "WH_DO", 1, { unknown }
0x0041DE20, "WH_SETLV", 1, { unknown }
0x0041DE60, "WH_LOOP", 0, {}
0x0041DEC0, "WH_BREAK", 0, {}
0x0041C910, "WH_Test", 1, { unknown }
0x0041D9B0, "WHMTG_StopMusic", 0, {}
0x0041D280, "WHMTG_AddBitmap", 1, { unknown }
0x0041D250, "WH_SetObjective", 2, { unknown }
0x0041D690, "WH_RemoveMagic", 1, { unknown }
0x0041CDA0, "WH_ShowMouse", 1, { unknown }
0x0041CD90, "WH_HideMouse", 0, {}
0x0041CBA0, "WH_Picture", 1, { unknown }


// not implemented
0x0041DA00, "WH_Narration"
0x0041DA10, "WH_WriteTextToFile"
0x0041DA20, "WH_SetUnitVar"
0x0041DA30, "WH_ReadUnitVar"
0x0041DA40, "WH_DisableAutosave"
0x0041DA50, "WHMTG_StartAnimAsync"
0x0041DA60, "WHMTG_StopAnim"
0x0041DA70, "WHMTG_PlayAnim"
0x0041DA80, "WHMTG_LoadHeads"
0x0041DA90, "WHMTG_ShowHead"
0x0041DAA0, "WHMTG_AddOption"
0x0041DAB0, "WHMTG_ChooseOption"
0x0041DAC0, "WHMTG_HideHead"
0x0041D9F0, "WHMTG_PlaySample"
0x0041DAD0, "WHMTG_PlaySampleNoWait"
0x0041DAE0, "WHMTG_LoadDots"
0x0041DB10, "WHMTG_PlayDots"
0x0041DB20, "WHMTG_WaitForDots"
0x0041DB30, "WHMTG_FinishDots"
0x0041DB40, "WHMTG_ContinuePrompt"
0x0041DAF0, "WHMTG_SetResult"
0x0041DB00, "WHMTG_RemoveBitmap"
0x0041DB50, "WHMTG_ResetDotList"

/////

int WHMTG_Narrate(         
   DWORD head_id
   DWORD head_animiation_id
   CHAR* psz_mad_name
   DWORD unused // always 0 in script and ignored in func
   DWORD keep_highlight
);


Logged
Ghabry
Developer
*
Offline Offline

Posts: 1020



View Profile
« Reply #3 on: December 01, 2013, 01:36:57 PM »

Most of them is already at http://wiki.dark-omen.org/do/DO/WHMTG/OpCodes

The WHMTG struct starts in the savegame header at 0xBC and has a size of 0xDC
Some parts of the struct can be easily figured out by looking at a debug print function at 0x40ED80. Any idea what LV means?

The program counter is at 0x00 of the struct, the script entry point at 0x08. (my data could be wrong)
For my first savegame (saved before border counties mission) the entry point is
0x4C3D90 and PC 0x3A. But by looking at other savegames that data is always the same so I lack some information there :/

But you are right, it can't be hardcoded, you can e.g. load English savegames in a German Dark Omen version.
Logged

aqrit
Developer
*
Offline Offline

Posts: 85



View Profile WWW
« Reply #4 on: December 01, 2013, 06:48:57 PM »

I saw that wiki page, I posted this here to show the arg count and arg types for the functions

the LV is "the" location that WH_IF, WH_UNTIL, and WH_ReadVariable, access... (as you know) ... state machine... Local Value maybe?

thanks for the info about the save file.
0xBC + 0x00 is the program counter
0xBC + 0x08 is the start script
0xBC + 0x0C is the end script

however the start script and end script are ignored. ( hex-edited them to 0xFFFFFFF )
so the exe must use its own values ( or possibly difference the two then align 4 and adjust :p )

004C3C48 + 3A * 4 = 004C3D30 ( aka right after the WH_SaveGame ln the english version )

004C3D90 is the script start in the german version?

« Last Edit: December 01, 2013, 07:02:29 PM by aqrit » Logged
Ghabry
Developer
*
Offline Offline

Posts: 1020



View Profile
« Reply #5 on: December 01, 2013, 10:40:13 PM »

Okay I had something else for 0x0C, but yours looks more correct.

I compared 10 different savegames and excluding the stack (20 ints starting from 0x1C) they are all the same. (I dont think that the stack is used for loading) So the actual location is stored somewhere else Sad

No idea where the script starts in the German version, never checked.
Logged

aqrit
Developer
*
Offline Offline

Posts: 85



View Profile WWW
« Reply #6 on: December 02, 2013, 08:35:07 AM »

I checked, 004C3D90 is start script in the German version.

The save script is a sub-function so the relative return address [program counter]  ( marking where we really are in the campaign ) is on the stack. The stack IS restored from the save file. The script entry points are hardcoded in the executable and need to be hooked.

 Tongue
« Last Edit: December 02, 2013, 08:41:01 AM by aqrit » Logged
Ghabry
Developer
*
Offline Offline

Posts: 1020



View Profile
« Reply #7 on: December 02, 2013, 01:50:25 PM »

Can you extend this further? How is the address of that WHMTG "save script" sub-function?
Logged

aqrit
Developer
*
Offline Offline

Posts: 85



View Profile WWW
« Reply #8 on: December 03, 2013, 12:56:13 AM »

I think we lost track of what each other are talking about...

I assume...
two sub-scripts can save the game one is 0x4C3C50 and the other is 0x4C3CF0
in a sav file the program counter will always point to after the call of WH_SaveGame in one of those two functions
when the script resumes at the program counter it will run until it hits the RETURN statement
in which case it pops the return address from the stack and continues from there.
just like a real function call

anyways,
here is a "compilable" dump of the default script
http://bitpatch.com/downloads/default_WHMGT.7z
now all we need is a compiler :p


edit: already found a bug: Voice has two args
though it seems the second one is ignored.
« Last Edit: December 03, 2013, 02:12:33 AM by aqrit » Logged
Ghabry
Developer
*
Offline Offline

Posts: 1020



View Profile
« Reply #9 on: December 03, 2013, 03:21:31 AM »

I know how a stack works Wink. But thanks for the additional notes, now it makes more sense how the program counter gets the correct value.

The script dump looks great. Have to write an assembler for the mod selector now...

Minor nit-picking:
Can you replace the first argument of IF/UNTIL with !=, ==, <, >?
1: LV == val, 2: LV != val, 3: LV < val, 4: LV > val

And maybe a feature suggestion:
You probably noticed that ClearVariables overwrites 96 bytes at 0x00537FF0. [That is in the savegame header and I called that "Savegame progress struct".
You can view that one in the Wh32Edit header editor (also see savegame header base source)]

And all SetVariable/ReadVariable calls are (4 byte aligned) in that 96 bytes (SetVariable doesnt check where it writes *uugh*). Maybe use numbers from 0-23 instead of the hex values when a write is in that range. Makes editing the script easier.
« Last Edit: December 03, 2013, 03:28:10 AM by Ghabry » Logged

aqrit
Developer
*
Offline Offline

Posts: 85



View Profile WWW
« Reply #10 on: December 08, 2013, 03:27:59 AM »

 Wink
https://github.com/Ghabry/Dark-Omen-Mod-Selector/pull/1

Script Compiler
Completely untested beyond the first mission.

I couldn't build the mod-selector with my C++ compiler,
so I'll leave it to Ghabry to hook it into that.
Code:
ImportScript( "whmtg.txt" );

also updated:
http://bitpatch.com/downloads/default_WHMGT.7z
with a new script and my quickly-made default script disassembler.

I'm sure some people are less than amused by the way I write C++...
but oh well. Cool


edit:
If it is to be used "on-the-fly" then something needs to be changed about the VirtualProtect call in ImportScript()
where it installs its hooks
« Last Edit: December 08, 2013, 04:38:38 AM by aqrit » Logged
Ghabry
Developer
*
Offline Offline

Posts: 1020



View Profile
« Reply #11 on: December 08, 2013, 12:34:47 PM »

My program should compile with Visual Studio 2010 (or newer).

I don't care much about code quality in that project, I'm just happy that it works Wink

Quote
// so fun fact: OutputDebugString() isn't suitable
// because that gets spammed with complaints of heap corruption by dark omen / the kernel
Yes, the corruption spamming after battle end is great... I wonder why this doesnt crash

Update:
The compiler works for me, too. At least in the first mission, not tested further. Great work aqrit.
« Last Edit: December 08, 2013, 07:57:55 PM by Ghabry » Logged

olly
Global Spokesperson
*
Offline Offline

Posts: 2301



View Profile
« Reply #12 on: December 08, 2013, 09:23:33 PM »

Amazing Script Compiler, many Thanks Aqrit! It will really open all sorts of possibilities for Dark Omen. So far, I've been shown how to mod the Tutorial map loading sequence, so it opens the Trading Post level instead.

LABEL TUTORIAL
    ForceUnit 1
    Deploy
    Battle "B1_01" 21
    GameOver

Smiley

 
Logged

and back in Nuln, the ageing Graf Berhardt smiled his secret smile of pride whenever he heard the latest tales of his eldest son's ever growing chain of glorious victories -(sothr manual)
Ghabry
Developer
*
Offline Offline

Posts: 1020



View Profile
« Reply #13 on: December 08, 2013, 09:41:23 PM »

Dread King and his friends having a quick chat before attempting to conquer the world.



* darktalk.jpg (55.4 KB, 637x480 - viewed 924 times.)
Logged

Jeronimo
Night Goblin Shaman
*
Offline Offline

Posts: 542



View Profile
« Reply #14 on: December 09, 2013, 04:40:38 PM »

Good one Ghabry. Smiley
Logged
Pages: [1] 2   Go Up
Print
Jump to: