Imperator to CKII Converter - Development Thread (not for troubleshooting)

  • We have updated our Community Code of Conduct. Please read through the new rules for the forum that are an integral part of Paradox Interactive’s User Agreement.

Idhrendur

Keeper of the Converters
108 Badges
Feb 27, 2009
12.005
4.226
  • Hearts of Iron 4: Arms Against Tyranny
  • Hearts of Iron IV: No Step Back
  • Crusader Kings II
  • Hearts of Iron IV: By Blood Alone
  • Victoria 3 Sign Up
  • Stellaris: Nemesis
  • Sengoku
  • Pillars of Eternity
  • Tyranny: Archon Edition
  • Europa Universalis IV
  • March of the Eagles
  • Victoria 2
  • 500k Club
  • Stellaris: Galaxy Edition
  • Hearts of Iron IV: Colonel
  • Shadowrun Returns
  • Imperator: Rome Deluxe Edition
  • Crusader Kings III: Royal Edition
  • Commander: Conquest of the Americas
  • Darkest Hour
Looking to get the converter or for help? This is the wrong place. Instead head over to the release thread!

As the lead behind the previous thread has disappeared, but there's still interest, I'm starting a new one.

However, I'm not planning to do the work, rather I'll instruct and guide others. You don't have to have any modding or programming knowledge to start, I'll provide guides. Though experienced sorts are more than welcome!

There's a Discord, so if you're helping out (including working on the programming guides), message me for the link.

More will be said here in the future.
 
Last edited:
  • 1Haha
  • 1Like
Reactions:
Last edited:
Reserved
 
Reserved
 
Reserved
 
Programming Guide - Hello World

You'll need to get and install Visual Studio Community edition (any parts relating to C# and C++ will be needed).

Once Visual Studio has installed, open it. Choose 'Create a new project', then 'Empty Project', and click Next. Call the project HelloWorld and pick a location for it, then click Create.
More will open up. Right-click on Source Files and choose Add -> New Item. Choose C++ File and name it main.cpp, then click Add. In the file that opens, enter the following code (I recommend carefully retyping instead of copy-pasting, it'll help you remember some things later):

Code:
#include <iostream>


int main()
{
   std::cout << "Hello world, this is the start of a converter!";
}

Once you've done so, press F5 or choose Debug->Start Debugging from the menus. A window should open and say some things. One of which should be familiar. :cool:

Once you've done all that, let me know and I'll explain what the heck all that code is and start you on the road to making in more exciting and converter-like.
 
Programming Guide - Explaining Hello World

Okay, time to explain the Hello World code. Let me know if this is confusing, and I'll break it down a little more.

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

Code:
#include <iostream>

Like how some Paradox games feel on launch (or how their launch feels looking back), C++ doesn't have much content direct content in the language. But it's easy to pull new features in using the #include instruction. Items in angle brackets <> afterwards are things in defined directories, generally official things (so, like DLC). But you can also put in items in quotes, which is like adding a mod. It'd look like this:

Code:
#include "myAdditions.h"

It's frequent you'll have a stack of these at the top of a file. Also, starting next year the particulars will change, but the fact that you need to add content to the programming language will remain.

iostream is a bunch of functionality related to inputting and outputting stuff.

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

Code:
int main()
{
}

This is an example of a function. Like a Vic2 factory or a Factorio building, it takes in some stuff, does work on it, and returns some stuff.

  • int - what the function outputs, in this case a number (in whole number — integer — format)
  • main - the name of the function, so it can be referenced. Like Factorio constructions, you use functions to build bigger, better ones.
  • () - what the function uses as inputs. In this case nothing. There will be better examples later.
  • { - the beginning of the function itself
  • } - the end of the function
I mentioned the name being referenced. main is a special function, one that the thing running your program references when it starts your program. So this is where everything starts running.

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

Code:
std::cout << "Hello world, this is the start of a converter!";

When you ran your program, you saw most of this line, so clearly this outputs stuff. C++ uses what it calls streams for output. You use the characters << to put stuff into a stream, and that causes it to go to the right place (back to the command line, to a file, to a printer, to be stored somewhere, etc). Similarly, the characters >> can get stuff out of a stream. More on that another time.

std::cout is a special stream that outputs to the command-line. Most programming languages have an equivalent. It's useful for a short time here, but we'll be moving on from it very quickly.

And then there's the text you output. Notice it's in quotes. That tells the language that it's a bunch of text, not instructions to try to follow.

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

And that's your first program explained! Ask any questions you might have, and I'll put together instructions for how to make something more exciting.
 
Making a .mod file

Let's get a converter actually started by outputting a mod file.

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

Open your Hello World program. Then replace the #include line with
Code:
#include <fstream>

Instead of the earlier stuff that allows output to the command line, this one is for outputting to files.

Your one line inside the main function probably has some red squiggles under it now. That's Visual Studio saying there's something wrong (in this case, we got rid of the functionality that allows std::cout). Just delete that line, we're doing something new.

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

Add the line
Code:
std::ofstream modFile{"CK2tester.mod"};

This tells the computer to create a file.
  • std::eek:fstream - That means 'make an output file stream'. So, a stream (we can put stuff in or take things out), that's output (okay, we put stuff in and it ends up somewhere), to a file.
  • modFile - what we're calling the file within the program. This will be important for the next line.
  • ( - the beginning of the input(s) to make the file be how we want it
  • "CK2tester.mod" - what we want the file to be named on the computer itself
  • ) - the end of the inputs to the file
If you were to build and and run this program now (feel free to do it!), you'd see this file get created.

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

Add the line
Code:
modFile.close();

This closes the file, as if you had closed it in more familiar programs like Word. This was done automatically when the program exited before, but it's polite to put your trash in the bin rather than throwing it on the ground.

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

Between those lines, add the lines
Code:
modFile << "name = \"Converted - CK2tester\"\n";
modFile << "path = \"mod/CK2tester\"";

Like in Hello World, this is putting things into the stream, which puts them into the file. These are the two lines CK2 needs in a mod file, so they're what we're putting. But there's some odd stuff:
  • \" - We need the line in the mod file to have a quote. But just a quote in our code would tell the program we're at the end of our text. So instead we have this, which tells the program we meant to put the quote character in.
  • \n - this means end a line. Yeah, it's weird, but that's how it's done.
--------------------------------------------

You should be able to build and run your program now, and it'll create CK2tester.mod with some stuff in it. Copy this to your CK2 mod directory (My Documents\Paradox Interactive\Crusader Kings II\mod), and add a folder called CK2tester. Then open up CK2 and check your list of mods. There should now be one called 'Converted - CK2tester'.
 
Add the line
Code:
std::ofstream modFile{"CK2tester.mod"};
This tells the computer to create a file.
  • std::eek:fstream - That means 'make an output file stream'. So, a stream (we can put stuff in or take things out), that's output (okay, we put stuff in and it ends up somewhere), to a file.
  • modFile - what we're calling the file within the program. This will be important for the next line.
  • ( - the beginning of the input(s) to make the file be how we want it
  • "CK2tester.mod" - what we want the file to be named on the computer itself
  • ) - the end of the inputs to the file
I wasn't sure if "CK2tester.mod" should be in braces or parentheses, but I guess it doesn't matter?
 
I wasn't sure if "CK2tester.mod" should be in braces or parentheses, but I guess it doesn't matter?

Either works, but using curly braces for objects and variables (more on those later) is preferred. Both for clarity, and to avoid a rare but annoying bug.
 
Making a mod folder

Okay, we also need a mod folder to go with the mod file.

First, some setup to make Visual Studio happy:
In the Solution Explorer pane in Visual Studio, right-click on your project. Choose properties. On the top, there's a drop down that'll probably say either Debug or Release. Choose All Configurations. Then in the main pane, choose General, then look under C++ Language Standard. Set it to Preview, then click Okay.

At the top of the file, add
Code:
#include <filesystem>

Then before the modfile lines, add this code:
Code:
std::filesystem::path modPath{ "output/CK2Tester" };
std::filesystem::create_directories(modPath);

This is the relatively new filesystem library, and it'll create the mod folder, plus all required folders before it. Note that path is under output. So change the modfile line to be
Code:
std::ofstream modFile{"output/CK2tester.mod"};

Now your output should end up in a folder called output, and have both the mod file and mod folder.
 
Last edited:
A Variable Mod Name

Okay, so now we've got a minimal mod, but it's always the same name. Surely we can do better?

Add this to the top of the program:
Code:
#include <string>

This will allow us to use strings, which are just bunches of text.

Add this line to the start of main:
Code:
std::string outputName{ "CK2Tester" };

This creates a variable (so named because it can vary, and actually rather unlike the variables in math). It has a type: string. And it has a name: modname. And it holds a value: "CK2Tester". Variables have those three properties, and it can be helpful to purposefully think about each of them when creating one. A variable should have a type appropriate to what you're doing (something that we'll cover is creating new types to help with this), it should have a name that's helpful to understand it's purpose, and it should have a meaningful value as often as possible.

Anyways, change the modFile text to:
Code:
"output/" + outputName + ".mod"

And the output to it to be
Code:
modFile << "name = \"Converted - " << outputName << "\"\n";
modFile << "path = \"mod/" << outputName << "\"";

And the mod folder text to be
Code:
"output/" + outputName

If you build and run the program again (delete the output folder first), it'll do the same thing as before. But try changing the text in modName - you should be able to influence the name of the resulting mod. Hopefully that illustrates the power of variables.

But it'd be better to not to have to rebuild the program every time you wanted to change things. We'll put together a better solution for that next.
 
Last edited:
13 errors! I must be missing something. This is what I have for HelloWorld so far:

Code:
#include <fstream>


int main()
{
    std::filesystem::path modPath{"output/CK2tester"};
    std::filesystem::create_directory(modPath);
    std::ofstream modFile{"output/CK2tester.mod"};
    modFile << "name = \"Converted - CK2tester\"\n";
    modFile << "path = \"mod/CK2tester\"";
    modFile.close();
}
 
Whoops, I forgot!

To the top of the file, add
Code:
#include <filesystem>

to allow the filesystem stuff.

(adding it to the instructions post, for anyone who follows along later)
 
Whoops, I forgot!

To the top of the file, add
Code:
#include <filesystem>

to allow the filesystem stuff.

(adding it to the instructions post, for anyone who follows along later)
Added #include <filesystem>

So now I have:
Code:
#include <filesystem>
#include <fstream>


int main()
{
    std::filesystem::path modPath{"output/CK2tester"};
    std::filesystem::create_directory(modPath);
    std::ofstream modFile{"output/CK2tester.mod"};
    modFile << "name = \"Converted - CK2tester\"\n";
    modFile << "path = \"mod/CK2tester\"";
    modFile.close();
}

But I'm still getting 13 errors:

Error (active) E0276 name followed by '::' must be a class or namespace name HelloWorld 7
Error (active) E0065 expected a ';' HelloWorld 7
Error (active) E0276 name followed by '::' must be a class or namespace name HelloWorld 8
Error (active) E0020 identifier "modPath" is undefined HelloWorld 8
Error C3083 'filesystem': the symbol to the left of a '::' must be a type HelloWorld 7
Error C2039 'path': is not a member of 'std' HelloWorld 7
Error C2065 'path': undeclared identifier HelloWorld 7
Error C2146 syntax error: missing ';' before identifier 'modPath' HelloWorld 7
Error C2065 'modPath': undeclared identifier HelloWorld 7
Error C3083 'filesystem': the symbol to the left of a '::' must be a type HelloWorld 8
Error C2039 'create_directory': is not a member of 'std' HelloWorld 8
Error C2065 'modPath': undeclared identifier HelloWorld 8
Error C3861 'create_directory': identifier not found HelloWorld 8


All in the two modPath lines, 7 and 8.
 
Added #include <filesystem>

So now I have:
Code:
#include <filesystem>
#include <fstream>


int main()
{
    std::filesystem::path modPath{"output/CK2tester"};
    std::filesystem::create_directory(modPath);
    std::ofstream modFile{"output/CK2tester.mod"};
    modFile << "name = \"Converted - CK2tester\"\n";
    modFile << "path = \"mod/CK2tester\"";
    modFile.close();
}

But I'm still getting 13 errors:

Error (active) E0276 name followed by '::' must be a class or namespace name HelloWorld 7
Error (active) E0065 expected a ';' HelloWorld 7
Error (active) E0276 name followed by '::' must be a class or namespace name HelloWorld 8
Error (active) E0020 identifier "modPath" is undefined HelloWorld 8
Error C3083 'filesystem': the symbol to the left of a '::' must be a type HelloWorld 7
Error C2039 'path': is not a member of 'std' HelloWorld 7
Error C2065 'path': undeclared identifier HelloWorld 7
Error C2146 syntax error: missing ';' before identifier 'modPath' HelloWorld 7
Error C2065 'modPath': undeclared identifier HelloWorld 7
Error C3083 'filesystem': the symbol to the left of a '::' must be a type HelloWorld 8
Error C2039 'create_directory': is not a member of 'std' HelloWorld 8
Error C2065 'modPath': undeclared identifier HelloWorld 8
Error C3861 'create_directory': identifier not found HelloWorld 8


All in the two modPath lines, 7 and 8.

Ah, another dumb thing (dumb in that you have to do it, and that I forgot to mention it, not on your part).

In the Solution Explorer pane in Visual Studio, right-click on your project. Choose properties. On the top, there's a drop down that'll probably say either Debug or Release. Choose All Configurations. Then in the main pane, choose General, then look under C++ Language Standard. Set it to Preview, then click Okay. Everything should now build.
 
Ah, another dumb thing (dumb in that you have to do it, and that I forgot to mention it, not on your part).

In the Solution Explorer pane in Visual Studio, right-click on your project. Choose properties. On the top, there's a drop down that'll probably say either Debug or Release. Choose All Configurations. Then in the main pane, choose General, then look under C++ Language Standard. Set it to Preview, then click Okay. Everything should now build.
OK so that fixed the 13 errors
VS2019-C-Hello-World1.png

But...
VS2019-C-Hello-World2.png

?
 
OK so that fixed the 13 errors
VS2019-C-Hello-World1.png

But...
VS2019-C-Hello-World2.png

?

Try going back to the properties, then Advanced. Check Character Set, and if it's Use Multi-Byte, switch it to Use Unicode.