• 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
mynameisnafe

C++ Multi-Byte - prepare string of extensions for OpenFileDialog

12 posts in this topic

HI all,

 

I'm having some trouble figuring out how to do this one.

Assimp gives me a string, containing all the file extensions it can handle.

I found some code to tokenize strings on SO and wrapped it in a function.

The format of the string that is used to initialise the accepted extensions list for the OPENFILENAME structure is as follows:
 

L"All\0*.*\0Text\*.txt\0"

And so I'm trying to recreate that programatically. Which is a nightmare, as I'm trying to stick '\0' in the middle of a string.

// Get a vector of extensions supported by Assimp ( assimp exts string, tokenise by ';' )
std::vector<std::string> exts = Scatterbrain::Split(extsAccepted.c_str(), ';');
	
//create a string with a bunch of '\0' s in it, if possible
std::string extsList="";
for(size_t i = 0; i < exts.size(); i++)
	extsList.append(exts[i] + '\0');
	
// contains the first extension.
CString pwszExts(extsList.c_str());

// Initialize OPENFILENAME
ZeroMemory(&ofn, sizeof(ofn));

ofn.lpstrFilter = (LPCWSTR)pwszExts; // This one
...

// Display the Open dialog box. 
if( GetOpenFileName(&ofn) == TRUE ) 
{
...

Okay - I see one bug already: the dialog is going to say 'extension_a extension_b', instead of 'ext_name_a ext_a', but I'm happy to add blank spaces for names.

how is the generation of a string 

L"Hello\0World\0How\0Are\0You\0'

 done?

Thanks in advance

 

0

Share this post


Link to post
Share on other sites

how is the generation of a string

L"Hello\0World\0How\0Are\0You\0'

done?

Well, in your case, it would just be replacing ';' with '\0'.

So, maybe something like this:
std::wstring ext;
ext.reserve(str.length()+1);

std::transform(str.begin(),str.end(),back_inserter(ext),[](char t) -> wchar_t {
	return t == ';' ? 0 : t;
});
ext.push_back(0); // (This is necessary to indicate where the array of strings end)

wchar_t* buf = ext.c_str(); // An extra null terminator should be appended here.

// str = "Hello;World;How;Are;You"
// buf = L"Hello\0World\0How\0Are\0You\0\0"
EDIT:
Code sample here: http://ideone.com/wqrckP Edited by fastcall22
1

Share this post


Link to post
Share on other sites

What is this lambda magic?
 

[](char t)->wchar_t

Is that it? It's not the replacing that I'm struggling with, it's the char ->wchar_t.. and there is the char->wchar_t

I'm going to try basically an empty lambda, meanwhile, whats with the extra push_back(0)'s? are those important (i.e. the real null terminator of the returned exts wstring?

 

0

Share this post


Link to post
Share on other sites
According to the documentation for OPENFILENAME, lpstrFilter must be terminated by two NULL characters, as I had suspected. However, according to std::string::c_str, there should already be a null terminator appended to its contents. Therefore, the second push_back(0) is not needed. I will modify my previous post.

As for the lambda, [tt][](char t)->wchar_t{}[/tt], it reads as: A lambda that takes one parameter, captures no variables in its parent scope(s), and returns a wchar_t. The equivalent C++03 code would look something like:
struct ReplaceAndWiden {
	char to_find;
	wchar_t to_replace;
	
	ReplaceAndWiden(char f, wchar_t r) : to_find(f), to_replace(r) {
	}

	wchar_t operator() (char t) const {
		return t == to_find ? to_replace : (wchar_t)t;
	}
};

std::transform( str.begin(), str.end(), back_inserter(ext), ReplaceAndWiden(';',0) );
0

Share this post


Link to post
Share on other sites

I bailed on my split fn, all that CString jazz.. now I have this beautiful code:


std::string App::DoOpenFileDialog(std::string extsAccepted)
{
	std::string fName;
	OPENFILENAME ofn;       // common dialog box structure
	char szFile[260];       // buffer for file name

	std::wstring wextsList;
	std::transform(extsAccepted.begin(), extsAccepted.end(), back_inserter(wextsList), [](char t)->wchar_t {
		return t==';' ? '\0' : t;
	});

	// Initialize OPENFILENAME
	ZeroMemory(&ofn, sizeof(ofn));
	ofn.lStructSize = sizeof(ofn);
	ofn.hwndOwner = m_hWnd;
	ofn.lpstrFile = (LPWSTR)szFile;
	ofn.lpstrFile[0] = '\0';
	ofn.nMaxFile = sizeof(szFile);
	ofn.lpstrFilter = (LPCWSTR)wextsList.c_str();
	ofn.nFilterIndex = 1;
	ofn.lpstrFileTitle = NULL;
	ofn.nMaxFileTitle = 0;
	ofn.lpstrInitialDir = NULL;
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

	// Display the Open dialog box. 

	if( GetOpenFileName(&ofn) == TRUE ) 
	{	

..Problem is, it's missing every other file format - this is something I saw before while straying through the realms of doing it a char at a time in a for loop, and also while using a CString..

PS: D'oh! I've got Unicode set in my project settings (VS 2012), so that should make things easier?
PPS: I just saw your reply - I've used lambdas before, just appreciating the sly-as-hell widening, just implicit. Must use lambdas much more.Loving that one, using it, thank you smile.png

Crap - I just added the extra null terminator.

 

Edited by mynameisnafe
0

Share this post


Link to post
Share on other sites
std::transform(str.begin(),str.end(),back_inserter(ext),[](char t) -> wchar_t { return t == ';' ? 0 : t; });

I don't think that's actually going to do the right thing if there's anything other than US ASCII in the original string. A simple integral promotion from a multibyte UTF-8 string into Microsoft's proprietary UCS-2 wide-character Unicode variant is a fail.

Of course, if you're restricting your somain to US ASCII, you're fine.

2

Share this post


Link to post
Share on other sites

Okay..

 

Everything seems to be working with the lambda. The reason I have half the file formats visible in the dialog is to do with the OPENFILENAME and the GetOpenFileName() function

 

http://msdn.microsoft.com/en-gb/library/windows/desktop/ms646829(v=vs.85).aspx#open_file

 

I think I need a name for each format.. "Model" will do.

Hello people above smile.png std::string seems to work fine - I think (it appears) Assimp provides an overload - pretty sure I've seen it documented.


std::string GLFactory::GetAssimpExtsList()
{
std::string exts;
Assimp::Importer importer;
importer.GetExtensionList(exts);
return exts;
}

 

 

I don't think that's actually going to do the right thing if there's anything other than US ASCII in the original string.

 

Indeed. I'm happy to assume Assimp don't bother with language packs. PS That all sounds like a lot of effort - have you seen that lambda?! ohmy.png

Edited by mynameisnafe
0

Share this post


Link to post
Share on other sites

Bregma is correct, Assimp's aiString is UTF-8. Therefore, a temporary buffer and mbstowcs are needed for proper UTF-8 handling.

 

That will probably not be a correct conversion either, as mbstowcs expects the multibyte string to be in the system native multibyte encoding, which on Windows probably never is UTF-8.

 

You probably want something like this for a correct conversion:

#include <codecvt>
...
std::wstring_convert<std::codecvt_utf8<wchar_t>> wc;
std::wstring converted_str = wc.from_bytes(utf8str);
0

Share this post


Link to post
Share on other sites

 

std::transform(str.begin(),str.end(),back_inserter(ext),[](char t) -> wchar_t { return t == ';' ? 0 : t; });

I don't think that's actually going to do the right thing if there's anything other than US ASCII in the original string. A simple integral promotion from a multibyte UTF-8 string into Microsoft's proprietary UCS-2 wide-character Unicode variant is a fail.

Of course, if you're restricting your somain to US ASCII, you're fine.

 

 

Microsoft hasn't used UCS-2 since Windows 2000. All modern versions of Windows use UTF-16 which is a multibyte encoding the same as UTF-8. Converting a UTF-8 string to UTF-16 is not going to make it easier to process.

 

http://www.utf8everywhere.org/

Edited by Chris_F
0

Share this post


Link to post
Share on other sites

If you are going to hard-code an L"" string, you are required to use the W version of OPENFILENAME (OPENFILENAMEW) and GetOpenFileName (GetOpenFileNameW).

Never mix-and-match "", _T( "" ), and L"" strings.

The solution is to convert from UTF-8 to wide-character strings via MultiByteToWideChar() (passing CP_UTF8) and use OPENFILENAMEW and GetOpenFileNameW() to open the file.


L. Spiro

Edited by L. Spiro
0

Share this post


Link to post
Share on other sites
L. Spiro replies to my thread? Honoured!

I shall give it a try in 5 mins and let you guys know what happened..
0

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