• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
nicksaiz65

How to get my program to find a folder that could be anywhere on the computer?

9 posts in this topic

Hi guys. I'm programming a level player in Blitz Plus, and it is coming along quite nicely. In fact, in the near future, I plan on releasing the file along with a level so that people can test my game. However, I don't know what I should do about the filepaths to many files that I need to load in on different computers. On my own computer, it is easy because I know exactly where the files are. I just have to tell the computer to look at the file in "C:\Users\Nick\Desktop\LEVELDATA" and so on. However, on other computers, I have no idea where the users could be saving my files. Therefore, I will need to find the location of all of the folders. I couldn't find any functions in Blitz Plus that would help me. The closest thing I could find was a CallDLL function. Would that help at all? My other option is just to save everything within the same directory, but I would rather not do that simply for the sake of neatness. Any ideas? Thanks smile.png

0

Share this post


Link to post
Share on other sites

I recently had a similar issue, and though I ended up not using the code for open/save dialogs, I still have it. 

Due to the wide-strings used by Windows, I also got these functions to convert string to wstring, which are needed by the open/save dialogs.

 

The other thing is that, as much as I tried, I couldn't get the defaultFolder to work - I ended up using these for a short time without setting a default folder... but they still work. You should be careful when using these, as these functions do block until the user either presses Open/Save on the dialog or Cancel.

 



static std::wstring s2ws(const std::string& s)
{
    int len;
    int slength = (int)s.length() + 1;
    len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); 
    std::wstring r(len, L'\0');
    MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, &r[0], len);
    return r;
}

static std::string ws2s(const std::wstring& s)
{
    int len;
    int slength = (int)s.length() + 1;
    len = WideCharToMultiByte(CP_ACP, 0, s.c_str(), slength, 0, 0, 0, 0); 
    std::string r(len, '\0');
    WideCharToMultiByte(CP_ACP, 0, s.c_str(), slength, &r[0], len, 0, 0); 
    return r;
}

static std::string OpenFileDialog (std::vector<std::string> fileTypes, std::string defaultPath)
{
    IFileOpenDialog *openDialog;
    // Create the FileOpenDialog object.
    std::string result = "";
    HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, IID_IFileOpenDialog, reinterpret_cast<void**>(&openDialog));

    // construct the array for the default file types
    COMDLG_FILTERSPEC *winFileTypes = new COMDLG_FILTERSPEC[fileTypes.size()];
    for (unsigned int i = 0; i < fileTypes.size(); i++)
    {
        std::wstring type = s2ws(fileTypes[i]);
        winFileTypes[i].pszName = type.c_str();
        winFileTypes[i].pszSpec = type.c_str();
    }
    // set the file types
    openDialog->SetFileTypes(fileTypes.size(), winFileTypes);

    // setup the default path
    IShellItem *psiFolder = nullptr;
    if (defaultPath != "")
    {
        LPCWSTR szFilePath = s2ws(defaultPath).c_str();
        HRESULT shr = SHCreateItemFromParsingName(szFilePath, NULL, IID_PPV_ARGS(&psiFolder));
            //SHCreateItemFromParsingName ( szFilePath, NULL, IID_PPV_ARGS(&psiFolder) );
        
        if (SUCCEEDED(shr))
            openDialog->SetFolder(psiFolder);
        else
            std::cout << "Error setting folder to: \"" << defaultPath << "\" - error code: " << shr;
    }


    if (SUCCEEDED(hr))
    {
        hr = openDialog->Show(NULL);
        if (SUCCEEDED(hr))
        {
            IShellItem *pItem;
            hr = openDialog->GetResult(&pItem);
            if (SUCCEEDED(hr))
            {
                PWSTR pszFilePath;
                hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);

                // Display the file name to the user.
                if (SUCCEEDED(hr))
                {
                    std::wstring wstr = pszFilePath;

                    result = ws2s(wstr);
                    // ...
                    CoTaskMemFree(pszFilePath);
                }
                pItem->Release();
            }
        }
        openDialog->Release();
    }

    delete winFileTypes;
    if (psiFolder != nullptr)
        psiFolder->Release();

    return result;
}

static std::string SaveFileDialog (std::vector<std::string> fileTypes, std::string defaultPath)
{
    IFileSaveDialog *saveDialog;
    // Create the FileOpenDialog object.
    std::string result = "";
    HRESULT hr = CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_ALL, IID_IFileSaveDialog, reinterpret_cast<void**>(&saveDialog));

    // construct the array for the default file types
    COMDLG_FILTERSPEC *winFileTypes = new COMDLG_FILTERSPEC[fileTypes.size()];
    for (unsigned int i = 0; i < fileTypes.size(); i++)
    {
        // set the default extension
        if (i == 0)
        {
            int fulllen = fileTypes[i].size();
            int dotloc = fileTypes[i].find_first_of('.') + 1;
            int extlen = fulllen - dotloc;
            std::wstring ext = s2ws(fileTypes[i].substr( dotloc, extlen ));
            saveDialog->SetDefaultExtension(ext.c_str());
        }
        std::wstring type = s2ws(fileTypes[i]);
        winFileTypes[i].pszName = type.c_str();
        winFileTypes[i].pszSpec = type.c_str();
    }
    // set the file types
    saveDialog->SetFileTypes(fileTypes.size(), winFileTypes);

    // setup the default path
    IShellItem *psiFolder = nullptr;
    if (defaultPath != "")
    {
        LPCWSTR szFilePath = s2ws(defaultPath).c_str();
        HRESULT shr = SHCreateItemFromParsingName(szFilePath, NULL, IID_PPV_ARGS(&psiFolder));
            //SHCreateItemFromParsingName ( szFilePath, NULL, IID_PPV_ARGS(&psiFolder) );
        
        if (SUCCEEDED(shr))
            saveDialog->SetFolder(psiFolder);
        else
            std::cout << "Error setting folder to: \"" << defaultPath << "\" - error code: " << shr;
    }


    if (SUCCEEDED(hr))
    {
        hr = saveDialog->Show(NULL);
        if (SUCCEEDED(hr))
        {
            IShellItem *pItem;
            hr = saveDialog->GetResult(&pItem);
            if (SUCCEEDED(hr))
            {
                PWSTR pszFilePath;
                hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);

                // Display the file name to the user.
                if (SUCCEEDED(hr))
                {
                    std::wstring wstr = pszFilePath;

                    result = ws2s(wstr);
                    // ...
                    CoTaskMemFree(pszFilePath);
                }
                pItem->Release();
            }
        }
        saveDialog->Release();
    }

    delete winFileTypes;
    if (psiFolder != nullptr)
        psiFolder->Release();

    return result;
}

 

Usage of OpenFileDialog would look something like:



std::vector<std::string> filetypes;
filetypes.push_back("*.txt");   // this will cause the Open file dialog to only show .txt files to be opened
std::string myfilepath = OpenFileDialog(filetypes, "");
// use your file stream to open the path contained in myfilepath

The SaveFileDialog function works the same way:



std::vector<std::string> filetypes;
filetypes.push_back("*.txt");   // this will cause the Save file dialog to save files as txt
std::string path = SaveFileDialog(filetypes, "");
// use path to open with a file reader and write

 

 

A good thing to note is that the default SaveFileDialog behavior is to prompt the user if he wants to override the existing file (if the file they tried saving exists) - so you don't have to worry about it

0

Share this post


Link to post
Share on other sites

if you distribute the binary, you could create a folder that is relative to the program's exe, and simply tell people to place their levels in that folder, and simply scan that folder for all valid level files.

Edited by slicer4ever
0

Share this post


Link to post
Share on other sites
if you distribute the binary, you could create a folder that is relative to the program's exe, and simply tell people to place their levels in that folder, and simply scan that folder for all valid level files.

 

Don't do this.

 

It won't work if users put their program in "standard" locations like Program Files, and it may break on other versions of the OS. At the [b]very least[/b] you should use [url=http://msdn.microsoft.com/en-us/library/bb762188(v=vs.85).aspx]SHGetKnownFolderPath[/url].

2

Share this post


Link to post
Share on other sites
if you distribute the binary, you could create a folder that is relative to the program's exe, and simply tell people to place their levels in that folder, and simply scan that folder for all valid level files.

 

Don't do this.

 

It won't work if users put their program in "standard" locations like Program Files, and it may break on other versions of the OS. At the very least you should use SHGetKnownFolderPath.

 

Now you got me curious. I'm currently using exactly the method described - below my exe is a folder named /data/StandaloneLevels/ which I'm currently using to read/write to. 

True, I'm not actually using a relative folder, I'm using GetModuleFileName to get the running folder of the exe, and then add the /data/StandaloneLevels/ to the end. But I just went and placed it in c:/Program Files(x86)/ - and both reading and writing the levels seemed to work just fine.

0

Share this post


Link to post
Share on other sites
if you distribute the binary, you could create a folder that is relative to the program's exe, and simply tell people to place their levels in that folder, and simply scan that folder for all valid level files.

 

 

if you distribute the binary, you could create a folder that is relative to the program's exe, and simply tell people to place their levels in that folder, and simply scan that folder for all valid level files.

 

Don't do this.

 

It won't work if users put their program in "standard" locations like Program Files, and it may break on other versions of the OS. At the very least you should use SHGetKnownFolderPath.

 

I was attempting to cover both of these instances here in my message.  Relative to the applications executable path and relative to the current working directory are different things.  Just the act of placing the file in a directory that exists in the same path as the executable is no gurantee that the level files will be relative to the executable at run time.  This will only occur if the program is launched by double clicking the executable inside the directory.  If you use a shortcut your current working directory is different and the relations will fail.  Thus the suggestion that if your relying on this that you install the shortcuts for the user and make sure that your shortcut calls a batch file or something to change the current working directory prior to executing the application.

 

To quote on the Program Files path, all real users of the operating system will have read privileges to the Program Files path's and all their sub directories.  Barring any writing process or any advanced PC / permissions configurations using the C:\Program Files (x86)\Your Game\Levels is no different than using C:\MyGameLevels.  It would be a fairly safe assumption that if your end user is this advanced that they have gone out of their way to restrict their own access to the program files directory that they will  not be installing in that location.

 

With all of that said, I would imagine the next point would be "The installer wants to install in the program files path!"  Well if your using an installer then have the installer create a levels directory under the user's documents path, save that path to the programs settings file and do it the right way as I mentioned in the "ideal" way to handle all of this.  However I think we might be over complicating the issue here, from my understanding the question is very simple (how can I find where the user put the level files).  The answer to that question (without considering all the special cases that rarely apply) the answer is (make the user put it where it belongs) or (scan the entire file system and look for them).

0

Share this post


Link to post
Share on other sites
Now you got me curious. I'm currently using exactly the method described - below my exe is a folder named /data/StandaloneLevels/ which I'm currently using to read/write to. 

True, I'm not actually using a relative folder, I'm using GetModuleFileName to get the running folder of the exe, and then add the /data/StandaloneLevels/ to the end. But I just went and placed it in c:/Program Files(x86)/ - and both reading and writing the levels seemed to work just fine.

 

What Windows version and account privileges? It's well-known that limited accounts on Vista and later will run into problems, especially on installs of Windows with harsher UAC configurations. If you're on Win7 with UAC on minimal and running as an administrator, well, of course you can do that without issue ;-)

 

 

[edit] For clarity: reading is fine. Writing is where account permissions matter. If you're on an elevated account you can write freely. But it's been recommended not to do that sort of thing since the Windows 2000 era.

Edited by ApochPiQ
1

Share this post


Link to post
Share on other sites

It seems a bit ambiguous what leveldata is here.

If its just permanent game assets then everyone puts those into the program files subdir by his exe while installing the program.

If its extra assets like a mod, I guess many people just put those there too, though it got more and more restricted so it gets put into the users my documents folder more often these days.

Volatile data that is more like a safegame than game asset, you will probably put it in the users appdata or my documents. There should be exact guidelines on MS website.

1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0