// SaveLib.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include "OSFDlg.h"
using namespace std;
extern "C"
{
__declspec(dllexport) unsigned int SaveGame(wchar_t* LevelName, float X, float Y, float Z, wchar_t* SavePath)
{
wofstream SaveStream(SavePath, ios::out);
SaveStream << LevelName;
SaveStream << X;
SaveStream << Y;
SaveStream << Z;
SaveStream.close();
return 1;
}
struct Save
{
wchar_t LevelName;
float X, Y, Z;
};
__declspec(dllexport) Save* LoadGame(wchar_t* LoadPath)
{
wifstream LoadStream(LoadPath, ios::in);
static Save *NewSave;
NewSave = new Save;
LoadStream >> NewSave->LevelName;
LoadStream >> NewSave->X;
LoadStream >> NewSave->Y;
LoadStream >> NewSave->Z;
LoadStream.close();
return NewSave;
}
__declspec(dllexport) wchar_t SaveDiag()
{
//Filter
TCHAR szFilter[] = TEXT("Save Files (*.SAV)\0*.sav\0");
//Default extension
TCHAR szDefExtension[] = TEXT("sav\0");
COSFDialog SaveDiag;
if(SaveDiag.FileSaveDlg(szFilter, szDefExtension, NULL))
{
return (wchar_t)SaveDiag.GetFileName();
}
}
__declspec(dllexport) wchar_t LoadDiag()
{
//Filter
TCHAR szFilter[] = TEXT("Save Files (*.SAV)\0*.sav\0");
//Default Extension
TCHAR szDefExtension[] = TEXT("sav\0");
COSFDialog LoadDiag;
if(LoadDiag.FileOpenDlg(szFilter, szDefExtension, TEXT("Load Game"), false))
return (wchar_t)LoadDiag.GetFileName();
}
}
Loading files in C++... (crashing)
I'm trying to load a saved file in C++. The problem is that I'm calling the DLL from UnrealScript, and don't really have a good way to debugging it. I can only confirm that it makes UDK crash.
So... can anyone spot any obvious flaws in my loading code (compiles fine)?
Edit: It's the LoadGame() function that fails. LoadDiag seems to work fine... it just displays a File Open Dialog.
In terms of debugging you can attach to an already running process in Visual Studio. All you really need to do is:
-Make the program wait somewhere to give you a chance to attach, something like
static bool wait = true;
while(wait) {}
at the top of the crashing function should do it.
-Perform the steps to recreate the crash, you should find that the program then hangs at the while loops instead of crashing.
-In Visual Studio goto "Debug"->"Attach To Process" and choose your dll or UDK editor process (this can be a bit of trail and error :S ).
You can then change the wait to false and step through the code :)
This feature probably exists with other IDE's if you're not using Visual Studio.
-Make the program wait somewhere to give you a chance to attach, something like
static bool wait = true;
while(wait) {}
at the top of the crashing function should do it.
-Perform the steps to recreate the crash, you should find that the program then hangs at the while loops instead of crashing.
-In Visual Studio goto "Debug"->"Attach To Process" and choose your dll or UDK editor process (this can be a bit of trail and error :S ).
You can then change the wait to false and step through the code :)
This feature probably exists with other IDE's if you're not using Visual Studio.
I don't have access to the Unreal sourcecode. :(
But I'm gonna do some MessageBox debugging to check the sanity of my variables etc
But I'm gonna do some MessageBox debugging to check the sanity of my variables etc
Quote:Original post by MatsVed
I don't have access to the Unreal sourcecode. :(
But I'm gonna do some MessageBox debugging to check the sanity of my variables etc
Why would you need it? you can still debug your DLL (as I understand it you are writing a DLL that unreal loads )
You don't need to actually debug this using UDK, just create a basic project that contains your loading/saving code...
If you take a look at the contents of test.txt that should give you some clue as to why this is not working:
How should operator >> intepret this data into a wide char and three floats? You need some way to distinguish which part of the text belongs to which field, for example you could put each field on a seperate line, or use some other token to delimit the fields:
Although if you are willing to use boost there is a great serialisation library included which will handle this for you easily, as well as providing different archive types.
#include <iostream>#include <fstream>struct Save{ wchar_t LevelName; float X, Y, Z;};template <typename Stream>Stream& operator << (Stream& stream, const Save& save){ stream << save.LevelName; stream << save.X; stream << save.Y; stream << save.Z; return stream;}template <typename Stream>Stream& operator >> (Stream& stream, Save& save){ stream >> save.LevelName; stream >> save.X; stream >> save.Y; stream >> save.Z; return stream;}int main(int argc, char* argv[]){ { Save test; test.X = 2.0f; test.Y = 4.0f; test.Z = 8.0f; test.LevelName = L'T'; std::wofstream os("test.txt"); os << test; } { Save test; std::wifstream is("test.txt"); is >> test; } return 0;}
If you take a look at the contents of test.txt that should give you some clue as to why this is not working:
T248
How should operator >> intepret this data into a wide char and three floats? You need some way to distinguish which part of the text belongs to which field, for example you could put each field on a seperate line, or use some other token to delimit the fields:
T248
template <typename Stream>Stream& operator << (Stream& stream, const Save& save){ stream << save.LevelName << '\n'; stream << save.X << '\n'; stream << save.Y << '\n'; stream << save.Z << '\n'; return stream;}template <typename Stream>Stream& operator >> (Stream& stream, Save& save){ std::wstring str; std::getline(stream, str); save.LevelName = boost::lexical_cast<wchar_t>(str); std::getline(stream, str); save.X = boost::lexical_cast<float>(str); std::getline(stream, str); save.Y = boost::lexical_cast<float>(str); std::getline(stream, str); save.Z = boost::lexical_cast<float>(str); return stream;}
Although if you are willing to use boost there is a great serialisation library included which will handle this for you easily, as well as providing different archive types.
I actually did this earlier:
But it didn't seem to have any effect, so I removed it. Is there any standard equivalent to boost:lexical_cast?
__declspec(dllexport) unsigned int SaveGame(wchar_t* LevelName, float X, float Y, float Z, wchar_t* SavePath) { wofstream SaveStream(SavePath, ios::out); SaveStream << LevelName; SaveStream << std::endl; SaveStream << X; SaveStream << std::endl; SaveStream << Y; SaveStream << std::endl; SaveStream << Z; SaveStream << std::endl; SaveStream.close(); return 1; }
But it didn't seem to have any effect, so I removed it. Is there any standard equivalent to boost:lexical_cast?
Quote:Original post by MatsVed
I actually did this earlier:
*** Source Snippet Removed ***
But it didn't seem to have any effect, so I removed it.
It may well be that the crash is caused by something else, but your load/save code is definately broken without some delimeter between the fields. I imagine that one or more of those variables will remain uninitialised. Do you have any more information about the crash?
Quote:
Is there any standard equivalent to boost:lexical_cast?
I believe it is part of tr2 although you can simulate the effect quite simply (I'm pretty sure the boost::lexical_cast does some other trickery, including error conditions and such):
template <typename Target, typename Source>Target lexical_cast(Source source){ Target target; std::ostringstream os; os << source; std::istringstream is(os.str()); is >> target; return target;}
Ok, so I got the thing to work in UDK (turns out I was doing something wrong with the UnrealScript code).
But I still have a problem... no text is actually read from the file and into the struct I'm returning. :
I created a test program for myself, and a ReadLine function.
When the MessageBox is displayed, it is empty! This makes me assume that no text is being read. So what is the problem here?
But I still have a problem... no text is actually read from the file and into the struct I'm returning. :
I created a test program for myself, and a ReadLine function.
// LoadTest.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include <fstream>#include <iostream>#include <sstream>#include "OSFDlg.h"#include <assert.h>using namespace std;template <typename Target, typename Source>Target lexical_cast(Source source){ Target target; std::ostringstream os; os << source; std::istringstream is(os.str()); is >> target; return target;}struct Save{ wchar_t *LevelName; float X, Y, Z;};wchar_t LoadDiag(){ //Filter TCHAR szFilter[] = TEXT("Save Files (*.SAV)\0*.sav\0"); //Default Extension TCHAR szDefExtension[] = TEXT("sav\0"); COSFDialog LoadDiag; if(LoadDiag.FileOpenDlg(szFilter, szDefExtension, TEXT("Load Game"), false)) return (wchar_t)LoadDiag.GetFileName();}wchar_t* ReadLine(wifstream *Stream){ wchar_t *Line = new wchar_t[256]; wchar_t ControlChar = '0'; int i = 0; while(ControlChar != '\n') { ControlChar = Stream->get(); Line = ControlChar; i++; if(i == 256) break; } return Line;}__declspec(dllexport) Save* LoadGame(wchar_t* LoadPath){ wifstream LoadStream(LoadPath, ios::in); wchar_t X; wchar_t Y; wchar_t Z; static Save NewSave; NewSave.LevelName = new wchar_t[100]; NewSave.LevelName = ReadLine(&LoadStream); LoadStream >> X; NewSave.X = lexical_cast<float, wchar_t>(X); LoadStream >> Y; NewSave.Y = lexical_cast<float, wchar_t>(Y); LoadStream >> Z; NewSave.Z = lexical_cast<float, wchar_t>(Z); /*LoadStream >> NewSave.X; LoadStream >> NewSave.Y; LoadStream >> NewSave.Z;*/ LoadStream.close(); MessageBox(NULL, (LPCWSTR)*NewSave.LevelName, TEXT("LoadGame()"), MB_OKCANCEL); return &NewSave;}int _tmain(int argc, _TCHAR* argv[]){ wchar_t SavePath = LoadDiag(); Save *Sav = LoadGame(&SavePath); assert(Sav); return 0;}
When the MessageBox is displayed, it is empty! This makes me assume that no text is being read. So what is the problem here?
I see, you are doing something wrong with that wchar_t in your MessageBox:
This:
should become this:
NewSave.LevelName is a *wchar_t type. And the message box takes a
Also, you don't delete the memory you allocate after use, or do you?
Anyways, this would be better:
So you don't need to allocate new memory. But note that you have to cast that string to a LPWSTR or an LPCWSTR when you want to use it!
(LPWSTR)&LevelName should do that.
This:
MessageBox(NULL, (LPCWSTR)*NewSave.LevelName, TEXT("LoadGame()"), MB_OKCANCEL);
should become this:
MessageBox(NULL, (LPCWSTR)NewSave.LevelName, TEXT("LoadGame()"), MB_OKCANCEL);
NewSave.LevelName is a *wchar_t type. And the message box takes a
LPCWSTR
which is not more than an const wchar_t*
.Also, you don't delete the memory you allocate after use, or do you?
Anyways, this would be better:
struct Save{ wchar_t LevelName[100]; float X, Y, Z;};
So you don't need to allocate new memory. But note that you have to cast that string to a LPWSTR or an LPCWSTR when you want to use it!
(LPWSTR)&LevelName should do that.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement