Public Macros Encapsulating NULLs
Reply
Encapsulating NULLs
Hullo~

Been tinkering around with .ini's and have been coming across problems with null returns being a (what I would call primitive) type. When I check for things like a name, partials, etc. I cannot because the possibility of a null return (that I cannot easily deal with) exists. Is there any good way to avoid this problem? I currently have checks in for the length being 0 since that seems to be acceptable to MQ2. But, if possible, I don't want to have to check this every time I want to reference something in my .ini file = p.

A.K.A: I am wondering if I can condense 2 checks into 1 since I am trying to check for strings when there is the possibility of being returned an int.
Sun Jun 05, 2016 11:22 am
Can you give a code example? The best way to avoid NULL returns usually involves a change elsewhere (how values are stored, algorithm, etc). And sometimes NULLs still can't be avoided.
Sun Jun 05, 2016 6:54 pm
Senior Project Member
Yeah, sure, I'll put it below.

Macro
More +
${Ini[varList.ini, zoneList, ${currentZone}].Equal[Cursor]}


This expects a string return for the value that I want, but when it returns a null, it causes syntax errors that crashes the macro.
Mon Jun 06, 2016 7:34 am
You can do a conditional and within the conditional load a variable with the string. If the item/spell/whatever exists you put its name, if it doesn't exist put nothing. Then insert the var into the ini. For the condition use the ID which will have a value when populated (true) or a null when invalid (false) something like the following (forgive me I'm on my phone)

If (item.ID) {
Myvar = item.name
}
Else {
Myvar = ""
}

Ini(Myvar)

Hopefully that makes sense, I'll clean it up when I have a keyboard if needed. I defer to grumble as usual though :)

Edit: I think I misunderstood, disregard that all hehe
Mon Jun 06, 2016 12:51 pm
Project Lead
Check this, I think you were barking up the right tree with the length

http://mqemulator.net/forum2/viewtopic.php?t=64

I think this is what I used last time I did a macro with ini and it worked.
Mon Jun 06, 2016 12:59 pm
Project Lead
Yeah, using .Length as a check is fine, but I was just seeing if there was any way to condense it to just the 1 check I want (comparing the strings) instead of having to either add a bunch of length checks every time I look in my .ini or having an .ini file that's hundreds of lines long (because I'm using it for differentiating loot as well).

i.e, is there like a top level "Object" I can wrap it in to detect the "class" type without scaring MQ2's syntax checker.
Mon Jun 06, 2016 1:45 pm
Not one that I'm aware of, but isn't wrapping it in something like that about the same amount of effort as cutting and pasting the.Length code? I totally get it though, the Length stuff is very inelegant and it feels... hackish, maybe grumble will be aware of something.
Mon Jun 06, 2016 5:22 pm
Project Lead
The ${Ini[]} TLO let's you output a custom string instead of returning NULL. In the wiki, it's called the default value.

${Ini[varList.ini, zoneList, ${currentZone}].Equal[Cursor], SuperSpecialString}

SuperSpecialString is usually something relatively common (0, TRUE, FALSE, etc) or something guaranteed to never occur in game.

This expects a string return for the value that I want, but when it returns a null, it causes syntax errors that crashes the macro.


If you expect to find non-strings in your ini, then you can force the result to be any type you want.

${Int[${Ini[varList.ini, zoneList, ${currentZone}].Equal[Cursor], 0}]}
${Bool[${Ini[varList.ini, zoneList, ${currentZone}].Equal[Cursor], FALSE}]}
${Float[${Ini[varList.ini, zoneList, ${currentZone}].Equal[Cursor], 3.14}]}

I currently have checks in for the length being 0 since that seems to be acceptable to MQ2. But, if possible, I don't want to have to check this every time I want to reference something in my .ini file = p.


A length of 0 indicates the ini search returned no result. That's a special case and usually worth knowing. Even using the SuperSpecialString method above, I would still catch those cases and do something, even if that something is to skip a section of code.

Yeah, using .Length as a check is fine, but I was just seeing if there was any way to condense it to just the 1 check I want (comparing the strings) instead of having to either add a bunch of length checks every time I look in my .ini or having an .ini file that's hundreds of lines long (because I'm using it for differentiating loot as well).

i.e, is there like a top level "Object" I can wrap it in to detect the "class" type without scaring MQ2's syntax checker.


Comparing two strings and at least one is obtained via ${Ini}?

You can write your own subroutine wrapper and call it repeatedly.

Macro
More +
Sub CustomStringCompare (string first, string second)
   /declare returnval int local 0
   /declare returnstring string local
   /if (${first.Length} < 1) {
      | first is a NULL or empty string
      /varcalc returnval ${returnval}+1
   }
   /if (${second.Length} < 1) {
      | second is a NULL or empty string
      /varcalc returnval ${returnval}+1
   }
   /if (${returnval} > 0) {
      | no point in comparing NULL strings
      /return ${returnval}
   }
   /if (${first.Equal[${second}]} {
      /varset returnstring EQUAL
   }
   /if (${first.Find[${second}]}) {
      /varset returnstring FirstContainsSecond
   }
   /if (${second.Find[${first}]}) {
      /varset returnstring SecondContainsFirst
   }

   | fill in with whatever edge cases you wish

/return ${returnstring}
Mon Jun 06, 2016 7:41 pm
Senior Project Member
Oh cool, I didn't know about the SSS; I had a passing thought about doing a subroutine, but it wasn't going to work when I was getting the null stuff. Thanks for the suggestions!
Mon Jun 06, 2016 7:54 pm
I got curious how it all worked and it mostly just jams everything into GetPrivateProfileString

c++
More +
    if (DWORD nSize=GetPrivateProfileString(pSection,pKey,pDefault,DataTypeTemp,MAX_STRING,FileName))
    {
        if (nSize>2)
            for (unsigned long N = 0 ; N < nSize-2 ; N++)
                if (DataTypeTemp[N]==0)
                    DataTypeTemp[N]='|';
        if ((!pSection || !pKey) && (nSize<MAX_STRING-3))
            strcat(DataTypeTemp,"||");

        Ret.Ptr=&DataTypeTemp[0];
        Ret.Type=pStringType;
        return true;
    }


So the C++ documentation might be interesting to read if you're really wanting to dig into it.

https://msdn.microsoft.com/en-us/library/windows/desktop/ms724353(v=vs.85).aspx

It for the most part just passes your parameters forward into lpAppName, lpKeyName, lpDefault, and lpFileName. The return string can have multiple values in it sometimes so they are NULL delimited and the last one is followed by two NULLs. The MQ code parses through that and replaces the NULLs with bar characters " | ".
Tue Jun 07, 2016 5:49 pm
Project Lead
Hm, I'll definitely take a look for curiosity's sake, but the default string replacement works perfect for what I needed. Thanks for the extra info!
Tue Jun 07, 2016 7:59 pm
Public Macros Encapsulating NULLs
Reply