I don't know anything about the NLS or Localization stuff. BlackBox would be a better source of info on that.
For LoadLibraryNew, I believe "prototype" is being used in the sense of "function declaration" (as opposed to function definition). I'd have to look at the code to know what it's doing, though it's pretty common practice to hook LoadLibrary when patching stuff in memory and writing extension modules. It's the earliest and most reliable point at which you can hook and control a dynamically loaded library.
Thanks. It didn’t occur to me that prototype means function declaration. All the localization references are commented out, so I’ll just document a bit more and leave it where it is in case it gets picked up on again later.
This strikes me more as a limitation of the user interface and parsing of the command line, rather than a backend issue. The backend might very well support loading multiple mods. In particular, the INI mod loader does this. You'd just need a way of specifying multiple modules at the command line. Maybe the option can take a list, or maybe the option can be specified multiple times.
Yes, this is what I’m getting at. It wouldn’t be difficult to expand to accept multiple mods as a list. The real question is how many mods do we want to allow loading through the command line? There is a lot of potential for making a mess by combining them. Besides the obvious mod 1 and mod 2 both touch the same piece of the game, I was thinking about multiplayer sync.
What if someone rewrites the color mod and assigns it to 0.0.1 and then someone makes a multitek2 multiplayer scenario and assigns it 0.0.2. Both mods may be compatible, but which SerialNumber do they use when both are loaded? I think the right answer is that a third mod is created, say multitek2Color that takes 0.0.3. It can then deal with integrating them both as needed.
We can warn devs in the op2ext ReadMe of these problems. We can also limit to one mod to keep it less likely that conflicts arrive. Some mods may be pretty safe to combine if they affect different parts of the game and don’t really create a multiplayer sync issue.
All that being said, I’m okay punting multiple command line mods until a different release even if we want it (unless it is a big deal to support for someone in the near future). I can go in and clean the code some more so it is clear that only one mod is getting loaded. From there it should not be too difficult to expand as needed. (I did rewrite the code to use std::string among other things, but the original code also only loaded one mod.)
I had a few concerns come to mind recently. While converting the OP2Helper project, I found I was able to run the source through the MinGW compiler, but it wouldn't link due to differences in name mangling. This got me thinking, there is a reason the interface for op2ext uses C rather than C++, the C ABI is highly portable, and allows modules to be written in a variety of languages using a variety of compilers. The C++ ABI is much less portable, and unlikely to interface well with other langues, and possibly not even with other C++ compilers.
I’ve actually been thinking about this as well. I noticed that FreeImage’s public interface only uses the C ABI, probably for similar reasons as you brought up. Currently all of the EXPORT functions remain C functions, except for the new GetGameDirectory. I’d like to remove the dllexport from this function. Then we can create a new version of the function that uses the C ABI for export, taking into account input for the buffer size.
In regards to the EXPORT functions, if we eliminate public.h, and declare the EXPORT functions in op2ext.h instead, would this be considered a breaking change? If so are we okay with this?
The other reason I’m not a fan of public.h is because we are declaring the functions in 2 separate places. I’m not sure if there is precedence for this, but it seems like a possible source of some insidious bugs.
If we want to remove public.h, I’m happy to work this, if not, then I’m happy to leave it alone for this release.
We may want to consider switching to a branching work model on this project, with pull requests for code review. Though that could be a painful transition when making extensive changes to so many areas of the project. A painful transition that will likely teach you about separating concerns and packaging commits nicely.
I would probably fair better working with branches and waiting on code reviews than you are giving me credit for. I’m fine with code reviews and branches, but it would require someone who can reliably compile the code be available to review the code on a regular basis.
Perhaps if we rework the filesystem code to something compatible with minGW, you could fill this role? Is anyone interested? Being hobby time for everyone, this might be too much of a commitment regularly?
I'm looking to wind down work on op2ext, so besides working on eliminating public.h (if we want to do that) I'm looking at moving into more cleaning and testing than expanding functionality and ripping apart.
I would like to create a sample module simply designed to test most functionality of op2ext, like the EXPORT functions and the different initialization and cleanup hooks. Maybe this should be the next pair programming session? I should be able to squeak this in on the Sunday, 17DEC if someone is interested. I'll post in Pair Programming about it in the near future.
IpDropDown
I am having difficulty forming IpDropDown into a class. Something to do with the code below. I don’t really understand what is happening as it looks like we are assigning functions to DWORDs (unsigned int) and then pushing them into Outpost 2’s executable’s memory. Anyways, I can get the class to compile, but the program would blow up when running.
BOOL __stdcall EnableWindowNew(HWND hWnd, BOOL bEnable)
{
. . . MORE CODE . . .
}
unsigned long __stdcall inet_addrNew(const char* cp)
{
. . . MORE CODE . . .
}
// Data constants for InstallIpDropDown
DWORD newEnableWindowAddr = (DWORD)EnableWindowNew;
DWORD newInetAddr = (DWORD)inet_addrNew;
DWORD* populateComboBoxAddr = (DWORD*)0x004197C1;
DWORD* saveIpTextAddr = (DWORD*)0x004C0E36;
void* nopDataAddr = (void*)0x0041988F;
void InstallIpDropDown()
{
// patch the call to EnableWindow so we can add strings.
Op2MemSetDword(populateComboBoxAddr, (int)&newEnableWindowAddr);
Op2MemSetDword(saveIpTextAddr, (int)&newInetAddr);
Op2MemSet(nopDataAddr, 0x90, 14);
}
All that being said, perhaps it would be best to just leave IpDropDown alone. As Hooman mentioned earlier, it would probably make more sense to make it a separate module that op2ext loads into Outpost 2 as opposed to including it in op2ext. Maybe a project for another release?
So, I’m looking at dropping further work on IpDropDown off the list.
Hooman, is the Outpost2DLL code on GitHub stable yet? If so, I’ll work on setting up a submodule.
I’ll place the submodule in ./Outpost2DLL. Unless we want some sort of different folder structure like ./submodules/Outpost2DLL. I’m happy with either setup.
Current task list * Remove public.h and push declarations for EXPORT functions to op2ext.h (if this is what we want?)
* Include in ReadMe.txt a section on recommended use of SetSerialNumber for mods involving multiplayer maps that will not interfere with incrementing the main game’s version number over time.
* Include more details in ReadMe.txt on how to load modules using op2ext.
* Include details in ReadMe.txt on available features in op2ext for developing and hooking modules into Outpost 2
* Write a post build script that auto-zips the ReadMe.txt, dll, and PDB file for posting on GitHub.
* Set Outpost2DLL project as a submodule once it exists on GitHub.
-Brett