Chapter 6. External files, Windows

Now, having a cross-platform solution, we can start thinking about our Graphics Engine. However, the first question will be not about graphics, but how we gonna handle data files (like shaders, models, textures and so on)?

For example, currently our triangle model (verts array) and 2 shaders (1 vertex and 1 fragment) are hardcoded in TheApp.cpp. Obviously not the best solution, but was quite reasonable for simplicity. In our turn, we want all data files outside of the executable.

Let's make a test sample.

1. In Windows File Explorer (not in VS), in C:\CPP\a999hello\ folder add sub-folder “dt” (for "data").

In a Text Editor create a simple txt file for testing, just couple lines, such as

test 1
test 2


Save it as C:\CPP\a999hello\dt\test0.txt


2. Now we need to pass this file to the executable. There are couple options such as including files into "resources" or directly into the project. However, we don't want data files "inside" of the project. Instead, we will instruct Visual Studio to copy data folder to the same place as executable.

Start Visual Studio, open C:\CPP\a999hello\pw\pw.sln solution.


3. Open pw project Properties, All Configurations / x64,

Configuration Properties -> Build events -> Post-Build Event, open Command Line -> Edit.

Add (copy-paste) following command:

xcopy "..\dt\*.*" "$(TargetDir)dt\" /E /R /D /y

Then - Ok, Apply, Ok.

Clarification:

  • xcopy - doesn't really need an explanation (I guess)
  • ..\dt\*.* – copy what and from. Refers to C:\CPP\a999hello\dt, just in relative form from pw project root folder
  • $(TargetDir)dt\ - copy to where. We want a copy of “dt” folder in the same place as executable

Keys:

  • /E - Copy directories and subdirectories, including empty ones
  • /R - Overwrite read-only files
  • /D - Copy files only if newer
  • /y - Don't ask if exists/overwrite

4. Now pick Debug config in VS top menu and run the program (green arrow). Runs. Now - close the program OR: VS top menu -> Debug -> Stop Debugging.

Go to Windows File Explorer, open C:\CPP\a999hello\pw\x64\Debug. Perfect, the copy of the "dt" folder with all it's content is there, in the same folder as HelloWindows.exe:


5. Now let's try to read our test file (test0.txt). For that we need to specify the file's full path. However, it can vary depending on where program was installed. So, will need to find it out, to detect application's root folder.

The code:

    //find application root folder
    char path[256];
    GetModuleFileNameA(NULL, path, 256);
    filesRoot.assign(path);
    int lastSlashPos = filesRoot.find_last_of('\\');
    if (lastSlashPos < 0)
        lastSlashPos = filesRoot.find_last_of('/');
    filesRoot = filesRoot.substr(0, lastSlashPos);
    mylog("App path = %s\n", filesRoot.c_str());

Insert it in main.cpp right before theApp.run().

Scroll up and add to the end of include section:

#include <windows.h>
#include <string>
#include <fstream>      // std::ifstream

std::string filesRoot;

Run. Result (in Console window):

App path = C:\CPP\a999hello\pw\x64\Debug

Just what we need.


6. Now let's try to open our test file and read it.

Right before theApp.run() add following code:

    //reading test file
    std::ifstream myFile(filesRoot + "/dt/test0.txt");
    std::string line;
    if (myFile.is_open())
    {
        while (getline(myFile, line))
            mylog("%s\n", line.c_str());
    }
    else mylog("Unable to open file\n");

Run. Result:

App path = C:\CPP\a999hello\pw\x64\Debug
test 1
test 2


7. After removing testing code, main.cpp (with new filesRoot variable and with corresponding code) is:

#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>

#include <stdlib.h>

#include "TheApp.h"
#include "platform.h"

#include <windows.h>
#include <string>

std::string filesRoot;

static void error_callback(int error, const char* description)
{
    mylog("Error: %s\n", description);
}

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GLFW_TRUE);
}

TheApp theApp;
GLFWwindow* myMainWindow;

int main(void)
{
    glfwSetErrorCallback(error_callback);

    if (!glfwInit())
        exit(EXIT_FAILURE);

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);

    myMainWindow = glfwCreateWindow(640, 480, "Hello Windows", NULL, NULL);
    if (!myMainWindow)
    {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    glfwSetKeyCallback(myMainWindow, key_callback);

    glfwMakeContextCurrent(myMainWindow);
    gladLoadGLES2Loader((GLADloadproc)glfwGetProcAddress); //gladLoadGL(glfwGetProcAddress);
    glfwSwapInterval(1);

    //find application root folder
    char path[256];
    GetModuleFileNameA(NULL, path, 256);
    filesRoot.assign(path);
    int lastSlashPos = filesRoot.find_last_of('\\');
    if (lastSlashPos < 0)
        lastSlashPos = filesRoot.find_last_of('/');
    filesRoot = filesRoot.substr(0, lastSlashPos);
    mylog("App path = %s\n", filesRoot.c_str());

    theApp.run();

    glfwDestroyWindow(myMainWindow);
    glfwTerminate();
    exit(EXIT_SUCCESS);
}

Replace main.cpp code by this one.

Save All.

Cool. What's next? - external data files on Android.


Leave a Reply

Your email address will not be published. Required fields are marked *