Notifications
Clear all

[Closed] Is there a getINISetting Key count limitation

I’m trying to setup an ini that records whether certain values for certain parameters in a single “section”. It appears that using the catchall for a “section” with getINISetting is limited to around 300 “keys”. Am I doing something wrong, or is the getINISetting command limited to this small number of keys?
Certainly there are workarounds, ie using more sections, or going down the xml route; but I just wanted to check whether I was doing something wrong.

Try this with an INI with a few hundred entries under the “param” “section” :
for key in (getINISetting iniFile #param) do ( format “%
” key)
Despite having > 400 keys, it appears to stop after around 300 without any error.

Has anyone else come up against this limitation.?

Thanks
p.

17 Replies

Hmm – never heard of such a limit – but is it possible that you hit some general ini file limits ?
Don’t know wether this applies to 3ds Max/Maxscript ini files too, but i stumbled over this doc

INI files are ideal when the there is relatively little data (INI files have a 64K file size limit), and when it is in text-only format. INI files can be readily shared across a network simply by placing the file on a network drive. The main disadvantage to INI files is that the text for each key entry must be under 256 bytes (the example above limit the entry to 128 characters). Another disadvantage is that the data in an INI file can contain text only, no special formatting, tabs, hard returns, or extended formatting. INI files can be edited by any text editor or word processor.

http://www.gmccomb.com/vault/edge/ini.html

this is the sdk function that handles it calling the windows fn GetPrivateProfileString

Value*
 get_INI_setting_cf(Value** arg_list, int count)
 {
 	TCHAR val[INI_BUFFER_LEN];
 	if (count == 1 || count == 2) // get sections or section keys
 	{
 		TSTR  fileName = arg_list[0]->to_filename();
 		AssetId assetId;
 		if (IAssetManager::GetInstance()->StringToAssetId(fileName, assetId))
 			fileName = IAssetManager::GetInstance()->GetAsset(assetId).GetFileName();
 
 		GetPrivateProfileString(
 			(count == 2) ? arg_list[1]->to_string() : NULL, 
 			NULL, 
 			_T(""),	val, INI_BUFFER_LEN, 
 			fileName);
 		one_typed_value_local(Array* result); 
 		TCHAR *cp = val;
 		TCHAR tmpSec[INI_BUFFER_LEN] = {0};
 		int i;
 		vl.result = new Array(0);
 		while(*cp) {
 			i = 0;
 			while((tmpSec[i++] = *cp++) != _T('\0'));
 			vl.result->append(new String(tmpSec));
 			}
 		return_value(vl.result);
 	}
 	else
 	{
 		check_arg_count(getIniSetting, 3, count);
 		TSTR  fileName = arg_list[0]->to_filename();
 		AssetId assetId;
 		if (IAssetManager::GetInstance()->StringToAssetId(fileName, assetId))
 			fileName = IAssetManager::GetInstance()->GetAsset(assetId).GetFileName();
 
 		GetPrivateProfileString(
 			arg_list[1]->to_string(), 
 			arg_list[2]->to_string(), 
 			_T(""),	val, INI_BUFFER_LEN, 
 			fileName);
 		return new String(val);	
 	}
 }
1 Reply
 lo1
(@lo1)
Joined: 11 months ago

Posts: 0

You forgot the important part:

#define INI_BUFFER_LEN 8192

have to not use the catchall then.

I didn’t realize that if you just passed a the file name to getinisetting it would just return the section headers. Makes for a good quick and dirty string table loader.

Hah, great input people!
So if that memory limit is for a single entry, and the mem limit for the entire returned array is huge, then there shouldn’t be a reason for the issue I’m having?
Very strange.

1 Reply
 lo1
(@lo1)
Joined: 11 months ago

Posts: 0

It is not the limit for a single entry, it is the limit for a single ‘read’. When that read is a single entry there should be no problem. When that read is trying to retrieve the names of 400 keys, it might get truncated if their collective length is greater than the length of the buffer.

The problem is, that the script won’t know which entries exist in the INI till it gets the catchall array. The parameters stored may vary on a scene by scene basis.

I’ll try to break the data down into seperate “sections” and then parse through those before gathering the keys… assuming there isn’t a limit on the number of “sections” too!.

1 Reply
 lo1
(@lo1)
Joined: 11 months ago

Posts: 0

Worst case scenario, you can parse the file yourself, instead of relying on GetIniSetting.

Yup, thanks again Lo.

Here’s a script to demonstrate the issue. :


no = 1000
inipath = "c:\	est.ini"
for i = 1 to no do (
	keystr = "keyval" + (i as string)
	setINISetting inipath "Sec" keystr "true"
)
testArr = #()
for i = 1 to no do (	
	keystr = "keyval" + (i as string)
	iniVal =  (getinisetting inipath "Sec" keystr )
	--format "% : %
" i (getinisetting inipath "Sec" keystr )
	append testArr iniVal
)

format "Specified entries : %
" testArr.count

testArr = (getinisetting inipath "Sec"  )
format "Catchall entries : %
" testArr.count

for i in testArr do format "%
" i

yeah the single data read is indeed massive though I wouldn’t really want to be polling an inifile hundreds of times, even if it’s very quick it’s just not the done thing

 lo1
fn getSectionKeys filename sectionName =
(
	local allLines = (dotNetClass "System.IO.File").ReadAllLines filename
	local targetString = "[" + sectionName + "]"
	
	local headerIndex = 0
	for i = 1 to allLines.count while headerIndex == 0 do if allLines[i] == targetString do headerIndex = i
	local result = #()
	for i = headerIndex + 1 to allLines.count do
	(
		if allLines[i][1] == "[" do return result
		append result (trimRight ((filterString allLines[i] "=")[1]))
	)
	result
)
1 Reply
(@reform)
Joined: 11 months ago

Posts: 0

Thanks Lo,
Great to see such efficient code

 PEN

Why not just go with the XML solution? Sounds like it will be less head ache and work?

Page 1 / 2