Jump to content

PHP Unpack files? Like how do you...


Hyvraine

Recommended Posts


  • Group:  Members
  • Topic Count:  17
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   14
  • Joined:  12/23/11
  • Last Seen:  

obtain the information inside a file? just like actOR how does it read the act files /fsh

is it possible doing so by hexing the client? if you know how or knows someone how to hex, can you teach me pl0x?

 

I have this php code that reads act files, I managed to make it extract the external offsets but it doesn't work with Kagerou and Lord Knight Female acts -_- I thought that maybe it's broken but actOR2 extracts the details fine. can anyone help me?

Link to comment

  • Group:  Members
  • Topic Count:  9
  • Topics Per Day:  0.00
  • Content Count:  379
  • Reputation:   304
  • Joined:  11/10/11
  • Last Seen:  

Can you paste here your PHP code, I will check if there is an error with (maybe an unsupported version).

Link to comment

  • Group:  Members
  • Topic Count:  17
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   14
  • Joined:  12/23/11
  • Last Seen:  

I removed the comments: 

	public function readAct($act,$save,$output,$code) {
		$this->act = fopen($act, "rb");
		$this->aHeader = unpack('H4ident/Sversion/SnumFrames', fread($this->act, 0x06));
		$this->aSize = filesize($act);
		$this->aAnimations = $this->aHeader['numFrames'];
		$this->aActions = ($this->aAnimations / 8);
		$this->aVersion = dechex($this->aHeader['version']);
		$this->aVersion = (double)preg_replace('{([0-9])(.*?)([0-9])}', '$1.$2', $this->aVersion);
		fseek($this->act, 0xA, SEEK_CUR);
		$currentAction = 0;
		$anim = 0;
		$pointerMemoryANIM = array();
		$pointerMemoryNF = array();
		$pointerMemoryPAT = array();
		while($anim < $this->aAnimations) {
			$this->aAnimData[$anim] = unpack('lnumFrames', fread($this->act, 0x04));
			for($nf = 0; $nf < $this->aAnimData[$anim]['numFrames']; $nf++) {
				fseek($this->act, 0x20, SEEK_CUR);
				$this->aAnimData[$anim][$nf] = unpack('lnumSubFrames', fread($this->act, 0x04));
				for($patNo = 0; $patNo < $this->aAnimData[$anim][$nf]['numSubFrames']; $patNo++) {
					$this->aAnimData[$anim][$nf][$patNo] = unpack('lxOffset/lyOffset/lsprNo/lmirrored/Cred/Cgreen/Cblue/Calpha', fread($this->act, 0x14));
					if($this->aVersion >= 2.0 && $this->aVersion <= 2.3) $this->aAnimData[$anim][$nf][$patNo] += unpack('fxyScale', fread($this->act, 0x04));
					if($this->aVersion >= 2.4) $this->aAnimData[$anim][$nf][$patNo] += unpack('fxScale/fyScale', fread($this->act, 0x08));
					$this->aAnimData[$anim][$nf][$patNo] += unpack('lrotation/lsprType', fread($this->act, 0x08));
					if($this->aVersion >= 2.5) $this->aAnimData[$anim][$nf][$patNo] += unpack('lsprWidth/lsprHeight', fread($this->act, 0x08));
					
				}
				$this->aAnimData[$anim][$nf] += unpack('lExtYOffset/lExtYOffset/lExtXOffset/lExtYOffset/lExtXOffset', fread($this->act, 0x14));
				$extrainfo = unpack('lbool', fread($this->act, 0x04));
				if($extrainfo['bool'] == 1) fseek($this->act, 0x10, SEEK_CUR);
			}
			$anim++;
		}
		$this->aSoundData = unpack('lnumSounds', fread($this->act, 0x04));
		if($this->aSoundData['numSounds'] > 0) {
			for($nSnd = 0; $nSnd < $this->aSoundData['numSounds']; $nSnd++) {
				$this->aSoundData[$nSnd] = unpack('H80file', fread($this->act, 0x28));
			}
		}
		for($intv = 0; $intv < $this->aAnimations; $intv++) {
			$this->aAnimData[$intv] += unpack('fanimSpeed', fread($this->act, 0x04));
		}

		foreach($this->aSoundData as $id=>$string) {
			if((string)$id != 'numSounds')
			$this->aSoundData[$id] = str_replace(pack('H2','00'),"",(pack('H*', $string['file'])));
		}
		echo '<pre>';
		echo print_r($this->aAnimData, true);
		echo print_r($this->aSoundData, true);
		echo '</pre>';
		fclose($this->act);
	}

 

I'm sorry I really have no knowledge of this. I got a lucky guess that's all ;)
-SoundNo got removed tho lol-

Edited by kenshn111
Link to comment

  • Group:  Members
  • Topic Count:  9
  • Topics Per Day:  0.00
  • Content Count:  379
  • Reputation:   304
  • Joined:  11/10/11
  • Last Seen:  

Replace this part:

for($patNo = 0; $patNo < $this->aAnimData[$anim][$nf]['numSubFrames']; $patNo++) {

$this->aAnimData[$anim][$nf][$patNo] = unpack('lxOffset/lyOffset/lsprNo/lmirrored/Cred/Cgreen/Cblue/Calpha', fread($this->act, 0x14));

if($this->aVersion >= 2.0 && $this->aVersion <= 2.3)

$this->aAnimData[$anim][$nf][$patNo] += unpack('fxyScale', fread($this->act, 0x04));

if($this->aVersion >= 2.4)

$this->aAnimData[$anim][$nf][$patNo] += unpack('fxScale/fyScale', fread($this->act, 0x08));

$this->aAnimData[$anim][$nf][$patNo] += unpack('lrotation/lsprType', fread($this->act, 0x08));

if($this->aVersion >= 2.5)

$this->aAnimData[$anim][$nf][$patNo] += unpack('lsprWidth/lsprHeight', fread($this->act, 0x08));

}

With:
				for($patNo = 0; $patNo < $this->aAnimData[$anim][$nf]['numSubFrames']; $patNo++) {
					$this->aAnimData[$anim][$nf][$patNo] = unpack('lxOffset/lyOffset/lsprNo/lmirrored/Cred/Cgreen/Cblue/Calpha', fread($this->act, 0x14));
					     if($this->aVersion  < 2.0 ); 
					else if($this->aVersion  < 2.4 ) $this->aAnimData[$anim][$nf][$patNo] += unpack('fxyScale/lrotation/lsprType', fread($this->act, 12));
					else if($this->aVersion == 2.4 ) $this->aAnimData[$anim][$nf][$patNo] += unpack('fxScale/fyScale/lrotation/lspr_type', fread($this->act, 16));
					else                             $this->aAnimData[$anim][$nf][$patNo] += unpack('fxScale/fyScale/lrotation/lsprType/lsprWidth/lsprHeight', fread($this->act, 24));
				}
There is also another problem after if you are reading a version < 2.3

Well your function is very ugly, hard to debug. If there is still a problem, can you report your act version ?

Edit: Sorry for the quote, the code bbcode was buggy...

Link to comment

  • Group:  Members
  • Topic Count:  17
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   14
  • Joined:  12/23/11
  • Last Seen:  

It still doesn't work:

 

Kagerou (version is 0 when I changed it to your code, 2.5 on the original code; weird lol) 

---

Original Code: Errors on: 

$this->aAnimData[$anim][$nf][$patNo] += unpack('lrotation/lsprType', fread($this->act, 0x08));
------------------------------------
else $this->aAnimData[$anim][$nf][$patNo] += unpack('fxScale/fyScale/lrotation/lsprType/lsprWidth/lsprHeight', fread($this->act, 24));

With my edit + yours: It just times out.

----

With Original Code + my edit: 

else $this->aAnimData[$anim][$nf][$patNo] += unpack('fxScale/fyScale/lrotation/lsprType/lsprWidth/lsprHeight', fread($this->act, 24));

----

I've added this line after my edit (after the ExtOffsets) :

----

$this->aAnimData[$anim][$nf] += unpack('lsoundNo', fread($this->act, 0x04));

----

 

Lord Knight Female (version is 2.5)

- Doesn't error on original code.

 

 

May I ask how you know what length to put in fread() ?

Edited by kenshn111
Link to comment

  • Group:  Members
  • Topic Count:  9
  • Topics Per Day:  0.00
  • Content Count:  379
  • Reputation:   304
  • Joined:  11/10/11
  • Last Seen:  

May I ask how you know what length to put in fread() ?

You just need to put enough length for the unpack().

Example:

unpack('fxScale/fyScale/lrotation/lsprType/lsprWidth/lsprHeight', fread($this->act, 24));
For each name in the unpack(), take the first letter:

f = float = 4 bytes

l = long = 4 bytes

4 (xScale) + 4 (yScale) + 4 (rotation) + 4 (sprType) + 4 (sprWidth) + 4 (sprHeight) = 24.

Can you upload a act you have problem with ?

Link to comment

  • Group:  Members
  • Topic Count:  17
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   14
  • Joined:  12/23/11
  • Last Seen:  

May I ask how you know what length to put in fread() ?

You just need to put enough length for the unpack().

Example:

unpack('fxScale/fyScale/lrotation/lsprType/lsprWidth/lsprHeight', fread($this->act, 24));
For each name in the unpack(), take the first letter:

f = float = 4 bytes

l = long = 4 bytes

4 (xScale) + 4 (yScale) + 4 (rotation) + 4 (sprType) + 4 (sprWidth) + 4 (sprHeight) = 24.

Can you upload a act you have problem with ?

 

And why isn't that in the PHP Manual.. or maybe I just missed it:

 

Here are the files: 4008_4211.rar

 

Edit:

Why does this work tho: 

$this->aAnimData[$anim][$nf] += unpack('lExtYOffset/lExtYOffset/lExtXOffset/lExtYOffset/lExtXOffset', fread($this->act, 0x14));

except f_4008 and 4211 of course

Edited by kenshn111
Link to comment

  • Group:  Members
  • Topic Count:  9
  • Topics Per Day:  0.00
  • Content Count:  379
  • Reputation:   304
  • Joined:  11/10/11
  • Last Seen:  

Ok I see the problem.

// Change this:

$this->aAnimData[$anim][$nf] += unpack('lExtYOffset/lExtYOffset/lExtXOffset/lExtYOffset/lExtXOffset', fread($this->act, 0x14));

$extrainfo = unpack('lbool', fread($this->act, 0x04));

if($extrainfo['bool'] == 1) fseek($this->act, 0x10, SEEK_CUR);

// To:

fseek( $this->act, 4, SEEK_CUR ); // sound id, store it in a variable if you want

$extrainfo = unpack('lbool', fread($this->act, 0x04)); // Extras infos (if you need)

fseek($this->act, 0x10 * $extrainfo['bool'], SEEK_CUR);

  • Upvote 1
Link to comment

  • Group:  Members
  • Topic Count:  17
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   14
  • Joined:  12/23/11
  • Last Seen:  

Hey ;o

 

It still doesn't work :( However this is the code with the comments and I added my edit to view the external offsets:

again it doesn't work with Kagerou and F_Lord Knight

 


	public function readAct($act,$save,$output,$code) {
		// Open the act file.
		$this->act = fopen($act, "rb");
		// Just like on the sprites we read some infos: ident=ACT, version, number of frames
		$this->aHeader = unpack('H4ident/Sversion/SnumFrames', fread($this->act, 0x06));
		// Get the filesize (used this for sql database etc.)
		$this->aSize = filesize($act);
		// The total amount of animations used
		$this->aAnimations = $this->aHeader['numFrames'];
		// The number of actions is the num of animations divided by 8.
		// So what are the "actions"? I just named them like this. They're basicly the directions you can look at. Just like in actOR.
		//  \ | /  }
		// -- o -- }-> 8 in total
		//  / | \  }
		$this->aActions = ($this->aAnimations / 8);
		// The version
		$this->aVersion = dechex($this->aHeader['version']);
		// Format the version nicely
		$this->aVersion = (double)preg_replace('{([0-9])(.*?)([0-9])}', '$1.$2', $this->aVersion);
		// Skip some useless data on one needs
		fseek($this->act, 0xA, SEEK_CUR);
		// We're currently at action 0
		$currentAction = 0;
		// Our index for the while loop
		$anim = 0;
		// Arrays we save our informations in
		$pointerMemoryANIM = array();
		$pointerMemoryNF = array();
		$pointerMemoryPAT = array();
		// Lets go through the animations, shall we?
		while($anim < $this->aAnimations) {
			// First how many Frames does the action have?
			// On a normal headgear this would be 3. Because when you are sitting downwards you can look down with your head, left and right. So 3 in total.
			// Ff you have an animated sprite its more obviously
			$this->aAnimData[$anim] = unpack('lnumFrames', fread($this->act, 0x04));
			// So lets loop through those Frames, cuz they all got individual informations for us
			for($nf = 0; $nf < $this->aAnimData[$anim]['numFrames']; $nf++) {
				// Skip useless data
				fseek($this->act, 0x20, SEEK_CUR);
				// And now we check how many subFrames this frame got. Or better said Patterns (like in actOR)
				// For example, if you have a monster sprite and some special effects as seperates sprite frames in the .spr file.
				// You'll have to paste them onto the stage right? Thats where the patters are being created. Like layers in photoshop.
				$this->aAnimData[$anim][$nf] = unpack('lnumSubFrames', fread($this->act, 0x04));
				// Loop through the patterns or layers for more info about them
				for($patNo = 0; $patNo < $this->aAnimData[$anim][$nf]['numSubFrames']; $patNo++) {
					// Grab OffsetX, OffsetY (I didnt go to the point where i would need to figure out the (0,0) coordinates to which this relates to)
					// Spr number (the sprite frame from the .spr file)
					// mirrored? red,green,blue,alpha
					$this->aAnimData[$anim][$nf][$patNo] = unpack('lxOffset/lyOffset/lsprNo/lmirrored/Cred/Cgreen/Cblue/Calpha', fread($this->act, 0x14));
					// Now there are different informations stored depending on the act version.
					// from 2.0 till 2.3 we grab XYScale
					if($this->aVersion >= 2.0 && $this->aVersion <= 2.3) $this->aAnimData[$anim][$nf][$patNo] += unpack('fxyScale', fread($this->act, 0x04));
					// If its 2.4 or higher we get XScale and YScale
					if($this->aVersion >= 2.4) $this->aAnimData[$anim][$nf][$patNo] += unpack('fxScale/fyScale', fread($this->act, 0x08));
					// Now the rotation and the sprType (32 bit or not)
					$this->aAnimData[$anim][$nf][$patNo] += unpack('lrotation/lsprType', fread($this->act, 0x08));
					// If its 2.5 or higher we also got the sprites Width and Height (we should have this info from the sprite itself already though, dont ask me why they put this there)
					if($this->aVersion >= 2.5) $this->aAnimData[$anim][$nf][$patNo] += unpack('lsprWidth/lsprHeight', fread($this->act, 0x08));
					
				}
				// The end of the Patterns/Layers on the Frame.
				// So lets see if it has some kind of sound attached to it? (To the frame)
				$this->aAnimData[$anim][$nf] += unpack('lsoundNo/fExtXOffset/lExtXOffset/lExtXOffset/sExtYOffset', fread($this->act, 0x14));
				// See if there are some extra info, if yes: Skip it, we dont need that crap
				$extrainfo = unpack('lbool', fread($this->act, 0x04));
				if($extrainfo['bool'] == 1) fseek($this->act, 0x10, SEEK_CUR);
			}
			// Increase our Action and loop through the next frames and patterns^^
			$anim++;
		}
		// After reading all infos we are at the end of the act file where the sound informations are stored.
		$this->aSoundData = unpack('lnumSounds', fread($this->act, 0x04));
		// It has some sounds?
		if($this->aSoundData['numSounds'] > 0) {
			// We loop through number of sounds to grab it.
			for($nSnd = 0; $nSnd < $this->aSoundData['numSounds']; $nSnd++) {
				// Now because the sound file is a string (directs to some .wav file in the grf), saved in hex and for our advantage always exactly 40 bytes long
				// we read the string as hex values and save it like that
				$this->aSoundData[$nSnd] = unpack('H80file', fread($this->act, 0x28));
			}
		}
		// Oh we also got the interval a frame is played at? That info is ours! >
		for($intv = 0; $intv < $this->aAnimations; $intv++) {
			$this->aAnimData[$intv] += unpack('fanimSpeed', fread($this->act, 0x04));
		}
		// Lets format our earlier saved hex string to a actual string with letters and not just numbers ^^
		// Some note: If there are sounds in the act (mostly monsters) there always appears to be a sound called "atk", just like that.
		// It doesnt exist in the grf and I have no idea how it is being handled. Maybe some description of the sounds?
		foreach($this->aSoundData as $id=>$string) {
			if((string)$id != 'numSounds')
			$this->aSoundData[$id] = str_replace(pack('H2','00'),"",(pack('H*', $string['file'])));
		}
		// Lets display our results of the act file.
		$act = '$jact';
		if (strpos($output, "head")) {
		$act = '$hact';
		}
		if ($save==1) {
		
file_put_contents($output."$code.php", "<?php $act = ".var_export($this->aAnimData,true).";?>");
		}
		else {
		echo '<pre>';
		echo print_r($this->aAnimData, true);
		echo print_r($this->aSoundData, true);
		echo '</pre>';
		}
		// Close the file, its no longer needed. =(
		fclose($this->act);
	}
Link to comment

  • Group:  Members
  • Topic Count:  9
  • Topics Per Day:  0.00
  • Content Count:  379
  • Reputation:   304
  • Joined:  11/10/11
  • Last Seen:  

Did you even read what I wrote ? :(

fseek( $this->act, 4, SEEK_CUR ); // sound id, store it in a variable if you want

$extrainfo = unpack('lbool', fread($this->act, 0x04));// Extras infos (if you need)

fseek($this->act, 0x10 * $extrainfo['bool'], SEEK_CUR);

If you want to store extrainfo, you have to do this:
fseek( $this->act, 4, SEEK_CUR ); // sound id, store it in a variable if you want

extract( unpack("lnumExtraInfo", fread($this->fp,0x04) ) );
$extraInfos = array();
for ( $i=0; $i<$numExtraInfo; ++$i ) {
    $extraInfos[$i] = unpack( "x4/VExtXOffsetX/VExtXOffsetY/x4", fread($this->act,16) );
}
Edit: Why the bbcode is buggy ? :(
Link to comment

  • Group:  Members
  • Topic Count:  17
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   14
  • Joined:  12/23/11
  • Last Seen:  

Yeah I have read your note and tried to extract it, it still didn't work (now it doesn't read all of them):

Sorry if I'm wasting your time >_>

 

This is now the code with your fix: 


	public function readAct($act,$save,$output,$code) {
		// Open the act file.
		$this->act = fopen($act, "rb");
		// Just like on the sprites we read some infos: ident=ACT, version, number of frames
		$this->aHeader = unpack('H4ident/Sversion/SnumFrames', fread($this->act, 0x06));
		// Get the filesize (used this for sql database etc.)
		$this->aSize = filesize($act);
		// The total amount of animations used
		$this->aAnimations = $this->aHeader['numFrames'];
		// The number of actions is the num of animations divided by 8.
		// So what are the "actions"? I just named them like this. They're basicly the directions you can look at. Just like in actOR.
		//  \ | /  }
		// -- o -- }-> 8 in total
		//  / | \  }
		$this->aActions = ($this->aAnimations / 8);
		// The version
		$this->aVersion = dechex($this->aHeader['version']);
		// Format the version nicely
		$this->aVersion = (double)preg_replace('{([0-9])(.*?)([0-9])}', '$1.$2', $this->aVersion);
		// Skip some useless data on one needs
		fseek($this->act, 0xA, SEEK_CUR);
		// We're currently at action 0
		$currentAction = 0;
		// Our index for the while loop
		$anim = 0;
		// Arrays we save our informations in
		$pointerMemoryANIM = array();
		$pointerMemoryNF = array();
		$pointerMemoryPAT = array();
		// Lets go through the animations, shall we?
		while($anim < $this->aAnimations) {
			// First how many Frames does the action have?
			// On a normal headgear this would be 3. Because when you are sitting downwards you can look down with your head, left and right. So 3 in total.
			// Ff you have an animated sprite its more obviously
			$this->aAnimData[$anim] = unpack('lnumFrames', fread($this->act, 0x04));
			// So lets loop through those Frames, cuz they all got individual informations for us
			for($nf = 0; $nf < $this->aAnimData[$anim]['numFrames']; $nf++) {
				// Skip useless data
				fseek($this->act, 0x20, SEEK_CUR);
				// And now we check how many subFrames this frame got. Or better said Patterns (like in actOR)
				// For example, if you have a monster sprite and some special effects as seperates sprite frames in the .spr file.
				// You'll have to paste them onto the stage right? Thats where the patters are being created. Like layers in photoshop.
				$this->aAnimData[$anim][$nf] = unpack('lnumSubFrames', fread($this->act, 0x04));
				// Loop through the patterns or layers for more info about them
				for($patNo = 0; $patNo < $this->aAnimData[$anim][$nf]['numSubFrames']; $patNo++) {
					$this->aAnimData[$anim][$nf][$patNo] = unpack('lxOffset/lyOffset/lsprNo/lmirrored/Cred/Cgreen/Cblue/Calpha', fread($this->act, 0x14));
					     if($this->aVersion  < 2.0 ); 
					else if($this->aVersion  < 2.4 ) $this->aAnimData[$anim][$nf][$patNo] += unpack('fxyScale/lrotation/lsprType', fread($this->act, 12));
					else if($this->aVersion == 2.4 ) $this->aAnimData[$anim][$nf][$patNo] += unpack('fxScale/fyScale/lrotation/lspr_type', fread($this->act, 16));
					else                             $this->aAnimData[$anim][$nf][$patNo] += unpack('fxScale/fyScale/lrotation/lsprType/lsprWidth/lsprHeight', fread($this->act, 24));
				}
				// The end of the Patterns/Layers on the Frame.
				// So lets see if it has some kind of sound attached to it? (To the frame)
			fseek( $this->act, 4, SEEK_CUR ); // sound id, store it in a variable if you want

extract( unpack("lnumExtraInfo", fread($this->fp,0x04) ) );
$extraInfos = array();
for ( $i=0; $i<$numExtraInfo; ++$i ) {
    $extraInfos[$i] = unpack( "x4/VExtXOffsetX/VExtXOffsetY/x4", fread($this->act,16) );
}
			}
			// Increase our Action and loop through the next frames and patterns^^
			$anim++;
		}
		// After reading all infos we are at the end of the act file where the sound informations are stored.
		$this->aSoundData = unpack('lnumSounds', fread($this->act, 0x04));
		// It has some sounds?
		if($this->aSoundData['numSounds'] > 0) {
			// We loop through number of sounds to grab it.
			for($nSnd = 0; $nSnd < $this->aSoundData['numSounds']; $nSnd++) {
				// Now because the sound file is a string (directs to some .wav file in the grf), saved in hex and for our advantage always exactly 40 bytes long
				// we read the string as hex values and save it like that
				$this->aSoundData[$nSnd] = unpack('H80file', fread($this->act, 0x28));
			}
		}
		// Oh we also got the interval a frame is played at? That info is ours! >
		for($intv = 0; $intv < $this->aAnimations; $intv++) {
			$this->aAnimData[$intv] += unpack('fanimSpeed', fread($this->act, 0x04));
		}
		// Lets format our earlier saved hex string to a actual string with letters and not just numbers ^^
		// Some note: If there are sounds in the act (mostly monsters) there always appears to be a sound called "atk", just like that.
		// It doesnt exist in the grf and I have no idea how it is being handled. Maybe some description of the sounds?
		foreach($this->aSoundData as $id=>$string) {
			if((string)$id != 'numSounds')
			$this->aSoundData[$id] = str_replace(pack('H2','00'),"",(pack('H*', $string['file'])));
		}
		// Lets display our results of the act file.
		$act = '$jact';
		if (strpos($output, "head")) {
		$act = '$hact';
		}
		if ($save==1) {
		
file_put_contents($output."$code.php", "<?php $act = ".var_export($this->aAnimData,true).";?>");
		}
		else {
		echo '<pre>';
		echo print_r($this->aAnimData, true);
		echo print_r($this->aSoundData, true);
		echo '</pre>';
		}
		// Close the file, its no longer needed. =(
		fclose($this->act);
	}

Edited by kenshn111
Link to comment

  • Group:  Members
  • Topic Count:  9
  • Topics Per Day:  0.00
  • Content Count:  379
  • Reputation:   304
  • Joined:  11/10/11
  • Last Seen:  

Oups the latest thing I wrote was wrong:

				fseek( $this->act, 4, SEEK_CUR ); // sound id, store it in a variable if you want

extract( unpack("lnumExtraInfo", fread($this->act,0x04) ) );

$extraInfos = array();

for ( $i=0; $i<$numExtraInfo; ++$i ) {

$extraInfos[$i] = unpack( "x2/VExtXOffsetX/VExtXOffsetY/x2", fread($this->act,10) );

}

Now working.
Link to comment

  • Group:  Members
  • Topic Count:  17
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   14
  • Joined:  12/23/11
  • Last Seen:  

Lol

Unsupported Operand Types: 

else $this->aAnimData[$anim][$nf][$patNo] += unpack('fxScale/fyScale/lrotation/lsprType/lsprWidth/lsprHeight', fread($this->act, 24));
Link to comment

  • Group:  Members
  • Topic Count:  9
  • Topics Per Day:  0.00
  • Content Count:  379
  • Reputation:   304
  • Joined:  11/10/11
  • Last Seen:  

Well test this:

	public function readAct($act,$save,$output,$code) {

// Open the act file.

$this->act = fopen($act, "rb");

// Just like on the sprites we read some infos: ident=ACT, version, number of frames

$this->aHeader = unpack('H4ident/Sversion/SnumFrames', fread($this->act, 0x06));

// Get the filesize (used this for sql database etc.)

$this->aSize = filesize($act);

// The total amount of animations used

$this->aAnimations = $this->aHeader['numFrames'];

// The number of actions is the num of animations divided by 8.

// So what are the "actions"? I just named them like this. They're basicly the directions you can look at. Just like in actOR.

// \ | / }

// -- o -- }-> 8 in total

// / | \ }

$this->aActions = ($this->aAnimations / 8);

// The version

$this->aVersion = dechex($this->aHeader['version']);

// Format the version nicely

$this->aVersion = (double)preg_replace('{([0-9])(.*?)([0-9])}', '$1.$2', $this->aVersion);

// Skip some useless data on one needs

fseek($this->act, 0xA, SEEK_CUR);

// We're currently at action 0

$currentAction = 0;

// Our index for the while loop

$anim = 0;

// Arrays we save our informations in

$pointerMemoryANIM = array();

$pointerMemoryNF = array();

$pointerMemoryPAT = array();

// Lets go through the animations, shall we?

while($anim < $this->aAnimations) {

// First how many Frames does the action have?

// On a normal headgear this would be 3. Because when you are sitting downwards you can look down with your head, left and right. So 3 in total.

// Ff you have an animated sprite its more obviously

$this->aAnimData[$anim] = unpack('lnumFrames', fread($this->act, 0x04));

// So lets loop through those Frames, cuz they all got individual informations for us

for($nf = 0; $nf < $this->aAnimData[$anim]['numFrames']; $nf++) {

// Skip useless data

fseek($this->act, 0x20, SEEK_CUR);

// And now we check how many subFrames this frame got. Or better said Patterns (like in actOR)

// For example, if you have a monster sprite and some special effects as seperates sprite frames in the .spr file.

// You'll have to paste them onto the stage right? Thats where the patters are being created. Like layers in photoshop.

$this->aAnimData[$anim][$nf] = unpack('lnumSubFrames', fread($this->act, 0x04));

// Loop through the patterns or layers for more info about them

for($patNo = 0; $patNo < $this->aAnimData[$anim][$nf]['numSubFrames']; $patNo++) {

$this->aAnimData[$anim][$nf][$patNo] = unpack('lxOffset/lyOffset/lsprNo/lmirrored/Cred/Cgreen/Cblue/Calpha', fread($this->act, 0x14));

if($this->aVersion < 2.0 );

else if($this->aVersion < 2.4 ) $this->aAnimData[$anim][$nf][$patNo] += unpack('fxyScale/lrotation/lsprType', fread($this->act, 12));

else if($this->aVersion == 2.4 ) $this->aAnimData[$anim][$nf][$patNo] += unpack('fxScale/fyScale/lrotation/lspr_type', fread($this->act, 16));

else $this->aAnimData[$anim][$nf][$patNo] += unpack('fxScale/fyScale/lrotation/lsprType/lsprWidth/lsprHeight', fread($this->act, 24));

}

// The end of the Patterns/Layers on the Frame.

// So lets see if it has some kind of sound attached to it? (To the frame)

fseek( $this->act, 4, SEEK_CUR ); // sound id, store it in a variable if you want

extract( unpack("lnumExtraInfo", fread($this->act,0x04) ) );

$extraInfos = array();

for ( $i=0; $i<$numExtraInfo; ++$i ) {

$extraInfos[$i] = unpack( "x4/VExtXOffsetX/VExtXOffsetY/x4", fread($this->act,16) );

}

}

// Increase our Action and loop through the next frames and patterns^^

$anim++;

}

// After reading all infos we are at the end of the act file where the sound informations are stored.

$this->aSoundData = unpack('lnumSounds', fread($this->act, 0x04));

// It has some sounds?

if($this->aSoundData['numSounds'] > 0) {

// We loop through number of sounds to grab it.

for($nSnd = 0; $nSnd < $this->aSoundData['numSounds']; $nSnd++) {

// Now because the sound file is a string (directs to some .wav file in the grf), saved in hex and for our advantage always exactly 40 bytes long

// we read the string as hex values and save it like that

$this->aSoundData[$nSnd] = unpack('H80file', fread($this->act, 0x28));

}

}

// Oh we also got the interval a frame is played at? That info is ours! >:D

for($intv = 0; $intv < $this->aAnimations; $intv++) {

$this->aAnimData[$intv] += unpack('fanimSpeed', fread($this->act, 0x04));

}

// Lets format our earlier saved hex string to a actual string with letters and not just numbers ^^

// Some note: If there are sounds in the act (mostly monsters) there always appears to be a sound called "atk", just like that.

// It doesnt exist in the grf and I have no idea how it is being handled. Maybe some description of the sounds?

foreach($this->aSoundData as $id=>$string) {

if((string)$id != 'numSounds')

$this->aSoundData[$id] = str_replace(pack('H2','00'),"",(pack('H*', $string['file'])));

}

// Lets display our results of the act file.

$act = '$jact';

if (strpos($output, "head")) {

$act = '$hact';

}

else {

echo '<pre>';

echo print_r($this->aAnimData, true);

echo print_r($this->aSoundData, true);

echo '</pre>';

}

// Close the file, its no longer needed. =(

fclose($this->act);

}

  • Upvote 1
Link to comment

  • Group:  Members
  • Topic Count:  17
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   14
  • Joined:  12/23/11
  • Last Seen:  

OMG! It's finally working!

Thank you so much!

 

Just one more question: where did you study this stuff? how did you know that they'll be available in:

 
                extract( unpack("lnumExtraInfo", fread($this->act,0x04) ) );
 
?? I just want to learn more 8D
Link to comment

  • Group:  Members
  • Topic Count:  9
  • Topics Per Day:  0.00
  • Content Count:  379
  • Reputation:   304
  • Joined:  11/10/11
  • Last Seen:  

You can find it by reversing the client, or by searching in some source tools as Actor.

But in the past we just have reverse engineering and trying to search by finding redundant informations in the hex.

But your current function is not really good, some bugs on some versions, not really good name convention...

If you want mine:

<?php

/**
* @fileoverview Action - Loader for the Gravity .act file
* @author Vincent Thibault (alias KeyWorld - Twitter: @robrowser)
* @version 2.3.0
*/

class Action
{

	private $fp, $version;
	public $actions = array(), $sounds  = array();



	/// Loader on construct
	public function __construct($filename=false)
	{
		if ( $filename )
			$this->open($filename);
	}




	/// Open a Action file
	/// Do some check before.
	public function open($filename)
	{
		if ( substr( $filename, 0, 7 ) !== "data://" )
		{
			if ( ! file_exists($filename) )
				throw new Exception("ACT::open() - Can't find file '{$filename}'.");
	
			if ( ! is_readable($filename) )
				throw new Exception("ACT::open() - Can't read file '{$filename}'.");

			$this->size = filesize($filename);
	
			if ( $this->size < 0x08 )
				throw new Exception("ACT::open() - Incorrect file size, shoulnot be a ACT file");
		}

		$this->fp = fopen($filename,'r');
		$this->load();
	}



	

	/// Load an Action file
	private function load()
	{
		extract( unpack( "a2head/C2ver", fread($this->fp, 0x4 ) ) );

		if ( $head !== 'AC' )
			throw new Exception("ACT::load() - Incorrect act header, is '{$head}' - should be 'AC'");

		$this->version = $ver1/10 + $ver2;
		$this->readActions();


		if ( $this->version >= 2.1 )
		{
			// Sound
			extract( unpack("Vcount", fread( $this->fp, 0x04 ) ) );
			for ( $i=0; $i<$count; ++$i )
				$this->sounds[$i] = current( unpack('a40', fread( $this->fp, 40 ) ) );

			// Delay
			if ( $this->version >= 2.2 )
				foreach( $this->actions as &$action )
					$action->delay = current( unpack("f", fread($this->fp, 0x04) ) ) * 25;
		}
	}



	/// Load ACT actions
	private function readActions()
	{
		extract( unpack("vcount/x10", fread( $this->fp, 12 ) ) );
		for ( $i=0; $i<$count; ++$i )
		{
			$this->actions[$i] = (object) array(
				"animations" => $this->readAnimations(),
				"delay"      => 150
			);
		}
	}




	/// Load ACT animations
	private function readAnimations()
	{
		extract( unpack("Vcount", fread( $this->fp, 0x04 ) ) );
		$animations = array();
	
		for ( $i=0; $i<$count; ++$i )
		{
			fseek( $this->fp, 32, SEEK_CUR );
			$animations[$i] = $this->readLayers();
		}
	
		return $animations;
	}



	/// Load ACT layers
	private function readLayers()
	{
		extract( unpack("Vcount", fread( $this->fp, 0x04 ) ) );
		$layers = array();


		if ( $this->version < 2.0 )
		{
			$size   = 0;
			$struct = "";
		}
		else if ( $this->version < 2.4 )
		{
			$size   = 16;
			$struct = "C4color/f1scale/Vangle/Vspr_type";
		}

		else if ( $this->version === 2.4 )
		{
			$size   = 20;
			$struct = "C4color/f2scale/Vangle/Vspr_type";
		}
	
		else
		{
			$size   = 28;
			$struct = "C4color/f2scale/Vangle/Vspr_type/Vwidth/Vheight";
		}


		for ( $i=0; $i<$count; ++$i )
		{
			$param      = unpack( "Vx/Vy/Vindex/Vis_mirror/" . $struct, fread($this->fp, $size + 16) );
			$layers[$i] = (object) array(
				"pos"       => array( $param['x'], $param['y'] ),
				"index"     => $param['index'],
				"is_mirror" => $param['is_mirror'],
				"scale"     => array( 1.0, 1.0 ),
				"color"     => array( 255, 255, 255, 255 ),
				"angle"     => 0,
				"spr_type"  => 0,
				"width"     => 0,
				"height"    => 0
			);
			$layer      = &$layers[$i];

			$layer->color[0] = $param['color1']/255;
			$layer->color[1] = $param['color2']/255;
			$layer->color[2] = $param['color3']/255;
			$layer->color[3] = $param['color4']/255;
			$layer->scale[0] = isset($param['scale1']) ? $param['scale1'] : 1.0;
			$layer->scale[1] = isset($param['scale2']) ? $param['scale2'] : $layer->scale[0];

			if ( isset($param['angle']) )    $layer->angle    = $param['angle'];
			if ( isset($param['spr_type']) ) $layer->spr_type = $param['spr_type'];
			if ( isset($param['width']) )    $layer->width    = $param['width'];
			if ( isset($param['height']) )   $layer->height   = $param['height'];
		}


		$sound = -1;
		if ( $this->version >= 2.0 )
			extract( unpack("Vsound", fread($this->fp,0x04) ) );



		$pos= array();
	
		if ( $this->version >= 2.3 )
		{
			extract( unpack("Vcount", fread($this->fp,0x04) ) );
			for ( $i=0; $i<$count; ++$i ) {
				$pos[$i] = (object) unpack( "Vunk/Vx/Vy/Vattr", fread($this->fp,16) );
			}
		}
	
		return (object) array(
			"layers"    => $layers,
			"sound"     => $sound,
			"pos"       => $pos	
		);
	}



	/// Compile a ACT file
	public function compile()
	{

		// Header
		$result  = pack('a2C2', 'AC', $this->version * 10 % 10, floor($this->version * 10)/10 );

		// Action count
		$result .= pack( 'vx10', count($this->actions) );

		// Compile each actions
		foreach( $this->actions as $action )
		{

			// Number of animations
			$result .= pack( 'V', count($action->animations) );

			// Compile animations
			foreach( $action->animations as $animation )
			{

				// 32 uknown offset + layers count
				$result .= pack( 'x32V', count($animation->layers) );

				// Compile layers
				foreach( $animation->layers as $layer )
				{
					$result .= pack('V4', $layer->pos[0], $layer->pos[1], $layer->index, $layer->is_mirror);

					if ( $this->version < 2.0 )
						continue;

					else if ( $this->version < 2.4 )
						$result .= pack(
							'C4fV2',
							$layer->color[0]*255,
							$layer->color[1]*255,
							$layer->color[2]*255,
							$layer->color[3]*255,
							$layer->scale[0],
							$layer->angle,
							$layer->spr_type
						);

					else if ( $this->version === 2.4 )
						$result .= pack(
							'C4f2V2',
							$layer->color[0]*255,
							$layer->color[1]*255,
							$layer->color[2]*255,
							$layer->color[3]*255,
							$layer->scale[0],
							$layer->scale[1],
							$layer->angle,
							$layer->spr_type
						);

					else 
						$result .= pack(
							'C4f2V4',
							$layer->color[0]*255, 
							$layer->color[1]*255,
							$layer->color[2]*255,
							$layer->color[3]*255,
							$layer->scale[0],
							$layer->scale[1],
							$layer->angle,
							$layer->spr_type,
							$layer->width,
							$layer->height
						);
				}


				// Animation sound
				if ( $this->version >= 2.0 )
				{
					$result .= pack('V', $animation->sound );
				}

				// Animation imf (head pos)
				if ( $this->version >= 2.3 )
				{
					$result .= pack('V', count($animation->pos) );
					foreach( $animation->pos as $pos )
					{
						$result .=  pack( 'V4', $pos->unk, $pos->x, $pos->y, $pos->attr );
					}
				}
			}
		}

		
		if ( $this->version >= 2.1 )
		{
			// Comple sounds
			$result .= pack( 'V', count($this->sounds) );
			foreach( $this->sounds as $sound )
			{
				$result .= pack( 'a40', $sound );
			}

			// Comple delay
			if ( $this->version >= 2.2 )
			{
				foreach( $this->actions as $action )
				{
					$result .= pack( 'f', $action->delay / 25 );
				}
			}
		}

		return $result;
	}
}

?>
Maybe should I release my character-viewer...

Have fun~

Link to comment

  • Group:  Members
  • Topic Count:  17
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   14
  • Joined:  12/23/11
  • Last Seen:  

You can find it by reversing the client, or by searching in some source tools as Actor.

But in the past we just have reverse engineering and trying to search by finding redundant informations in the hex.

But your current function is not really good, some bugs on some versions, not really good name convention...

If you want mine:

<?php

/**
* @fileoverview Action - Loader for the Gravity .act file
* @author Vincent Thibault (alias KeyWorld - Twitter: @robrowser)
* @version 2.3.0
*/

class Action
{

	private $fp, $version;
	public $actions = array(), $sounds  = array();



	/// Loader on construct
	public function __construct($filename=false)
	{
		if ( $filename )
			$this->open($filename);
	}




	/// Open a Action file
	/// Do some check before.
	public function open($filename)
	{
		if ( substr( $filename, 0, 7 ) !== "data://" )
		{
			if ( ! file_exists($filename) )
				throw new Exception("ACT::open() - Can't find file '{$filename}'.");
	
			if ( ! is_readable($filename) )
				throw new Exception("ACT::open() - Can't read file '{$filename}'.");

			$this->size = filesize($filename);
	
			if ( $this->size < 0x08 )
				throw new Exception("ACT::open() - Incorrect file size, shoulnot be a ACT file");
		}

		$this->fp = fopen($filename,'r');
		$this->load();
	}



	

	/// Load an Action file
	private function load()
	{
		extract( unpack( "a2head/C2ver", fread($this->fp, 0x4 ) ) );

		if ( $head !== 'AC' )
			throw new Exception("ACT::load() - Incorrect act header, is '{$head}' - should be 'AC'");

		$this->version = $ver1/10 + $ver2;
		$this->readActions();


		if ( $this->version >= 2.1 )
		{
			// Sound
			extract( unpack("Vcount", fread( $this->fp, 0x04 ) ) );
			for ( $i=0; $i<$count; ++$i )
				$this->sounds[$i] = current( unpack('a40', fread( $this->fp, 40 ) ) );

			// Delay
			if ( $this->version >= 2.2 )
				foreach( $this->actions as &$action )
					$action->delay = current( unpack("f", fread($this->fp, 0x04) ) ) * 25;
		}
	}



	/// Load ACT actions
	private function readActions()
	{
		extract( unpack("vcount/x10", fread( $this->fp, 12 ) ) );
		for ( $i=0; $i<$count; ++$i )
		{
			$this->actions[$i] = (object) array(
				"animations" => $this->readAnimations(),
				"delay"      => 150
			);
		}
	}




	/// Load ACT animations
	private function readAnimations()
	{
		extract( unpack("Vcount", fread( $this->fp, 0x04 ) ) );
		$animations = array();
	
		for ( $i=0; $i<$count; ++$i )
		{
			fseek( $this->fp, 32, SEEK_CUR );
			$animations[$i] = $this->readLayers();
		}
	
		return $animations;
	}



	/// Load ACT layers
	private function readLayers()
	{
		extract( unpack("Vcount", fread( $this->fp, 0x04 ) ) );
		$layers = array();


		if ( $this->version < 2.0 )
		{
			$size   = 0;
			$struct = "";
		}
		else if ( $this->version < 2.4 )
		{
			$size   = 16;
			$struct = "C4color/f1scale/Vangle/Vspr_type";
		}

		else if ( $this->version === 2.4 )
		{
			$size   = 20;
			$struct = "C4color/f2scale/Vangle/Vspr_type";
		}
	
		else
		{
			$size   = 28;
			$struct = "C4color/f2scale/Vangle/Vspr_type/Vwidth/Vheight";
		}


		for ( $i=0; $i<$count; ++$i )
		{
			$param      = unpack( "Vx/Vy/Vindex/Vis_mirror/" . $struct, fread($this->fp, $size + 16) );
			$layers[$i] = (object) array(
				"pos"       => array( $param['x'], $param['y'] ),
				"index"     => $param['index'],
				"is_mirror" => $param['is_mirror'],
				"scale"     => array( 1.0, 1.0 ),
				"color"     => array( 255, 255, 255, 255 ),
				"angle"     => 0,
				"spr_type"  => 0,
				"width"     => 0,
				"height"    => 0
			);
			$layer      = &$layers[$i];

			$layer->color[0] = $param['color1']/255;
			$layer->color[1] = $param['color2']/255;
			$layer->color[2] = $param['color3']/255;
			$layer->color[3] = $param['color4']/255;
			$layer->scale[0] = isset($param['scale1']) ? $param['scale1'] : 1.0;
			$layer->scale[1] = isset($param['scale2']) ? $param['scale2'] : $layer->scale[0];

			if ( isset($param['angle']) )    $layer->angle    = $param['angle'];
			if ( isset($param['spr_type']) ) $layer->spr_type = $param['spr_type'];
			if ( isset($param['width']) )    $layer->width    = $param['width'];
			if ( isset($param['height']) )   $layer->height   = $param['height'];
		}


		$sound = -1;
		if ( $this->version >= 2.0 )
			extract( unpack("Vsound", fread($this->fp,0x04) ) );



		$pos= array();
	
		if ( $this->version >= 2.3 )
		{
			extract( unpack("Vcount", fread($this->fp,0x04) ) );
			for ( $i=0; $i<$count; ++$i ) {
				$pos[$i] = (object) unpack( "Vunk/Vx/Vy/Vattr", fread($this->fp,16) );
			}
		}
	
		return (object) array(
			"layers"    => $layers,
			"sound"     => $sound,
			"pos"       => $pos	
		);
	}



	/// Compile a ACT file
	public function compile()
	{

		// Header
		$result  = pack('a2C2', 'AC', $this->version * 10 % 10, floor($this->version * 10)/10 );

		// Action count
		$result .= pack( 'vx10', count($this->actions) );

		// Compile each actions
		foreach( $this->actions as $action )
		{

			// Number of animations
			$result .= pack( 'V', count($action->animations) );

			// Compile animations
			foreach( $action->animations as $animation )
			{

				// 32 uknown offset + layers count
				$result .= pack( 'x32V', count($animation->layers) );

				// Compile layers
				foreach( $animation->layers as $layer )
				{
					$result .= pack('V4', $layer->pos[0], $layer->pos[1], $layer->index, $layer->is_mirror);

					if ( $this->version < 2.0 )
						continue;

					else if ( $this->version < 2.4 )
						$result .= pack(
							'C4fV2',
							$layer->color[0]*255,
							$layer->color[1]*255,
							$layer->color[2]*255,
							$layer->color[3]*255,
							$layer->scale[0],
							$layer->angle,
							$layer->spr_type
						);

					else if ( $this->version === 2.4 )
						$result .= pack(
							'C4f2V2',
							$layer->color[0]*255,
							$layer->color[1]*255,
							$layer->color[2]*255,
							$layer->color[3]*255,
							$layer->scale[0],
							$layer->scale[1],
							$layer->angle,
							$layer->spr_type
						);

					else 
						$result .= pack(
							'C4f2V4',
							$layer->color[0]*255, 
							$layer->color[1]*255,
							$layer->color[2]*255,
							$layer->color[3]*255,
							$layer->scale[0],
							$layer->scale[1],
							$layer->angle,
							$layer->spr_type,
							$layer->width,
							$layer->height
						);
				}


				// Animation sound
				if ( $this->version >= 2.0 )
				{
					$result .= pack('V', $animation->sound );
				}

				// Animation imf (head pos)
				if ( $this->version >= 2.3 )
				{
					$result .= pack('V', count($animation->pos) );
					foreach( $animation->pos as $pos )
					{
						$result .=  pack( 'V4', $pos->unk, $pos->x, $pos->y, $pos->attr );
					}
				}
			}
		}

		
		if ( $this->version >= 2.1 )
		{
			// Comple sounds
			$result .= pack( 'V', count($this->sounds) );
			foreach( $this->sounds as $sound )
			{
				$result .= pack( 'a40', $sound );
			}

			// Comple delay
			if ( $this->version >= 2.2 )
			{
				foreach( $this->actions as $action )
				{
					$result .= pack( 'f', $action->delay / 25 );
				}
			}
		}

		return $result;
	}
}

?>
Maybe should I release my character-viewer...

Have fun~

My god! Thank you for this!

 

Well, I was just trying out if I could make my own charsim, it's just in the... garbage state lol.

 

Thank you very much!! again!

How can I repay you :)))

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...