Outpost Universe Forums

Projects & Development => Outpost 2 Programming & Development => Topic started by: Vagabond on March 29, 2016, 10:00:13 AM

Title: Adding odasl to LevelsAndMods API section
Post by: Vagabond on March 29, 2016, 10:00:13 AM
I'm using odasl.h and odasl.lib in most all my single player maps to create a briefing dialog. Odasl is also used by PlymouthColdWar and TheMission. I believe we should move it into the API for common reference. I think it is a general Sierra Header file and possibly not specific only to Outpost 2. BlackBox seems to have done the original work on it, since it first showed up in PlymouthColdWar as far as I can tell.

We could just put it in API/odasl, but I'm not sure if would be more appropriate as a subfolder to another API project. Below is the contents of odasl.h and attached is the odasl.lib.

As a side note, an article on adding briefing screens to missions: http://wiki.outpost2.net/doku.php?id=op2_sdk:textdialogbox (http://wiki.outpost2.net/doku.php?id=op2_sdk:textdialogbox). Article needs more proof-reading and suffers from my limited knowledge of resources. I would be happy if someone with more knowledge on the subject cleaned it up, but hopefully it is useful in its current form.

odasl.h
Code: [Select]
// ODASL.DLL public interface.
// ODASL is the Sierra library that implements the ownerdraw in Outpost 2.

#ifndef ODASL_H
#define ODASL_H

#include <windows.h>

// Structure used to pass information to ODASL in wplInit.
struct wplOptions
{
ULONG structSize; // 60
ULONG unknown1; // 87 // Flags?
HINSTANCE hAppInst; // HINSTANCE OP2Shell.dll
HINSTANCE hResDllInst; // HINSTANCE op2shres.dll
ULONG startResId; // 200 // Starting resource id?
ULONG unknown3; // 0 // Palette?
ULONG unknown4; // 0 // More flags?
ULONG unknown5; // 96 // Size scale? Large values make windows huge
ULONG unknown[7]; // All 0
};

#ifdef BUILDING_ODASL
#define ODASL_API(rt) __declspec(dllexport) rt __cdecl
#else
#define ODASL_API(rt) __declspec(dllimport) rt __cdecl
#endif // BUILDING_ODASL

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

ODASL_API(int) wplInit(wplOptions *inf);
ODASL_API(void) wplExit();
ODASL_API(void) wplSetPalette(HPALETTE pal);
ODASL_API(int) wplGetSystemMetrics(int nIndex);
ODASL_API(BOOL) wplAdjustWindowRect(LPRECT lpRect, DWORD dwStyle, BOOL bMenu);
ODASL_API(HBITMAP) wplLoadResourceBitmap(HMODULE hModule, LPCTSTR lpName);
ODASL_API(int) wplManualDialogSubclass(HWND dlg);
ODASL_API(void) wplEnable();
ODASL_API(void) wplDisable();


#ifdef __cplusplus
}
#endif // __cplusplus

#endif // ODASL_H
Title: Re: Adding odasl to LevelsAndMods API section
Post by: Hooman on March 31, 2016, 08:02:28 AM
Huh, that's also a good idea. It looks like a separate project to me. It should probably go in it's own subfolder.

I find it's best to name a library based on what it's used for, rather than how it does it. The odasl part sounds like an implementation detail, partly because I'm not even sure what odasl stands for. I'm tempted to suggest "MissionBriefing" for an API name if it's used for pre-game mission briefings. Looking at the code though, I'm not sure that's a good description. The code might be used for other things. It looks to be geared towards skinning dialog boxes. Maybe "Outpost2Theme" would be a better name?


That's a rather extensive wiki page you've created. I'm impressed by all the stuff you've worked on recently.
Title: Re: Adding odasl to LevelsAndMods API section
Post by: Vagabond on March 31, 2016, 08:41:24 PM
Perhaps the name Outpost2Dialog?

I can also put the code required to initialize the mission briefing dialog in the folder, briefing.cpp. It doesn't change between scenarios either. Really, the only thing that changes is briefing.rtf. The definition of the dialog box from the resource script file and resource.h doesn't change between scenarios either, but I'm not sure it would be smart to put the resource script file and associated resource.h in a project that is not the main level project that produces the dll.

I had a question on the header files though. In Black Box's original implementation of the briefing screen, there is no header file, just briefing.cpp. I added a header file in my implementation (mostly because I was troubleshooting and not knowing what I was doing at the time.)

I think the benefit of not using a header file is that it speeds up compile time. Are there other pros/cons? Is there a rule of thumb on when a header file should or should not be used? For example, I noticed it is common in Outpost 2 scenarios to separate out base scripting into a large function and place it in a separate .cpp file without giving it a header. Then just calling extern void BaseScriptingFunction() in main.cpp.

briefing.cpp from BlackBox
Code: [Select]
#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
#include <windows.h>

// These are needed for the mission briefing
#include <richedit.h>
#include "resource.h"
#include "odasl\odasl.h"

// These deal with the briefing screen
extern HINSTANCE hInst;
LRESULT CALLBACK DialogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);


void ShowBriefing()
{
    // OP2 turns off the skinning before the game loads.
    // We have to re-enable it (otherwise the briefing box will look ugly).
    wplEnable();
    // Show the dialog box, using the shell window as the parent
HWND shellWnd = FindWindow("Outpost2Shell", NULL);
DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_MISSIONINFO), shellWnd, (DLGPROC)DialogProc, NULL);
    // Turn off the skinning again (probably not needed but a good idea to do it anyway)
    wplDisable();
}

LRESULT CALLBACK DialogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    ...
}
Title: Re: Adding odasl to LevelsAndMods API section
Post by: Hooman on April 01, 2016, 12:24:29 AM
Sounds like a good name.

I think it makes sense to include the mission briefing code, particularly if it doesn't change between levels. You can put all the related files in the project folder. At the very least, if it's inappropriate to use them directly from the library, you could put them in a Sample folder, so people can just copy out of there.


In C/C++ you can do some really strange things with header files. Part of that stems from just how incredibly simple the system works, which has lots of complicated side effects. A #include statement is equivalent to an in-place copy-paste of the included file's contents. That's it. A C/C++ compiler doesn't know anything particularly special about header files. It only knows how to compile C/C++ code, which it expects in a .c or .cpp file. What goes in a header file is just convention, as is the extension ".h".

What you should put in a header file is largely dictated by what a C/C++ compiler will do if it encounters the same code in two separate "compilation units" (roughly, two separate .cpp files, along with all their #included content pasted in). A struct or class declaration can be repeated without problem. It tells the compiler the format of a structure, but does not allocate any space for it. It only dictates how other code will interact with such objects. On the other hand, a function which is compiled and takes up space in the code section should not be repeated. A global variable which takes up space in the data section should not be repeated. You can declare an extern global variable in a header file, which lets the compiler know a variable with a given name and type is defined somewhere, but the actual definition should only appear once.

If a header file declares a single function, there isn't much savings in typing to declare that function at the start of a .cpp file that needs it, as opposed to including a header file with the declaration. In either case, it's one line of code that is basically pasted into the start of the .cpp file. Hence, for a single function, particularly one with no parameters, people will sometimes omit a header file.

If you want to see what the compiler sees, run the compiler with an option to only do the pre-precessing step. It should output a new source file with all the #includes pasted in, and all the macros expanded. The result is what the compiler actually sees, and what actually needs to be valid C/C++ code. A C/C++ compiler actually implements two languages. The C/C++ pre-processor language, and then the C/C++ language. They can have some funny interactions too.

Of course there's nothing stopping you from abusing the #include system. In fact, I highly recommend it. Not in actual practice of course. Just in fun little experiments. There's no need to always stick the #include at the start of your file either. If you wanted, you could put the body of a function in a separate file, write the start and end of a function in your main file, and then #include the body of the function from inside the wrapper. Not much point, but it works.

If you really want to twist your brain about, read up on X-Macros (https://en.wikipedia.org/wiki/X_Macro). By making use of #define before #include, you can include header files more than once, with a different effect each time. This is actually a very clever and sometimes useful way of abusing the system. This is some acceptance behind this method when it's done in a clean way. There are also many code obfuscation examples when this is done in a non-clean way.

You can find some examples of X-Macros using #include (https://en.wikibooks.org/wiki/C_Programming/Preprocessor#X-Macros) on WikiBooks.

It's a little off topic, but fun, and educational. If you can get your head around that stuff, you should be better able to reason about header files, and what should go where.
Title: Re: Adding odasl to LevelsAndMods API section
Post by: Vagabond on April 01, 2016, 08:38:30 PM
I added Outpost2Dialog to the APIs section. It has odasl as a subfolder and a sample subfolder.

I put a sample file, BriefingDialog.cpp in the sample folder. I went ahead and just went with the .cpp file without a header file. Having just one file for the sample is better, especially since only one function requires an external declaration and it matches how BlackBox originally used it.

There is also a quick readme file with some basics and a link to the wiki article.

@Hooman,

Thank you for the information on header and cpp files. I think I've got some questions on requirements and best practices for function declarations vs definitions, but need to do some more thought and research first. Trying to find a good intermediate C++ book right now. The beginner book I bought (C++ Without Fear, Brian Overland) was helpful, but I find myself at the end of sections with many questions that it hasn't answered.
Title: Re: Adding odasl to LevelsAndMods API section
Post by: Hooman on April 02, 2016, 03:10:14 AM
Do you have a note about declaring the one method in the file it will be called from? A note about that might be useful if there is no header file to include.


I'm amused by the title "C++ Without Fear". It seems like a strangely appropriate name considering how many dark corners the language has. Mind you, for simple starter the stuff, it doesn't need to be too bad. You learn it as you go. A lot of the detail doesn't matter that much for the vast majority of tasks.

I'm also reminded of the somewhat tongue-in-check website:
C++ FQA Lite (http://yosefk.com/c++fqa/) (Frequently Questioned Answers)
It's a somewhat angry sounding take on many problems with C++. It seems to be more aimed at convincing people not to use C++, rather than teaching them how to use it better. It was written in reply to a C++ FAQ Lite, which has since been merged into another FAQ article. The original material would be more useful for learning how to better use the language.

You can try reading the C++ FAQ (https://isocpp.org/faq). I think that might be the merger of the original article. The section on the "friend" keyword might be of interest.