SDK Question Saving and Loading an array from a .scn file

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,868
Reaction score
4
Points
0
Location
San Diego
I thought about resurecting my erlier thread but this is a more specific question than simply "How do I save something to a scenario?".

My problem is that I have 60+ switches in my VC and am looking to add more before I'm done. (still have the Cmd and Pilot's side-panels as well as CBs left to add) Now obviously writing each switch to the scenario individually would result in a massive file so I thought I'd adapt the technique I used in my Panel Initialization routine.

The problem is that I have no Idea how to go about writing an array to, or loading it from a scenario (text string) without writing each value individually.

If someone could help me out or point me towards a pertinent SDK sample it'd be much appreciated. :hailprobe:


Here's my panel Initialization code for reference

Code:
// Set initial switch positions
void SpiderLEM::InitPanel(void)
{
	// Default switch positions for vessel when spawned by Scenario Editor 
	int P1_Init[17] = { 0, 0, 0, 0, 2, 2, 2, 2, 0, 2, 0, 2, 1, 1, 0, 0, 0}; // Panel 1 (Comander's console) switches
	int P2_Init[18] = { 0, 2, 2, 0, 0, 2, 2, 0, 0, 2, 2, 0, 2, 2, 0, 2, 2, 2}; // Panel 2 (Pilot's console) switches
	int P3_Init[25] = { 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Panel 3 (Center Console) switches

	// Set switch positions
	for (int i = 0; i < 17; i++) P1_Switch[i] = (SwitchState) P1_Init[i];
	for (int i = 0; i < 18; i++) P2_Switch[i] = (SwitchState) P2_Init[i];
	for (int i = 0; i < 25; i++) P3_Switch[i] = (SwitchState) P3_Init[i];
}
 
Here's a very handy little function:

Code:
void Tokenize(const string& str, vector<string>& tokens, const string& delimiters) {
    // Skip delimiters at beginning.
    string::size_type lastPos = str.find_first_not_of(delimiters, 0);
    // Find first "non-delimiter".
    string::size_type pos = str.find_first_of(delimiters, lastPos);

    while (pos != string::npos || lastPos != string::npos) {
        // Found a token, add it to the vector.
        tokens.push_back(str.substr(lastPos, pos - lastPos));
        // Skip delimiters.  Note the "not_of"
        lastPos = str.find_first_not_of(delimiters, pos);
        // Find next "non-delimiter"
        pos = str.find_first_of(delimiters, lastPos);
    }
}

It will take a string apart with a specified deliminator and store the substrings in a vector. You use the function like this ("line" is what you got from oapi_ReadScenarioLine() or oapiReadItem_string()):

Code:
vector<string> tokens;
Tokenize(line, tokens, ",")    //takes the string line, hacks it appart at every "," and stores the substring in the vector tokens(if no deliminator specified, space is assumed)

for (UINT i = 0; i < tokens.size(); ++i)
{
      sscanf(tokens[i], "%d", &(whatever)       //reads through the substrings and assigns the values to whatever variable they need to be assigned on. Works with other format identifiers too, of course)
}

The whole thing works very nice for entries with an undefined amount of items, and for nested stuff that uses different deliminators.

Otherwise, if you know how many items there are in the line, and you know their types, you can just do something like:
Code:
sscanf(line, "%d %d %d %f", &(int1), &(int2), &(int3), &(float1));
Or whatever order of things you have. This way you define the structure of the line as a format string. and then read the individual values into your variables. As mentioned, this requires that you know absolutely perfectly well how the line is structured and how many entries of what type in which order there are.
 
A compact way to store the switch positions may be as a string:
Code:
char switchpos[nswitch+1];
for (i = 0; i < nswitch; i++) {
    switchpos[i] = switchstate[i] + '0';
}
switchpos[i] = '\0';
oapiWriteScenario_string(file, "SWITCHSTATE", switchpos);
for writing (where "switchstate" is assumed to be an array of integer switch states). The +'0' offset converts integers to characters.
For reading:
Code:
oapiReadScenario_nextline(file, cbuf);
if (!strnicmp (cbuf, "SWITCHSTATE")) {
   cbuf += 12;
   for (i = 0; i < nswitch && *cbuf) {
      switchstate[i] = *cbuf++ - '0';
   }
}
Note: this works only if all switch states are represented by states between 0 and 207, since a character can hold values between 0 and 255, and the ASCII code of '0' is 48(dec). If a switch can have more states than that, you need to find another way (e.g. using two characters per switch).
 
A compact way to store the switch positions may be as a string:
Code:
char switchpos[nswitch+1];
for (i = 0; i < nswitch; i++) {
    switchpos[i] = switchstate[i] + '0';
}
switchpos[i] = '\0';
oapiWriteScenario_string(file, "SWITCHSTATE", switchpos);
for writing (where "switchstate" is assumed to be an array of integer switch states). The +'0' offset converts integers to characters.
For reading:
Code:
oapiReadScenario_nextline(file, cbuf);
if (!strnicmp (cbuf, "SWITCHSTATE")) {
   cbuf += 12;
   for (i = 0; i < nswitch && *cbuf) {
      switchstate[i] = *cbuf++ - '0';
   }
}
Note: this works only if all switch states are represented by states between 0 and 207, since a character can hold values between 0 and 255, and the ASCII code of '0' is 48(dec). If a switch can have more states than that, you need to find another way (e.g. using two characters per switch).

Thankyou Professor,

Something like this is pretty much exactly what I was looking for. :tiphat:
 
If your switches don't have more than 8 states, and if you want them even more compact, you could even store two switches in a single character. In principle you could store two 16-state switches in one character, but you are again restricted by the fact that you cannot use non-printable characters.
Code:
char switchpos[(nswitch+1)/2+1];
for (i = 0; i < nswitch; i++) {
    if (!(i & 1)) switchpos[i/2] = switchstate[i];
    else switchpos[i/2] |= switchstate[i] << 3;
}
for (i = 0; i < (nswitch+1)/2; i++)
    switchpos[i] += '0';  // make sure characters are printable
}
switchpos[i] = '\0';
(I didn't test this, so make sure there is no mistake).
The decoding part is left to the interested reader. Extra points if you can store 3 four-state switches or 7 2-state switches in one character :thumbup:
Even more points if you can tell me how many 3-state switches can be stored in one printable character ...
 
If your switches don't have more than 8 states, and if you want them even more compact, you could even store two switches in a single character.

They don't, but I see no reason to make it more complicated than it already is, one characer per switch will do just fine :lol:

Looking at your code however...

shouldn't "switchpos = '\0';" be inside the brackets of "for (i = 0; i < nswitch; i++)"?
 
shouldn't "switchpos = '\0';" be inside the brackets of "for (i = 0; i < nswitch; i++)"?

No. It would reset the current character to '\0' in every iteration step then, either making the string empty with 0 characters length or making it contain (printable) zeros only, depending whether you put it at the end of the loop or at the beginning, and you only need one last character at the end of your string be set this way.

The 'i' variable will point at the character next to end of your created text after the loop has ended, and that's the only place where you should place the '\0' to correctly end the null terminated string.



Extra points if you can store 3 four-state switches or 7 2-state switches in one character :thumbup:
Even more points if you can tell me how many 3-state switches can be stored in one printable character ...

7 2-state switches starting from space equal to all switch states = 0 ('x' position is excluded because the bit is used by adding space):
bit position|0|1|2|3|4|5|6|7
switch number |1|2|3|4|5|x|6|7


3 4-state switches starting from '0' equal to all switch states = 0 ('x' position is excluded because bits are used by adding '0'):
bit position|0|1|2|3|4|5|6|7
switch number|1|1|2|2|x|x|3|3


22222 in base 3 is 242 decimal, so a byte could store 5 3-state switches, but as a text it would need to use some of the control characters, and a #13 character which could be the first if we want the last possible be #255, should be excluded, because it's a carriage return, making the scenario parser treating it as the end of line (it could be replaced for example with #12, which would be a special case being tested first, resetting all the switches in group to 0 without analyzing the character further).

But if a scenario line uses only one character storing 5 3-state switches, #13 character wouldn't be a problem (the returned string would be empty, meaning all switches should be set to 0).

However, I'm not sure how #26 character is being treated during file reading, because it could be treated as EOF when file is read in text mode, from the MS DOS legacy.
 
is clbkPostCreation called before or after reading from the scenario file?
 
Ok good to know.

However, I already eliminated it as the culprit by moving my "InitPanel" function to the constructor.

Basically I've got the clbkSaveState side working but the load state is being stuborn (switches are still loading in their default positions)

This is what my load function looks like

Code:
		if (!_strnicmp( cbuf, "PANEL1_SWITCHES", 256)) 
		{
			cbuf += 16;
			for (i = 0; (i < 17) && (*cbuf);) 
			{
				P1_Toggle[i] = (ToggleState) (*cbuf++ - '0');
			}
		}

My save function looks like this...

Code:
	char switchpos[256];
	for (i = 0; i < 17; i++) switchpos[i] = P1_Toggle[i] + '0';
	switchpos[i] = '\0';
	oapiWriteScenario_string(scn, "PANEL1_SWITCHES", switchpos);

I've looked in the scenario files and confirmed that the string is being recorded properly so I assume the problem must lie with clbkLoadState.
 
Why are you comparing first 256 characters instead of 15?:
Code:
_strnicmp( cbuf, "PANEL1_SWITCHES", [color=red][s]2[/s][/color][color=green]1[/color]5[color=red][s]6[/s][/color])

_strnicmp @ MSDN said:
If the strings are equal when a terminating null character is reached in either string before count characters are compared, the shorter string is lesser.
MSDN: _strnicmp, _wcsnicmp, _mbsnicmp, _strnicmp_l, _wcsnicmp_l, _mbsnicmp_l
 
Code:
			for (i = 0; (i < 17) && (*cbuf);)
This is is missing an i++ in the increment section of the loop. I note that this was missing in my code sample too. Sorry - that was a typo. (And yes, the "11" in my strnicmp function was missing also. Thanks for picking that up, orb. It was a bit late when I wrote that yesterday ...)
 
Why are you comparing first 256 characters instead of 15?

Misinterpretation of an error code on my part.

My compiler said "Missing Argument: Length of 'cbuf'." so I passed it the length of 'cbuf' :facepalm:

...and thanks again professor I got it working now

Code:
		if (!_strnicmp( cbuf, "PANEL1_SWITCHES", 15)) 
		{
			cbuf += 16;
			for (i = 0; (i < 17) && (*cbuf); i++) P1_Toggle[i] = (ToggleState) (*cbuf++ - '0');
		}
 
Last edited:
Back
Top