H|H ~ The Hampshire Heavies Community : Forum
http://www.thehh.eu/forum/

Anyone fancy doing a bit of C++?
http://www.thehh.eu/forum/viewtopic.php?f=30&t=8451
Page 1 of 3

Author:  frog [ Mon Apr 02, 2012 8:25 pm ]
Post subject:  Anyone fancy doing a bit of C++?

As part of the preparation for rebuilding the server on Debian, a little image conversion command-line program needs creating to convert a png to a vtf. This can be done with the use of the DevIL image libraries

Previously I was using wine to run vtfcmd, as I was unable to find a utility for Linux that could do it. However, I want to try to avoid installing wine on the new server.

Unfortunately, I don't have time to get my head around doing this, so I'm asking for help.

Here is the C source for a utility that will convert vtf to png.

Code:
//-----------------------------------------------------------------------------
//
// VTFConv - Converts VTF files to multiple PNGs
//
//-----------------------------------------------------------------------------

// Required include files.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <IL/il.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#ifdef ILU_ENABLED
#include <IL/ilu.h>
#define PRINT_ERROR_MACRO printf("Error: %s\n", iluErrorString(Error))
#else /* not ILU_ENABLED */
#define PRINT_ERROR_MACRO printf("Error: 0x%X\n", (unsigned int)Error)
#endif /* not ILU_ENABLED */


char *str_replace(char * t1, char * t2, char * t6){
   char*t4;
   char*t5=malloc(0);

   while(strstr(t6,t1)){
      t4=strstr(t6,t1);
      strncpy(t5+strlen(t5),t6,t4-t6);
      strcat(t5,t2);
      t4+=strlen(t1);
      t6=t4;
   }
   return strcat(t5,t4);
}


int main(int argc, char **argv)
{
   ILuint   ImgId;
   ILenum   Error;

   // We use the filename specified in the first argument of the command-line.
   if (argc < 2) {
      fprintf(stderr, "VTFConv - Converts VTF files to multiple PNGs\n");
      fprintf(stderr, "Usage: vtfconv <file>\n");
      return 1;
   }

   // Check if the shared lib's version matches the executable's version.
   if (ilGetInteger(IL_VERSION_NUM) < IL_VERSION) {
      printf("DevIL version is different...exiting!\n");
      return 2;
   }

   // Initialize DevIL.
   ilInit();
#ifdef ILU_ENABLED
   iluInit();
#endif

   // Generate the main image name to use.
   ilGenImages(1, &ImgId);

   // Bind this image name.
   ilBindImage(ImgId);

   // Loads the image specified by File into the image named by ImgId.
   if (!ilLoadImage(argv[1])) {
      printf("Could not open file...exiting.\n");
      return 3;
   }


   // Enable this to let us overwrite the destination file if it already exists.
   ilEnable(IL_FILE_OVERWRITE);
   ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);


   const unsigned int lNumImages = ilGetInteger(IL_NUM_IMAGES)+1;
   unsigned int derp = 0;
   char flerp[256];
   for(derp = 0; derp < lNumImages; derp ++){
      sprintf(flerp, "%s.%d.png", argv[1], derp);
      printf("%s\n", flerp);
      ilSaveImage(flerp);
      if(derp < lNumImages-1)
         if(ilActiveImage(1) == IL_FALSE) break;
   }
   printf("Saved frames: %d/%d\n", derp, lNumImages);

   // Simple Error detection loop that displays the Error to the user in a human-readable form.
   while ((Error = ilGetError())) {
      PRINT_ERROR_MACRO;}

   return 0;


Here are the docs for DevIL. http://openil.sourceforge.net/docs/index.php

This how the conversion is currently achieved at the moment.
Code:
wine vtfcmd.exe -file ".$out." -format DXT1 -flag anisotropic -flag nolod -nomipmaps


If you feel like you could have a good go at it, then please let me know. I can set you up with a shell account if required.

Author:  A Llamas Hat [ Tue Apr 03, 2012 2:22 pm ]
Post subject:  Re: Anyone fancy doing a bit of C++?

I had a look at the source and altered a few lines.
Code:
//-----------------------------------------------------------------------------
//
// VTFConv_Inv - Converts PNG files to VTF
// Based on VTFConv by Darkimmortal
//
//-----------------------------------------------------------------------------

// Required include files.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <IL/il.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


/* We would need ILU just because of iluErrorString() function... */
/* So make it possible for both with and without ILU!  */
#ifdef ILU_ENABLED
#include <IL/ilu.h>
#define PRINT_ERROR_MACRO printf("Error: %s\n", iluErrorString(Error))
#else /* not ILU_ENABLED */
#define PRINT_ERROR_MACRO printf("Error: 0x%X\n", (unsigned int)Error)
#endif /* not ILU_ENABLED */


char *str_replace(char * t1, char * t2, char * t6){
   char*t4;
   char*t5=malloc(0);

   while(strstr(t6,t1)){
      t4=strstr(t6,t1);
      strncpy(t5+strlen(t5),t6,t4-t6);
      strcat(t5,t2);
      t4+=strlen(t1);
      t6=t4;
   }
   return strcat(t5,t4);
}


int main(int argc, char **argv)
{
   ILuint   ImgId;
   ILenum   Error;

   // We use the filename specified in the first argument of the command-line.
   if (argc < 2) {
      fprintf(stderr, "VTFConv - Converts VTF files to multiple PNGs\n");
      fprintf(stderr, "Usage: vtfconv <file> [<destination>]\n");
      return 1;
   }

   // Check if the shared lib's version matches the executable's version.
   if (ilGetInteger(IL_VERSION_NUM) < IL_VERSION) {
      printf("DevIL version is different...exiting!\n");
      return 2;
   }

   // Initialize DevIL.
   ilInit();
#ifdef ILU_ENABLED
   iluInit();
#endif

   // Generate the main image name to use.
   ilGenImages(1, &ImgId);

   // Bind this image name.
   ilBindImage(ImgId);

   // Loads the image specified by File into the image named by ImgId.
   if (!ilLoadImage(argv[1])) {
      printf("Could not open file...exiting.\n");
      return 3;
   }

   // Enable this to let us overwrite the destination file if it already exists.
   ilEnable(IL_FILE_OVERWRITE);
   ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);

   if (argc > 2)
   {
      // user specified a file with 2nd argument, save to that
      printf("Saving image to user specified file \"%s\"\n",argv[2]);
      ilSaveImage(argv[2]);
      
   }
   else
   {
      // user didn't specify a destination file
      
      // allocate for the new string:
      // the old name plus 4 (.vtf) plus 1 (terminal null)
      char* newName = malloc(strlen(argv[1]+4));
      // make the new filename "<oldfilename>.vtf"
      strcpy(newName,argv[1]);
      strcat(newName,".vtf");
      
      printf("Saving image to default file %s\n",newName);
      ilSaveImage(newName);
   }

   // We're done with the image, so let's delete it.
   //ilDeleteImages(1, &ImgId);

   // Simple Error detection loop that displays the Error to the user in a human-readable form.
   while ((Error = ilGetError())) {
      PRINT_ERROR_MACRO;}

   printf("done\n");

   return 0;

}



This is without any file type or name checks but I'll add those. I don't know a lot of C so somebody could surely improve it. But it works :)

I tested it on my machine (Arch Linux 64) by converting an hhrules VTF from my TF2 folder to a PNG with the original program. Then I converted the new PNG back to VTF with my new program. The old and new VTF show the same content. I also tried a few random PNGs and the contents also matched with the resulting VTF.

The caveat is that the new VTF files are huge compared to the png files. My PNG had 425KB while the resulting VTF had about 2MB. That's not really acceptable and probably happens because I'm doing something wrong with DevIL. I'll try and leaf through the manual soon.

I'm pretty sure I could get this working with DevIL on a debian system. I'll improve the code if you want to use this.

Author:  K3D [ Tue Apr 03, 2012 2:36 pm ]
Post subject:  Re: Anyone fancy doing a bit of C++?

Is your resulting VTF producing mipmaps? Is that possibly why it is much larger?

Author:  A Llamas Hat [ Tue Apr 03, 2012 4:32 pm ]
Post subject:  Re: Anyone fancy doing a bit of C++?

Hm no I don't think that is the problem. I started sifting through the source code of DevIL, the mipmap count is hard set to "1" with a large TODO next to it.

The size of the actual image content is the same for PNG and converted VTFs (to the bit). But somehow DevIL bloats the VTF file with hell knows what.

The culprit seems to be a lack of compression. But whenever I try to enable compression DevIL dies with a cryptic error message. I'll delve a bit deeper into the source code today or tomorrow.

Author:  frog [ Tue Apr 03, 2012 11:55 pm ]
Post subject:  Re: Anyone fancy doing a bit of C++?

Thanks for your help, A Llamas Hat. :)

Just had a quick look. I've realised that the documentation on the website has been updated to the latest version available in the archive!

It seems that with DevIL that it's better to supply the image as a jpg, as I read that pngs are currently always converted to RGB(A), and DXT1, the desire compression isn't possible with that format. Although I might have misunderstood.

Looking at chapter 10 seems to be most use.

Also http://code.google.com/p/libsquish/

Author:  A Llamas Hat [ Thu Apr 05, 2012 4:20 pm ]
Post subject:  Re: Anyone fancy doing a bit of C++?

First: Since the forum is back up I assume the move was successful. Nice [beer]

Second: I'm still working on this. I'm just not making much progress. The Problem is that it's simply not working for VTF.

For example I can save DDS files in any compression and reasonable file sizes. Supposedly they are similar to VTF files with only the header info being different. But if I do the same thing for VTF the resulting files are still huge. It does not matter what the source format is. Whether it's JPG, PNG, BMP, DDS or even another VTF ... with the same content the resulting VTF always has the same (huge) size.

I'm starting to think that the save functionality for VTF is broken. I'm currently looking into writing the file myself without relying on the DevIL VTF functionality. But as I said I'm not experienced with C coding.

I'll post what I have so far. If someone could point out obvious flaws or give it a try that'd be much appreciated.

This first approach simply loads the image, sets the compression and then saves the image again. Works for JPG, PNG and DDS with reasonably good results. Has no effect on VTF unless I set IL_VTF_COMP. Then it dies with a file read error.
Code:
#include <IL/il.h>
#include <IL/ilu.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
   ILuint   ImgId;
   ILenum   Error;

   // We use the filename specified in the first argument of the command-line.
   if (argc < 2) {
      fprintf(stderr, "Usage: vtfconv <file> [<destination>]\n");
      return 1;
   }

   // Check if the shared lib's version matches the executable's version.
   if (ilGetInteger(IL_VERSION_NUM) < IL_VERSION) {
      printf("DevIL version is different...exiting!\n");
      return 2;
   }

   // Initialize DevIL.
   ilInit();
   iluInit();

   // Generate the main image name to use.
   ilGenImages(1, &ImgId);

   // Bind this image name.
   ilBindImage(ImgId);

   // Loads the image specified by File into the image named by ImgId.
   if (!ilLoadImage(argv[1])) {
      printf("Could not open file...exiting.\n");
      return 3;
   }
   
   ilEnable(IL_FILE_OVERWRITE);
   ilSetInteger(IL_KEEP_DXTC_DATA,IL_TRUE);
   //ilConvertImage(IL_BGRA,IL_UNSIGNED_SHORT);
   ilSetInteger(IL_DXTC_FORMAT, IL_DXT1);
   ilSetInteger(IL_VTF_COMP, IL_DXT1);

   if (argc > 2)
   {
      // user specified a file with 2nd argument, save to that
      printf("Saving image to user specified file \"%s\"\n",argv[2]);
      printf("return value: %d\n",ilSaveImage(argv[2]));
   }
   else
   {
      // user didn't specify a destination file
      
      // allocate for the new string:
      // the old name plus 4 (.vtf) plus 1 (terminal null)
      char* newName = malloc(strlen(argv[1]+4));
      // make the new filename "<oldfilename>.vtf"
      strcpy(newName,argv[1]);
      strcat(newName,".vtf");
      
      printf("Saving image to default file %s\n",newName);
      ilSave(IL_VTF,newName);
   }

   // We're done with the image, so let's delete it.
   //ilDeleteImages(1, &ImgId);

   // Simple Error detection loop that displays the Error to the user in a human-readable form.
   while ((Error = ilGetError()))
   {
      printf("Error: %s\n", iluErrorString(Error));
   }

   printf("done\n");

   return 0;

}


The second approach loads the image, gets the image data and compresses it. Then sets the image data and saves the image. Again this works for e.g. DDS files but has no effect on VTFs.
Code:
#include <IL/il.h>
#include <IL/ilu.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
   ILuint   ImgId;
   ILenum   Error;
   ILubyte   *compressedData;
   ILubyte * data;
   ILuint DXTCSize;
   
   // We use the filename specified in the first argument of the command-line.
   if (argc < 2) {
      fprintf(stderr, "Usage: vtfconv <file> [<destination>]\n");
      return 1;
   }

   // Check if the shared lib's version matches the executable's version.
   if (ilGetInteger(IL_VERSION_NUM) < IL_VERSION) {
      printf("DevIL version is different...exiting!\n");
      return 2;
   }

   // Initialize DevIL.
   ilInit();
   iluInit();

   // Generate the main image name to use.
   ilGenImages(1, &ImgId);

   // Bind this image name.
   ilBindImage(ImgId);

   // Loads the image specified by File into the image named by ImgId.
   if (!ilLoadImage(argv[1])) {
      printf("Could not open file...exiting.\n");
      return 3;
   }
   
   ilEnable(IL_FILE_OVERWRITE);
   
   int memory_needed = ilGetInteger(IL_IMAGE_WIDTH) *ilGetInteger(IL_IMAGE_HEIGHT) * 3 * sizeof(unsigned char);
   data = (ILubyte *)malloc(memory_needed);
   ilCopyPixels(0, 0, 0, ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT), 1, IL_RGB, IL_UNSIGNED_BYTE, data);
   compressedData = ilCompressDXT(data,ilGetInteger(IL_IMAGE_WIDTH),ilGetInteger(IL_IMAGE_HEIGHT),1,IL_DXT1,&DXTCSize);
   printf("compressed data size: %d\n",DXTCSize);
   ilSetPixels(0, 0, 0, ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT), 1, IL_RGB, IL_UNSIGNED_BYTE, compressedData);

   if (argc > 2)
   {
      // user specified a file with 2nd argument, save to that
      printf("Saving image to user specified file \"%s\"\n",argv[2]);
      printf("return value: %d\n",ilSaveImage(argv[2]));
   }
   else
   {
      // user didn't specify a destination file
      
      // allocate for the new string:
      // the old name plus 4 (.vtf) plus 1 (terminal null)
      char* newName = malloc(strlen(argv[1]+4));
      // make the new filename "<oldfilename>.vtf"
      strcpy(newName,argv[1]);
      strcat(newName,".vtf");
      
      printf("Saving image to default file %s\n",newName);
      ilSave(IL_VTF,newName);
   }

   // We're done with the image, so let's delete it.
   //ilDeleteImages(1, &ImgId);

   // Simple Error detection loop that displays the Error to the user in a human-readable form.
   while ((Error = ilGetError()))
   {
      printf("Error: %s\n", iluErrorString(Error));
   }

   ilDeleteImages(1, & ImgId);
   free(data); data = NULL;

   printf("done\n");

   return 0;

}


I'll keep working on this but it may take some time. If anyone wants to take this code and carry on be my guest :)

EDIT
Fixed an error in the second bit of code.

Author:  Snatchers [ Thu Apr 05, 2012 5:33 pm ]
Post subject:  Re: Anyone fancy doing a bit of C++?

I will try and have a look at it when I get home tonight.

Author:  A Llamas Hat [ Sat Apr 07, 2012 2:02 am ]
Post subject:  Re: Anyone fancy doing a bit of C++?

Update on this:
I got a solution that works on my box. But it's ugly. Like "they'd kick me out of uni if my real name ever came up in conjunction with this code" kind of bad.

As I said the VTF write function dies. But it manages to write the header info correctly. So what I ended up doing was appending the image data to that with fwrite. The resulting files have acceptable file sizes.

I tested the results with Irfan View + VTF Plugin and GIMP + VTF Plugin. Both applications can open the resulting VTF files and display them correctly. Since the GIMP plugin utilizes the supposedly largely popular VTFLib also used by VTFEdit im fairly certain the images are usable.

I attach to this post two files that I used for testing purposes (among others). hh.png is the source image and test.vtf is the result. If anyone that has experience with VTF could check out the result that'd be great. Seems like I can't upload VTF files.

I'll have to clean up the code a bit. If nothing goes horribly wrong I should be able to post it tomorrow ... well today, by now.

Author:  frog [ Sat Apr 07, 2012 8:11 am ]
Post subject:  Re: Anyone fancy doing a bit of C++?

Big thanks for the time you've spent on this Llama. I really appreciate it. In this case, if it gets the job done, then elegance doesn't matter.

VTF files are not in the whitelist for post attachments, but ZIPs are.

Author:  A Llamas Hat [ Sat Apr 07, 2012 2:44 pm ]
Post subject:  Re: Anyone fancy doing a bit of C++?

I'm done with the code (I think) :)

The attached archive vtfcreate.zip contains the source file along with a precompiled binary. In theory if the server uses the same DevIL version (1.7.8) the binary should work out of the box methinks. But a safer bet would be to compile it on the server I guess.
Code:
gcc vtfcreate.c -lIL -o ./<name for the binary>
is all you need once DevIL is set up.

Usage is:
Code:
$ ./vtfcreate <input file> <output file> [<compression>]
The optional compression arguments takes "DXT1", "DXT3" and "DXT5" as values. DXT1 is default if nothing is specified. I saw little to no difference between DXT3 and DXT5 with the attached sample. Input format doesn't seem to matter much either.

If you want any help setting it up or need additional changes let me know!

Here's the source for those interested but too lazy to download:
Spoiler Alert:




frog wrote:
VTF files are not in the whitelist for post attachments, but ZIPs are.

Right, I've attached a zip with sample files. hh.png is the source and as the names imply hh_DXT1.vtf and hh_DXT3.vtf are the DXT1 and DXT3 compressed images, respectively. Tested with IrfanView and GIMP.

frog wrote:
Big thanks for the time you've spent on this Llama.
You're welcome. I've wanted to try C for some time anyways; this is as good an opportunity as any :geek:

Attachments:
File comment: source code
vtfcreate.zip [5.96 KiB]
Downloaded 85 times
File comment: Examplary converted images
examples.zip [693.51 KiB]
Downloaded 82 times

Page 1 of 3 All times are UTC [ DST ]
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/