RunUO Community

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

Grr. Problems trying to properly read Art.mul

Tissemand

Squire
I've been converting some snippets from C# (UltimaSDK) to C and using 32-bit bitmaps (RGBA8888) instead of UltimaSDK's 16-bit one's (ARGB1555)...

I'm currently stuck trying to convert this section:
Code:
        private static unsafe Bitmap LoadStatic( Stream stream )
        {
            BinaryReader bin = new BinaryReader( stream );

            bin.ReadInt32();
            int width = bin.ReadInt16();
            int height = bin.ReadInt16();

            if ( width <= 0 || height <= 0 )
                return null;

            int[] lookups = new int[height];

            int start = (int)bin.BaseStream.Position + (height * 2);

            for ( int i = 0; i < height; ++i )
                lookups[i] = (int)(start + (bin.ReadUInt16() * 2));

            Bitmap bmp = new Bitmap( width, height, PixelFormat.Format16bppArgb1555 );
            BitmapData bd = bmp.LockBits( new Rectangle( 0, 0, width, height ), ImageLockMode.WriteOnly, PixelFormat.Format16bppArgb1555 );

            ushort *line = (ushort *)bd.Scan0;
            int delta = bd.Stride >> 1;

            for ( int y = 0; y < height; ++y, line += delta )
            {
                bin.BaseStream.Seek( lookups[y], SeekOrigin.Begin );

                ushort *cur = line;
                ushort *end;

                int xOffset, xRun;

                while ( ((xOffset = bin.ReadUInt16()) + (xRun = bin.ReadUInt16())) != 0 )
                {
                    cur += xOffset;
                    end = cur + xRun;

                    while ( cur < end )
                        *cur++ = (ushort)(bin.ReadUInt16() ^ 0x8000);
                }
            }

            bmp.UnlockBits( bd );

            return bmp;
        }

This is what I currently have:
Code:
GRRLIB_texImg *get_image( unsigned int object_id, enum art_type type )
{
  fseek( art, art_position( object_id, type ), SEEK_SET );

  unsigned short width, height;
  read_int( art );
  width  = read_short( art );
  height = read_short( art );

  printf( "w=%d h=%d", width, height );

  if ( width <= 0 || height <= 0 )
    return NULL;

  int lookups[height];
  int i, start = ftell(art) + height * 2;
  for( i = 0; i < height; i++ )
    lookups[i] = (int)(start + (unsigned short)(read_short( art ) * 2 ));

  GRRLIB_texImg *image = GRRLIB_CreateEmptyTexture( width, height );

  int y;

  for ( y = 0; y < height; ++y )
  {
    fseek( art, lookups[y], SEEK_SET );
    unsigned short xOffset, xRun;
    while ( ((xOffset = read_short(art)) + (xRun = read_short(art))) != 0 )
    {
      printf( "%x", read_short(art) ); // I have no idea what to do here or anywhere around here.
    }

  }
  return image;
}
I just don't know where each of the read pixels go, or how to get there, so any pointers would be great.

read_short(FILE *file) and read_int(FILE *file) read 2 and 4 bytes respectively (duh), and I also have a function called ARGB1555toRGBA8888(unsigned short color)...

Any ideas?
 

Zippy

Razor Creator
Too lazy to figure out your code and you will probably never see this anyways. But here is some code that definitely does work, you should be able to adapt it:

[syntax]

#define Color16to32(c16) (((c16) & 0x7C00) >> 7) | (((c16) & 0x3E0) << 6) | (((c16) & 0x1F) << 19)

UOItem *ReadUOItem( int item, int bh )
{
if ( item == 0 || item >= 0xFFFF || !pShared )
return NULL;

char str[512];
short *Lookup;
unsigned short *Run;

FILE *idxMul, *artMul;
ArtIdx idx;
ArtHeader header;
memset( &header, 0, sizeof(ArtHeader) );

WaitForSingleObject( CommMutex, INFINITE );
sprintf( str, "%s/artidx.mul", pShared->DataPath );
ReleaseMutex( CommMutex );

idxMul = fopen( str, "rb" );
if ( !idxMul )
return NULL;
fseek( idxMul, item*sizeof(ArtIdx), SEEK_SET );
fread( &idx, sizeof(ArtIdx), 1, idxMul );
fclose( idxMul );
if ( idx.FilePos == -1 || idx.Length == -1 )
return NULL;

WaitForSingleObject( CommMutex, INFINITE );
sprintf( str, "%s/art.mul", pShared->DataPath );
ReleaseMutex( CommMutex );

artMul = fopen( str, "rb" );
if ( !artMul )
{
fclose( idxMul );
return NULL;
}
fseek( artMul, idx.FilePos, SEEK_SET );
fread( &header, sizeof(ArtHeader), 1, artMul );
if ( header.Height <= 0 || header.Width <= 0 || header.Height >= 1024 || header.Width >= 1024 || header.Unknown > 0xFFFF || header.Unknown == 0 )
{
fclose( artMul );
return NULL;
}

Run = new unsigned short[header.Width]; // it should never be wider than the whole image!
Lookup = new short[header.Height];
fread( Lookup, header.Height * 2, 1, artMul );
long dataStart = ftell( artMul );

UOItem *pNew = new UOItem;
pNew->ItemID = item;
pNew->pNext = ArtCache;
ArtCache = pNew;

unsigned short **Image = new unsigned short*[header.Width];
for(int i=0;i<header.Width;i++)
{
Image = new unsigned short[header.Height];
memset( Image, 0, header.Height*2 );
}

pNew->Left = pNew->Top = 0x7FFFFFFF;
pNew->Right = pNew->Bottom = 0;
for (int y=0;y<header.Height;y++)
{
int x = 0;

fseek( artMul, dataStart + Lookup[y] * 2, SEEK_SET );
do {
short RunOffset = 0, RunLength = 0;

fread( &RunOffset, 2, 1, artMul );
fread( &RunLength, 2, 1, artMul );

if ( RunLength <= 0 || RunOffset < 0 || RunOffset + RunLength >= 2048 || RunLength > header.Width )
break;

if ( y > pNew->Bottom )
pNew->Bottom = y;
if ( y < pNew->Top )
pNew->Top = y;

x += RunOffset;
if ( x < pNew->Left )
pNew->Left = x;

fread( Run, RunLength*2, 1, artMul );
for (int o=0;o<RunLength;o++,x++)
Image[x][y] = Run[o];

if ( x > pNew->Right )
pNew->Right = x;
} while ( true );
}
fclose( artMul );

delete[] Run;
delete[] Lookup;

float scale = float(bh) / float(pNew->GetHeight());
if ( scale > 1 || scale <= 0 )
scale = 1;

pNew->RealHeight = (int)(header.Height * scale + 1);
pNew->RealWidth = (int)(header.Width * scale + 1);
pNew->Data = new unsigned short *[pNew->RealWidth];
for(int x=0;x<pNew->RealWidth;x++)
{
pNew->Data[x] = new unsigned short[pNew->RealHeight];
memset( pNew->Data[x], 0, 2*pNew->RealHeight );
}

for(int x=0;x<header.Width;x++)
{
for(int y=0;y<header.Height;y++)
pNew->Data[(int)(x * scale)][(int)(y * scale)] |= Image[x][y];
}

pNew->Top = (int)(pNew->Top * scale);
pNew->Left = (int)(pNew->Left * scale);
pNew->Bottom = (int)(pNew->Bottom * scale);
pNew->Right = (int)(pNew->Right * scale);

for(int x=0;x<header.Width;x++)
delete[] Image[x];
delete[] Image;

return pNew;
}

int DrawUOItem( HDC hDC, RECT rect, int item, int hueIdx )
{
item |= 0x4000;

rect.top ++;
rect.bottom --;
int maxHeight = rect.bottom - rect.top;

UOItem *i = FindItem( item );
if ( i == NULL )
i = ReadUOItem( item, maxHeight );

if ( i == NULL )
return 0;

if ( i->GetHeight() < maxHeight )
rect.top += ( maxHeight - i->GetHeight() ) / 2;

unsigned short *hue = GetHue( hueIdx );
for(int x=i->Left;x<=i->Right;x++)
{
for (int y=i->Top;y<=i->Bottom;y++)
{
if ( i->Data[x][y] != 0 )
SetPixel( hDC, rect.left + x - i->Left, rect.top + y - i->Top, Brightness( 0x30, Color16to32( ApplyHueToPixel( hue, i->Data[x][y] ) ) ) );
}
}

return i->GetWidth() + 3;
}

[/syntax]
 

Tissemand

Squire
Too lazy to figure out your code and you will probably never see this anyways. But here is some code that definitely does work, you should be able to adapt it:

[syntax]

#define Color16to32(c16) (((c16) & 0x7C00) >> 7) | (((c16) & 0x3E0) << 6) | (((c16) & 0x1F) << 19)

UOItem *ReadUOItem( int item, int bh )
{
if ( item == 0 || item >= 0xFFFF || !pShared )
return NULL;

char str[512];
short *Lookup;
unsigned short *Run;

FILE *idxMul, *artMul;
ArtIdx idx;
ArtHeader header;
memset( &header, 0, sizeof(ArtHeader) );

WaitForSingleObject( CommMutex, INFINITE );
sprintf( str, "%s/artidx.mul", pShared->DataPath );
ReleaseMutex( CommMutex );

idxMul = fopen( str, "rb" );
if ( !idxMul )
return NULL;
fseek( idxMul, item*sizeof(ArtIdx), SEEK_SET );
fread( &idx, sizeof(ArtIdx), 1, idxMul );
fclose( idxMul );
if ( idx.FilePos == -1 || idx.Length == -1 )
return NULL;

WaitForSingleObject( CommMutex, INFINITE );
sprintf( str, "%s/art.mul", pShared->DataPath );
ReleaseMutex( CommMutex );

artMul = fopen( str, "rb" );
if ( !artMul )
{
fclose( idxMul );
return NULL;
}
fseek( artMul, idx.FilePos, SEEK_SET );
fread( &header, sizeof(ArtHeader), 1, artMul );
if ( header.Height <= 0 || header.Width <= 0 || header.Height >= 1024 || header.Width >= 1024 || header.Unknown > 0xFFFF || header.Unknown == 0 )
{
fclose( artMul );
return NULL;
}

Run = new unsigned short[header.Width]; // it should never be wider than the whole image!
Lookup = new short[header.Height];
fread( Lookup, header.Height * 2, 1, artMul );
long dataStart = ftell( artMul );

UOItem *pNew = new UOItem;
pNew->ItemID = item;
pNew->pNext = ArtCache;
ArtCache = pNew;

unsigned short **Image = new unsigned short*[header.Width];
for(int i=0;i<header.Width;i++)
{
Image = new unsigned short[header.Height];
memset( Image, 0, header.Height*2 );
}

pNew->Left = pNew->Top = 0x7FFFFFFF;
pNew->Right = pNew->Bottom = 0;
for (int y=0;y<header.Height;y++)
{
int x = 0;

fseek( artMul, dataStart + Lookup[y] * 2, SEEK_SET );
do {
short RunOffset = 0, RunLength = 0;

fread( &RunOffset, 2, 1, artMul );
fread( &RunLength, 2, 1, artMul );

if ( RunLength <= 0 || RunOffset < 0 || RunOffset + RunLength >= 2048 || RunLength > header.Width )
break;

if ( y > pNew->Bottom )
pNew->Bottom = y;
if ( y < pNew->Top )
pNew->Top = y;

x += RunOffset;
if ( x < pNew->Left )
pNew->Left = x;

fread( Run, RunLength*2, 1, artMul );
for (int o=0;o<RunLength;o++,x++)
Image[x][y] = Run[o];

if ( x > pNew->Right )
pNew->Right = x;
} while ( true );
}
fclose( artMul );

delete[] Run;
delete[] Lookup;

float scale = float(bh) / float(pNew->GetHeight());
if ( scale > 1 || scale <= 0 )
scale = 1;

pNew->RealHeight = (int)(header.Height * scale + 1);
pNew->RealWidth = (int)(header.Width * scale + 1);
pNew->Data = new unsigned short *[pNew->RealWidth];
for(int x=0;x<pNew->RealWidth;x++)
{
pNew->Data[x] = new unsigned short[pNew->RealHeight];
memset( pNew->Data[x], 0, 2*pNew->RealHeight );
}

for(int x=0;x<header.Width;x++)
{
for(int y=0;y<header.Height;y++)
pNew->Data[(int)(x * scale)][(int)(y * scale)] |= Image[x][y];
}

pNew->Top = (int)(pNew->Top * scale);
pNew->Left = (int)(pNew->Left * scale);
pNew->Bottom = (int)(pNew->Bottom * scale);
pNew->Right = (int)(pNew->Right * scale);

for(int x=0;x<header.Width;x++)
delete[] Image[x];
delete[] Image;

return pNew;
}

int DrawUOItem( HDC hDC, RECT rect, int item, int hueIdx )
{
item |= 0x4000;

rect.top ++;
rect.bottom --;
int maxHeight = rect.bottom - rect.top;

UOItem *i = FindItem( item );
if ( i == NULL )
i = ReadUOItem( item, maxHeight );

if ( i == NULL )
return 0;

if ( i->GetHeight() < maxHeight )
rect.top += ( maxHeight - i->GetHeight() ) / 2;

unsigned short *hue = GetHue( hueIdx );
for(int x=i->Left;x<=i->Right;x++)
{
for (int y=i->Top;y<=i->Bottom;y++)
{
if ( i->Data[x][y] != 0 )
SetPixel( hDC, rect.left + x - i->Left, rect.top + y - i->Top, Brightness( 0x30, Color16to32( ApplyHueToPixel( hue, i->Data[x][y] ) ) ) );
}
}

return i->GetWidth() + 3;
}

[/syntax]
Thanks! What's that snippet from?
For about a few weeks or so, I was tempted to try making a UO client for Wii homebrew (especially with the work being done on UltimaXNA), but I wasn't too familiar with the libraries that were available (and I was also busy spending time with my partner) and ended up giving up.

Might look into it another day though; very tempted to now, after spending a bit more time on C++ and have a better understanding of pointers and better practices. :)
 

Zippy

Razor Creator
That code is from what Razor uses to draw item icons on the UO titlebar for counters.
 

Tissemand

Squire
Sorry to bump this, but I started working on it again. I got confused by some of your code and I had to infer some of your structs and stuff by looking at ultimasdk's source.

I currently have this and I'm completely stuck because something's wrong and I don't see where. I can't really use a debugger now and at most I can only use printfs.

art.cpp:

[syntax]#include "art.h"

FILE *artIdx, *artMul;
Entry3D **entries;

Entry3D::Entry3D( uint32_t loo, uint32_t len, uint32_t ext )
: lookup(loo), length(len), extra(ext)
{
}

Entry3D::~Entry3D()
{
}

Item::Item( uint32_t itemid )
: id( itemid )
{
this->type = Static;
this->texImg = NULL;
}

Item::~Item()
{
delete position;
delete texImg;
}

uint32_t Item::getPosition()
{
return position ? (*position) : ((*position) = ((this->type == Land) ? entries[this->id & 0x3FFF] : entries[(this->id + 0x4000) & 0xFFFF])->lookup);
}

uint16_t **Item::getSImage()
{
if ( !ArtAvailable() || !this->valid() )
return NULL;

//uint16_t width, height;
uint16_t *run;
int16_t *lookup;
uint32_t dataStart;
uint16_t **image;


fseek( artMul, this->getPosition() + 4 /* skip 4 bytes for the first junk [?] int */, SEEK_SET );
width = (uint16_t)read_short( artMul );
height = (uint16_t)read_short( artMul );

//fread( &width, 2, 1, artMul );
//fread( &height, 2, 1, artMul );

if ( height <= 0 || width <= 0 || height >= 1024 || width >= 1024 )
return NULL;

run = new uint16_t[ width ];
lookup = new int16_t[ height ];

//fread( lookup, height * 2, 1, artMul );
for( int i = 0; i < height; i++ )
lookup = read_short( artMul );

dataStart = ftell( artMul );

image = new uint16_t*[ width ];
for( int i = 0; i < width; i++ )
{
image = new uint16_t[ height ];
for( int j = 0; j < height; j++ )
{
image[j] = new uint16_t; // maybe to = 0 if it doesn't work
image[j] = 0;
}
}

left = top = 0x7FFFFFFF;
right = bottom = 0;

for( int y = 0; y < height; y++ )
{
int x = 0;
fseek( artMul, dataStart + lookup[y] * 2, SEEK_SET );

do
{
int16_t rOffset = 0, rLength = 0;
rOffset = read_short( artMul );
rLength = read_short( artMul );
//fread( &rOffset, 2, 1, artMul );
//fread( &rLength, 2, 1, artMul );

if ( rLength <= 0 || rOffset < 0 || (rOffset + rLength) >= 2048 || rLength > width )
break;
if ( y > bottom )
bottom = y;
if ( y < top )
top = y;

x += rOffset;
if ( x < left )
left = x;
for( int i = 0; i < rLength; i++ )
run = read_short( artMul );
//fread( run, rLength * 2, 1, artMul );
for( int i = 0; i < rLength; i++, x++ )
image[x][y] = run;
if ( x > right )
right = x;
}
while( true );
}

delete[] run;
delete[] lookup;

return image;
}

GRRLIB_texImg *Item::getTexImg()
{
if ( this->texImg )
return this->texImg;
uint16_t **image = this->getSImage();
texImg = GRRLIB_CreateEmptyTexture( width, height );

for( int x = this->left; x <= this->right; x++ )
{
for( int y = this->top; y <= this->bottom; y++ )
{
if ( image[x][y] )
GRRLIB_SetPixelTotexImg( x, y, texImg, c16to32( image[x][y] ) );
}
if ( x != this->left )
delete[] image[x-1]; // uhh, I think this should work?
}
delete[] image;
return texImg;
}

bool Item::valid()
{
return this->id > 0 && this->id < 0xFFFF;
}

void ArtInitilize()
{
printf( "Initilizing Art\n" );
artIdx = fopen( "sd:/artidx.mul", "rb" ),
artMul = fopen( "sd:/art.mul", "rb" );
printf( "ArtIdx: %i, ArtMul: %i\n", artIdx != NULL, artMul != NULL);

if ( artIdx == NULL || artMul == NULL )
return;

uint32_t count, length, i, lookup;

fseek( artIdx, 0, SEEK_END );
count = (ftell(artIdx) / 12), length = 0x10000;
(entries) = new Entry3D*[length];
rewind( artIdx );

for( i = 0; i < count && i < length; i++ ) // read 12B per iter
{
entries = new Entry3D();
fread( &lookup, 4, 1, artIdx );
entries->lookup = htonl(lookup);
fread( &(entries->length), 4, 1, artIdx );
fread( &(entries->extra), 4, 1, artIdx );
}

for( i = count; i < length; i++ ) // set remainder
{
entries = new Entry3D();
}
printf( "Done\n" );
}
uint32_t c16to32(const uint16_t &c)
{
const uint32_t a = c&0x8000, r = c&0x7C00, g = c&0x03E0, b = c&0x1F;
const uint32_t rgb = (r << 9) | (g << 6) | (b << 3);
uint32_t color = (a*0x1FE00) | rgb | ((rgb >> 5) & 0x070707);
return (color << 8) | 0xFF;
}
bool ArtAvailable()
{
return artIdx != NULL && artMul != NULL && entries != NULL;
}

int16_t read_short( FILE *file )
{
int16_t s;
fread( &s, 2, 1, file );
return htons(s); // endian issues :|
}[/syntax]

art.h:

[syntax]#ifndef ART_H
#define ART_H

#include <fstream>
#include <stdint.h>
#include <grrlib.h>

#define htons(s) ((s & 0xFF) << 8) | ((s & 0xFF00) >> 8);
#define htonl(s) ((s & 0xFF) << 24) | ((s & 0xFF00) << 8) | ((s & 0xFF0000) >> 8) | ((s & 0xFF000000) >> 24)
#define Color16to32(c16) (((c16) & 0x7C00) >> 7) | (((c16) & 0x3E0) << 6) | (((c16) & 0x1F) << 19)

uint32_t c16to32(const uint16_t &);
enum ItemType
{
Static,
Land
};

class Item
{
public:
Item( uint32_t );
~Item();
uint32_t getPosition();
uint16_t **getSImage();
GRRLIB_texImg *getTexImg();
bool valid();
uint32_t id;
ItemType type;
uint16_t width, height;
int32_t left, top, right, bottom;

private:
uint32_t *position;
GRRLIB_texImg *texImg;
//uint16_t **image;
};

class Entry3D
{
public:
Entry3D( uint32_t = -1, uint32_t = -1, uint32_t = -1 );
~Entry3D();
uint32_t lookup, length, extra;
};

void ArtInitilize();
bool ArtAvailable();
int16_t read_short( FILE * );

#endif[/syntax]

main.cpp:
[syntax]#include "main.h"

GXRModeObj *rmode;

void Initilize()
{
printf( "Wii Initilizing..." );
GRRLIB_Init();
WPAD_Init();

GRRLIB_Settings.antialias = false;
GRRLIB_SetBackgroundColour( 0xFF, 0xFF, 0xFF, 0xFF );
printf( "Wii Initilizing Done\n" );
for( int i = 0; i < 10; i++ )
printf( "\n" );
}

int main()
{
Initilize();
ArtInitilize();

Item* item = new Item( 3 );
//printf( "Image OK? %i", item->getSImage() != NULL );
GRRLIB_texImg *img = item->getTexImg();

while(1)
{
WPAD_ScanPads();

GRRLIB_DrawImg( 100, 100, img, 0, 1, 1, 0xFFFFFFFF );
GRRLIB_Rectangle( 100, 100, item->width, item->height, 0xFF0000FF, 0 );
GRRLIB_Render();

if(WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) return(0);
}


GRRLIB_Exit();
return(0);
}[/syntax]

Sorry the code's pretty sloppy, but I'll fix it up later :) I should also probably using a bit more references but I can fix that later.

My current output is this:


Frankly, I don't think that's anything like itemid 3 (which I think is some land tile).

ps: not all c++ libs will work with this (which is why i'm sorta using c libs).
 
Top