Page 1 of 1

CSV help

Posted: Fri Aug 27, 2004 6:15 pm
by ieatacid
I have an ini key, for example: key=1,2,3,4,5,6,7

It's pulled from an ini file, by a plugin, and sometimes varies. Sometimes it's longer, sometimes shorter. In all cases I only need to change values 3 and 4 and preserve the previous states of the other values when I write back to the ini.

- Values 1 through 4 are always numbers.
- Anything from the 5th value on may also contain numbers, letters or spaces.
- Sometimes a value from the 5th onward will also contain a comma or other punctuation.

Basically I need to stop CSV'ing after the 4th variable and set the rest as a single string so it can be written back to the ini unchanged, but I'm at a loss. I was using strtok() but I can't figure out how to stop it after the fourth comma and write the rest to a string.

Anyone have any ideas?

Posted: Fri Aug 27, 2004 7:53 pm
by aChallenged1
You're way beyond my skills, that we know, however, maybe you've tried this, maybe not...

In the scripting language you have the .Mid, .Left, and .Right functions. I'm sure there must be a simular function in C. I'm guessing you're looking for a C answer as you are dealing with a plugin, not a macro.

I know you can grab portions of text, or numbers, etc with the above functions, so I'm thinking that maybe first grab it as a whole and make a string to work from ${FullCount}. Do a character count of the data in question to find out how long the string is, subtract 4 from that number and store for the moment, ${ShortCount}. Next use the .Right and character by character pull from right to left via an index equal to the Stored Number ${ShortCount}. I am hoping that the commas are part of the first 4 and not a delimiter of somekind to seperate out data.

I can see this better in my head than explain it I fear.

But basically...

Code: Select all

ini key=1,2,3,4,5,6,7
/varset KeyData ReadIniKey
/varset TakeCount KeyData


      /if (!${TakeCount.Mid[${index},1]}) /varset index 1
      :CountingLoop
      /varcalc index ${index}+1
      /if (!${TakeCount.Mid[${index},1]}), /varcalc FullCount ${Index}-1, /goto :CountingLoop

      /varcalc ShortCount ${FullCount}-4
      /varset ReturnData ${KeyData.Right[${ShortCount},${ShortCount}]
Walkthrough of how it works in my mind.

step 1, Grab Key from INI
step 2, create to variables using that data
step 3, loop that uses the data to get a characterlenght of the data, which in this case is 13, with commas included.
step 4, We have the string lenght, what we want is that minus 4, so leaves us with 9 which is now our ShortCount data.
step 5, Get the ReturnData by reading KeyData from right to left 9spaces aquiring all 9 portions of it.
step 6, Tack on ReturnData to the end of ModData.

Is it doable? Did it help at all?

Posted: Fri Aug 27, 2004 8:00 pm
by ksmith
In C, I'd reccommend sscanf(), not sure what exactly the way to do it in C++ with iostream would be. This works though.

Code: Select all

#include <stdio.h>
#include <string.h>

int main (int argc, char **argv)
{
    static const char *key = "1,2,3,4,5,6,7";
    char *extra;
    char *newKey;
    int newSize;
    int num1, num2, num3, num4;

    num1 = num2 = num3 = num4 = 0;

    newSize = strlen(key) * 2;
    extra = new char[newSize];
    newKey = new char[newSize];

    sscanf(key, "%d,%d,%d,%d,%s", &num1, &num2, &num3, &num4, extra);

    num3 = 12;
    num4 = 15;

    snprintf(newKey, newSize, "%d,%d,%d,%d,%s", num1, num2, num3, num4, extra);

    printf("%s\n", newKey);

    delete extra;
    delete newKey;

    return(0);
}

Posted: Fri Aug 27, 2004 8:23 pm
by aChallenged1
I hope that works out.

I was going to start asking a bunch of questions but decided it's not my place to ask them here, at this time in this thread.

My meger skills allow me to at least see enough of this to realize it should do what he asked for.

Posted: Sat Aug 28, 2004 1:45 am
by ieatacid
Yeah, I could easily do it within a macro but I need to do it with a plugin. I'll try KSmith's suggestion.

Thanks, and I'll be sure to post back if that does or does not solve my problem.

Posted: Sat Aug 28, 2004 6:36 am
by aChallenged1
Actually, I didn't write it with the intention of you macroing it, but instead as a map of what you might do in C/C++. I just don't have the knowledge to write it out that way.

Anyway, I hope that code does/did the trick for you!

Posted: Sat Aug 28, 2004 12:17 pm
by Digitalxero
Could just use

Code: Select all

GetArg(Arg1,key,3,FALSE,FALSE,FALSE,',');

Not sure where GetArg is defined, but here is the help tip VC++ pops up for it. I have no clude what ToParen or AnyNonAlphaNum does but I am fairly sure they default to FALSE and Seperator I know defaults to <space>

Code: Select all

PSTR GetArg(
PSTR szDest,
PSTR szSrc,
DWORD dwNumber,
[BOOL LeaveQuotes],
[BOOL ToParen],
[BOOL CSV],
[CHAR Seperator],
[BOOL AnyNonAlphaNum])

Posted: Sat Aug 28, 2004 3:29 pm
by ieatacid
Well, I couldn't get it to work using either of those methods. Maybe I'm doing something wrong.

I'm making a hud mover plugin. The format for an ini entry for a hud is:

name=TYPE,X,Y,RED,GREEN,BLUE,TEXT

Actually, it's just the second and third values that need to be changed.

It works great so far, *IF* the TEXT portion doesn't contain commas. Such as this:

Code: Select all

ManaRegen=3,5,340,0,255,255,MR - ${Me.ManaRegen}
Here's an example of a hud that messes it up:

Code: Select all

target=3,124,643,0,255,255,${If[${Target.ID},${Target.Level} - ${Target.Class} - ${Target.Distance} - ${Target.LineOfSight},]} 
This is code that works if the TEXT portion doesn't contain commas:

Code: Select all

		char * iniVal[MAX_STRING];
		char * pArg;
		pArg = strtok(iniString,",");
		int i;
		for (i = 0; pArg != NULL; i++)
		{
			iniVal[i] = pArg;
			DebugSpew("iniVal: %s\n",iniVal[i]);
			pArg = strtok(NULL, ",");
		}

		sprintf(iniString,"%s,%d,%d,%s,%s,%s",iniVal[0],newX,newY,iniVal[3],iniVal[4],iniVal[5],iniVal[6]);
		WritePrivateProfileString("Elements",Option,iniString,iniName);

Now, if the hud text contains commas as in the target hud above I can populate the array using the code above. Here's the debug spew from doing that:

Code: Select all

[MQ2]iniVal: 3

[MQ2]iniVal: 124

[MQ2]iniVal: 643

[MQ2]iniVal: 0

[MQ2]iniVal: 255

[MQ2]iniVal: 255

[MQ2]iniVal: ${If[${Target.ID}

[MQ2]iniVal: ${Target.Level} - ${Target.Class} - ${Target.Distance} - ${Target.LineOfSight}

[MQ2]iniVal: ]}
(for reference)

Code: Select all

target=3,124,643,0,255,255,${If[${Target.ID},${Target.Level} - ${Target.Class} - ${Target.Distance} - ${Target.LineOfSight},]} 

So the question is, how do I write back to the ini if the number of array elements could vary depending on the hud? Also the commas will need to be written back.

Or if I could just set

Code: Select all

0,255,255,${If[${Target.ID},${Target.Level} - ${Target.Class} - ${Target.Distance} - ${Target.LineOfSight},]} 
as iniVal[3] (it doesn't need to change any of that at the moment)


Any other ideas?

Posted: Sat Aug 28, 2004 4:00 pm
by Digitalxero
This is my code to parse args starting at a certin arg and reformat them back into a string. (ie "this is my sting I am parsing to see what is happening" if I run it though ParsArgs(szSting, 5) it will return "am Parsing to see what is happening")

Code: Select all

CHAR ParseArgs(PCHAR Line, int offset, CHAR Seperator=' ')
{
char szTemp[MAX_STRING];  
char szTmp[MAX_STRING];
int plcmnt = 0;
int j = 0;
for(int i=0;i<30;i++)   
{
	GetArg(szTemp,Line,i+offset,FALSE,FALSE,FALSE,Seperator);
	if(strlen(szTemp) == 0)
	{
		szTmp[plcmnt] = szTemp[j];
		break;
	}

	sprintf(szTemp,"%s%s",szTemp,Seperator);   

	for(j=0; j<strlen(szTemp); j++)
	{ 
		szTmp[plcmnt+j] = szTemp[j];  
	} 
	plcmnt += strlen(szTemp);
}

return szTemp[MAX_STING];
}

So some example code

Code: Select all

target=3,124,643,0,255,255,${If[${Target.ID},${Target.Level} - ${Target.Class} - ${Target.Distance} - ${Target.LineOfSight},]}

GetArg(Arg3,target,3,FALSE,FALSE,FALSE,',');
szRest = ParseArg(target, 4, ',');
with the above code you would get the following.

Code: Select all

Arg3 = 643
szRest would = 0,255,255,${If[${Target.ID},${Target.Level} - ${Target.Class} - ${Target.Distance} - ${Target.LineOfSight},]}

Posted: Sat Aug 28, 2004 11:21 pm
by ieatacid
Seems like a great idea but I can't get it to work. Either I get nothing for szRest or I get a CTD. :cry:

Posted: Wed Sep 01, 2004 1:38 am
by Digitalxero
Ok just got back in town today and decided to look at it again. The code I posted worked for me, but in a slightly diff format because my need was slightly diffrent. So I started working on a way to get something working for you. I found one very very simple way to do it, and one slightly more complex way to do it :)


KISS

Code: Select all

void Test(PSPAWNINFO pChar, PCHAR szLine)
{
	CHAR target[MAX_STRING] = {0};
	sprintf(target,"3,124,643,0,255,255,${If[${Target.ID},${Target.Level} - ${Target.Class} - ${Target.Distance} - ${Target.LineOfSight},]}");

	CHAR Arg3[MAX_STRING] = {0};
	CHAR szRest[MAX_STRING] = {0};

	GetArg(Arg3,target,3,FALSE,FALSE,FALSE,',');
	szRest = GetNextArg(target,3,FALSE,',');
	WriteChatColor(Arg3,USERCOLOR_CHAT_CHANNEL);
	WriteChatColor(szRest,USERCOLOR_CHAT_CHANNEL);
}

Slightly more complex way to do things.

Code: Select all

PSTR ParseArg(PSTR szDest, PCSTR szSrc, DWORD dwNumber, CHAR Separator=' ')
{
   PCSTR szTemp = szSrc;
   ZeroMemory(szDest,MAX_STRING);

   szTemp = GetNextArg(szTemp,dwNumber-1,FALSE,Separator);
   sprintf(szDest,"%s",szTemp);

   return szDest;
}

void Test(PSPAWNINFO pChar, PCHAR szLine)
{
	CHAR target[MAX_STRING] = {0};
	sprintf(target,"3,124,643,0,255,255,${If[${Target.ID},${Target.Level} - ${Target.Class} - ${Target.Distance} - ${Target.LineOfSight},]}");
	
	CHAR Arg3[MAX_STRING] = {0};
	CHAR szRest[MAX_STRING] = {0};

	GetArg(Arg3,target,3,FALSE,FALSE,FALSE,',');
	ParseArg(szRest, target, 4, ',');
	WriteChatColor(Arg3,USERCOLOR_CHAT_CHANNEL);
	WriteChatColor(szRest,USERCOLOR_CHAT_CHANNEL);
}

The more complex meathod saves you from having to type FALSE each time, and makes it so ArgNumber makes sence sequentualy(ie. if you put a 4 it starts with Arg 4 vs having to put a 3 to start with Arg 4 in the KISS meathod)

I came up with a 3rd meathod which puts each Arg into an array which is what I needed for mine

Code: Select all

int gNumArgs=0;
CHAR gParsedArgs[100][MAX_STRING] = {0}
PSTR ParseArgs(PSTR szDest, PCHAR szSrc, DWORD dwNumber, CHAR Separator)
{
	char szTemp[MAX_STRING];  
	ZeroMemory(szDest,MAX_STRING);
	int plcmnt = 0;
	int j = 0;
	for(int i=0;i<strlen(szSrc);i++)   
	{
		GetArg(szTemp,szSrc,i+dwNumber);
		if(strlen(szTemp) == 0)
		{
			szDest[plcmnt] = szTemp[j];
			break;
		}

		sprintf(gParsedArgs[i],"%s",szTemp);
		gNumArgs = i;
		sprintf(szTemp,"%s ",szTemp); 

		for(j=0; j<strlen(szTemp); j++)
		{ 
			szDest[plcmnt+j] = szTemp[j];  
		} 
		plcmnt += strlen(szTemp);
	}
	return szDest;
}

void Test()
{
CHAR Fuck[MAX_STRING] = {0};
		sprintf(Fuck,"This is a test and only a test. Please go back to sleep");
		ParseArg(gTemp,Fuck,9);
		WriteChatColor(gTemp,USERCOLOR_CHAT_CHANNEL);
}
gTemp wrote:Please go back to sleep

Code: Select all

gParsedArgs[0] = Please
gParsedArgs[1] = go
ect...
This 3rd way I created because I needed each arg to be passed to an array as well as the entire string returned to me.


I think I am going to Talk to Amadeus and see if he can change GetArg to have the BOOL settings after the Separator setting so you dont have to type FALSE three times when you want to set a custom seperator.

Posted: Wed Sep 01, 2004 3:55 pm
by ieatacid
Thanks for your reply :)

Over the weekend I got it working using Ksmith's code and some help from Efudd.