flasm-1.62/0000755000175000017500000000000010643715005011201 5ustar pabspabsflasm-1.62/memwatch.c0000666000175000017500000022430607734364137013202 0ustar pabspabs/* ** MEMWATCH.C ** Nonintrusive ANSI C memory leak / overwrite detection ** Copyright (C) 1992-2003 Johan Lindh ** All rights reserved. ** Version 2.71 This file is part of MEMWATCH. MEMWATCH is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. MEMWATCH is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with MEMWATCH; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** ** 920810 JLI [1.00] ** 920830 JLI [1.10 double-free detection] ** 920912 JLI [1.15 mwPuts, mwGrab/Drop, mwLimit] ** 921022 JLI [1.20 ASSERT and VERIFY] ** 921105 JLI [1.30 C++ support and TRACE] ** 921116 JLI [1.40 mwSetOutFunc] ** 930215 JLI [1.50 modified ASSERT/VERIFY] ** 930327 JLI [1.51 better auto-init & PC-lint support] ** 930506 JLI [1.55 MemWatch class, improved C++ support] ** 930507 JLI [1.60 mwTest & CHECK()] ** 930809 JLI [1.65 Abort/Retry/Ignore] ** 930820 JLI [1.70 data dump when unfreed] ** 931016 JLI [1.72 modified C++ new/delete handling] ** 931108 JLI [1.77 mwSetAssertAction() & some small changes] ** 940110 JLI [1.80 no-mans-land alloc/checking] ** 940328 JLI [2.00 version 2.0 rewrite] ** Improved NML (no-mans-land) support. ** Improved performance (especially for free()ing!). ** Support for 'read-only' buffers (checksums) ** ^^ NOTE: I never did this... maybe I should? ** FBI (free'd block info) tagged before freed blocks ** Exporting of the mwCounter variable ** mwBreakOut() localizes debugger support ** Allocation statistics (global, per-module, per-line) ** Self-repair ability with relinking ** 950913 JLI [2.10 improved garbage handling] ** 951201 JLI [2.11 improved auto-free in emergencies] ** 960125 JLI [X.01 implemented auto-checking using mwAutoCheck()] ** 960514 JLI [2.12 undefining of existing macros] ** 960515 JLI [2.13 possibility to use default new() & delete()] ** 960516 JLI [2.20 suppression of file flushing on unfreed msgs] ** 960516 JLI [2.21 better support for using MEMWATCH with DLL's] ** 960710 JLI [X.02 multiple logs and mwFlushNow()] ** 960801 JLI [2.22 merged X.01 version with current] ** 960805 JLI [2.30 mwIsXXXXAddr() to avoid unneeded GP's] ** 960805 JLI [2.31 merged X.02 version with current] ** 961002 JLI [2.32 support for realloc() + fixed STDERR bug] ** 961222 JLI [2.40 added mwMark() & mwUnmark()] ** 970101 JLI [2.41 added over/underflow checking after failed ASSERT/VERIFY] ** 970113 JLI [2.42 added support for PC-Lint 7.00g] ** 970207 JLI [2.43 added support for strdup()] ** 970209 JLI [2.44 changed default filename to lowercase] ** 970405 JLI [2.45 fixed bug related with atexit() and some C++ compilers] ** 970723 JLI [2.46 added MW_ARI_NULLREAD flag] ** 970813 JLI [2.47 stabilized marker handling] ** 980317 JLI [2.48 ripped out C++ support; wasn't working good anyway] ** 980318 JLI [2.50 improved self-repair facilities & SIGSEGV support] ** 980417 JLI [2.51 more checks for invalid addresses] ** 980512 JLI [2.52 moved MW_ARI_NULLREAD to occur before aborting] ** 990112 JLI [2.53 added check for empty heap to mwIsOwned] ** 990217 JLI [2.55 improved the emergency repairs diagnostics and NML] ** 990224 JLI [2.56 changed ordering of members in structures] ** 990303 JLI [2.57 first maybe-fixit-for-hpux test] ** 990516 JLI [2.58 added 'static' to the definition of mwAutoInit] ** 990517 JLI [2.59 fixed some high-sensitivity warnings] ** 990610 JLI [2.60 fixed some more high-sensitivity warnings] ** 990715 JLI [2.61 changed TRACE/ASSERT/VERIFY macro names] ** 991001 JLI [2.62 added CHECK_BUFFER() and mwTestBuffer()] ** 991007 JLI [2.63 first shot at a 64-bit compatible version] ** 991009 JLI [2.64 undef's strdup() if defined, mwStrdup made const] ** 000704 JLI [2.65 added some more detection for 64-bits] ** 010502 JLI [2.66 incorporated some user fixes] ** [mwRelink() could print out garbage pointer (thanks mac@phobos.ca)] ** [added array destructor for C++ (thanks rdasilva@connecttel.com)] ** [added mutex support (thanks rdasilva@connecttel.com)] ** 010531 JLI [2.67 fix: mwMutexXXX() was declared even if MW_HAVE_MUTEX was not defined] ** 010619 JLI [2.68 fix: mwRealloc() could leave the mutex locked] ** 020918 JLI [2.69 changed to GPL, added C++ array allocation by Howard Cohen] ** 030212 JLI [2.70 mwMalloc() bug for very large allocations (4GB on 32bits)] ** 030520 JLI [2.71 added ULONG_LONG_MAX as a 64-bit detector (thanks Sami Salonen)] */ #define __MEMWATCH_C 1 #ifdef MW_NOCPP #define MEMWATCH_NOCPP #endif #ifdef MW_STDIO #define MEMWATCH_STDIO #endif /*********************************************************************** ** Include files ***********************************************************************/ #include #include #include #include #include #include #include #include #include "memwatch.h" #ifndef toupper #include #endif #if defined(WIN32) || defined(__WIN32__) #define MW_HAVE_MUTEX 1 #include #endif #if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H) #define MW_HAVE_MUTEX 1 #include #endif /*********************************************************************** ** Defines & other weird stuff ***********************************************************************/ /*lint -save -e767 */ #define VERSION "2.71" /* the current version number */ #define CHKVAL(mw) (0xFE0180L^(long)mw->count^(long)mw->size^(long)mw->line) #define FLUSH() mwFlush() #define TESTS(f,l) if(mwTestAlways) (void)mwTestNow(f,l,1) #define PRECHK 0x01234567L #define POSTCHK 0x76543210L #define mwBUFFER_TO_MW(p) ( (mwData*) (void*) ( ((char*)p)-mwDataSize-mwOverflowZoneSize ) ) /*lint -restore */ #define MW_NML 0x0001 #ifdef _MSC_VER #define COMMIT "c" /* Microsoft C requires the 'c' to perform as desired */ #else #define COMMIT "" /* Normal ANSI */ #endif /* _MSC_VER */ #ifdef __cplusplus #define CPPTEXT "++" #else #define CPPTEXT "" #endif /* __cplusplus */ #ifdef MEMWATCH_STDIO #define mwSTDERR stderr #else #define mwSTDERR mwLog #endif #ifdef MW_HAVE_MUTEX #define MW_MUTEX_INIT() mwMutexInit() #define MW_MUTEX_TERM() mwMutexTerm() #define MW_MUTEX_LOCK() mwMutexLock() #define MW_MUTEX_UNLOCK() mwMutexUnlock() #else #define MW_MUTEX_INIT() #define MW_MUTEX_TERM() #define MW_MUTEX_LOCK() #define MW_MUTEX_UNLOCK() #endif /*********************************************************************** ** If you really, really know what you're doing, ** you can predefine these things yourself. ***********************************************************************/ #ifndef mwBYTE_DEFINED # if CHAR_BIT != 8 # error need CHAR_BIT to be 8! # else typedef unsigned char mwBYTE; # define mwBYTE_DEFINED 1 # endif #endif #if defined(ULONGLONG_MAX) || defined(ULLONG_MAX) || defined(_UI64_MAX) || defined(ULONG_LONG_MAX) # define mw64BIT 1 # define mwROUNDALLOC_DEFAULT 8 #else # if UINT_MAX <= 0xFFFFUL # define mw16BIT 1 # define mwROUNDALLOC_DEFAULT 2 # else # if ULONG_MAX > 0xFFFFFFFFUL # define mw64BIT 1 # define mwROUNDALLOC_DEFAULT 8 # else # define mw32BIT 1 # define mwROUNDALLOC_DEFAULT 4 # endif # endif #endif /* mwROUNDALLOC is the number of bytes to */ /* round up to, to ensure that the end of */ /* the buffer is suitable for storage of */ /* any kind of object */ #ifndef mwROUNDALLOC # define mwROUNDALLOC mwROUNDALLOC_DEFAULT #endif #ifndef mwDWORD_DEFINED #if ULONG_MAX == 0xFFFFFFFFUL typedef unsigned long mwDWORD; #define mwDWORD_DEFINED "unsigned long" #endif #endif #ifndef mwDWORD_DEFINED #if UINT_MAX == 0xFFFFFFFFUL typedef unsigned int mwDWORD; #define mwDWORD_DEFINED "unsigned int" #endif #endif #ifndef mwDWORD_DEFINED #if USHRT_MAX == 0xFFFFFFFFUL typedef unsigned short mwDWORD; #define mwDWORD_DEFINED "unsigned short" #endif #endif #ifndef mwBYTE_DEFINED #error "can't find out the correct type for a 8 bit scalar" #endif #ifndef mwDWORD_DEFINED #error "can't find out the correct type for a 32 bit scalar" #endif /*********************************************************************** ** Typedefs & structures ***********************************************************************/ /* main data holding area, precedes actual allocation */ typedef struct mwData_ mwData; struct mwData_ { mwData* prev; /* previous allocation in chain */ mwData* next; /* next allocation in chain */ const char* file; /* file name where allocated */ long count; /* action count */ long check; /* integrity check value */ #if 0 long crc; /* data crc value */ #endif size_t size; /* size of allocation */ int line; /* line number where allocated */ unsigned flag; /* flag word */ }; /* statistics structure */ typedef struct mwStat_ mwStat; struct mwStat_ { mwStat* next; /* next statistic buffer */ const char* file; long total; /* total bytes allocated */ long num; /* total number of allocations */ long max; /* max allocated at one time */ long curr; /* current allocations */ int line; }; /* grabbing structure, 1K in size */ typedef struct mwGrabData_ mwGrabData; struct mwGrabData_ { mwGrabData* next; int type; char blob[ 1024 - sizeof(mwGrabData*) - sizeof(int) ]; }; typedef struct mwMarker_ mwMarker; struct mwMarker_ { void *host; char *text; mwMarker *next; int level; }; #if defined(WIN32) || defined(__WIN32__) typedef HANDLE mwMutex; #endif #if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H) typedef pthread_mutex_t mwMutex; #endif /*********************************************************************** ** Static variables ***********************************************************************/ static int mwInited = 0; static int mwInfoWritten = 0; static int mwUseAtexit = 0; static FILE* mwLog = NULL; static int mwFlushing = 0; static int mwStatLevel = MW_STAT_DEFAULT; static int mwNML = MW_NML_DEFAULT; static int mwFBI = 0; static long mwAllocLimit = 0L; static int mwUseLimit = 0; static long mwNumCurAlloc = 0L; static mwData* mwHead = NULL; static mwData* mwTail = NULL; static int mwDataSize = 0; static unsigned char mwOverflowZoneTemplate[] = "mEmwAtch"; static int mwOverflowZoneSize = mwROUNDALLOC; static void (*mwOutFunction)(int) = NULL; static int (*mwAriFunction)(const char*) = NULL; static int mwAriAction = MW_ARI_ABORT; static char mwPrintBuf[MW_TRACE_BUFFER+8]; static unsigned long mwCounter = 0L; static long mwErrors = 0L; static int mwTestFlags = 0; static int mwTestAlways = 0; static FILE* mwLogB1 = NULL; static int mwFlushingB1 = 0; static mwStat* mwStatList = NULL; static long mwStatTotAlloc = 0L; static long mwStatMaxAlloc = 0L; static long mwStatNumAlloc = 0L; static long mwStatCurAlloc = 0L; static long mwNmlNumAlloc = 0L; static long mwNmlCurAlloc = 0L; static mwGrabData* mwGrabList = NULL; static long mwGrabSize = 0L; static void * mwLastFree[MW_FREE_LIST]; static const char *mwLFfile[MW_FREE_LIST]; static int mwLFline[MW_FREE_LIST]; static int mwLFcur = 0; static mwMarker* mwFirstMark = NULL; static FILE* mwLogB2 = NULL; static int mwFlushingB2 = 0; #ifdef MW_HAVE_MUTEX static mwMutex mwGlobalMutex; #endif /*********************************************************************** ** Static function declarations ***********************************************************************/ static void mwAutoInit( void ); static FILE* mwLogR( void ); static void mwLogW( FILE* ); static int mwFlushR( void ); static void mwFlushW( int ); static void mwFlush( void ); static void mwIncErr( void ); static void mwUnlink( mwData*, const char* file, int line ); static int mwRelink( mwData*, const char* file, int line ); static int mwIsHeapOK( mwData *mw ); static int mwIsOwned( mwData* mw, const char* file, int line ); static int mwTestBuf( mwData* mw, const char* file, int line ); static void mwDefaultOutFunc( int ); static void mwWrite( const char* format, ... ); static void mwLogFile( const char* name ); static size_t mwFreeUp( size_t, int ); static const void *mwTestMem( const void *, unsigned, int ); static int mwStrCmpI( const char *s1, const char *s2 ); static int mwTestNow( const char *file, int line, int always_invoked ); static void mwDropAll( void ); static const char *mwGrabType( int type ); static unsigned mwGrab_( unsigned kb, int type, int silent ); static unsigned mwDrop_( unsigned kb, int type, int silent ); static int mwARI( const char* text ); static void mwStatReport( void ); static mwStat* mwStatGet( const char*, int, int ); static void mwStatAlloc( size_t, const char*, int ); static void mwStatFree( size_t, const char*, int ); static int mwCheckOF( const void * p ); static void mwWriteOF( void * p ); static char mwDummy( char c ); #ifdef MW_HAVE_MUTEX static void mwMutexInit( void ); static void mwMutexTerm( void ); static void mwMutexLock( void ); static void mwMutexUnlock( void ); #endif /*********************************************************************** ** System functions ***********************************************************************/ void mwInit( void ) { time_t tid; if( mwInited++ > 0 ) return; MW_MUTEX_INIT(); /* start a log if none is running */ if( mwLogR() == NULL ) mwLogFile( "memwatch.log" ); if( mwLogR() == NULL ) { int i; char buf[32]; /* oops, could not open it! */ /* probably because it's already open */ /* so we try some other names */ for( i=1; i<100; i++ ) { sprintf( buf, "memwat%02d.log", i ); mwLogFile( buf ); if( mwLogR() != NULL ) break; } } /* initialize the statistics */ mwStatList = NULL; mwStatTotAlloc = 0L; mwStatCurAlloc = 0L; mwStatMaxAlloc = 0L; mwStatNumAlloc = 0L; mwNmlCurAlloc = 0L; mwNmlNumAlloc = 0L; /* calculate the buffer size to use for a mwData */ mwDataSize = sizeof(mwData); while( mwDataSize % mwROUNDALLOC ) mwDataSize ++; /* write informational header if needed */ if( !mwInfoWritten ) { mwInfoWritten = 1; (void) time( &tid ); mwWrite( "\n=============" " MEMWATCH " VERSION " Copyright (C) 1992-1999 Johan Lindh " "=============\n"); mwWrite( "\nStarted at %s\n", ctime( &tid ) ); /**************************************************************** Generic */ mwWrite( "Modes: " ); #ifdef mwNew mwWrite( "C++ " ); #endif /* mwNew */ #ifdef __STDC__ mwWrite( "__STDC__ " ); #endif /* __STDC__ */ #ifdef mw16BIT mwWrite( "16-bit " ); #endif #ifdef mw32BIT mwWrite( "32-bit " ); #endif #ifdef mw64BIT mwWrite( "64-bit " ); #endif mwWrite( "mwDWORD==(" mwDWORD_DEFINED ")\n" ); mwWrite( "mwROUNDALLOC==%d sizeof(mwData)==%d mwDataSize==%d\n", mwROUNDALLOC, sizeof(mwData), mwDataSize ); /**************************************************************** Generic */ /************************************************************ Microsoft C */ #ifdef _MSC_VER mwWrite( "Compiled using Microsoft C" CPPTEXT " %d.%02d\n", _MSC_VER / 100, _MSC_VER % 100 ); #endif /* _MSC_VER */ /************************************************************ Microsoft C */ /************************************************************** Borland C */ #ifdef __BORLANDC__ mwWrite( "Compiled using Borland C" #ifdef __cplusplus "++ %d.%01d\n", __BCPLUSPLUS__/0x100, (__BCPLUSPLUS__%0x100)/0x10 ); #else " %d.%01d\n", __BORLANDC__/0x100, (__BORLANDC__%0x100)/0x10 ); #endif /* __cplusplus */ #endif /* __BORLANDC__ */ /************************************************************** Borland C */ /************************************************************** Watcom C */ #ifdef __WATCOMC__ mwWrite( "Compiled using Watcom C %d.%02d ", __WATCOMC__/100, __WATCOMC__%100 ); #ifdef __FLAT__ mwWrite( "(32-bit flat model)" ); #endif /* __FLAT__ */ mwWrite( "\n" ); #endif /* __WATCOMC__ */ /************************************************************** Watcom C */ mwWrite( "\n" ); FLUSH(); } if( mwUseAtexit ) (void) atexit( mwAbort ); return; } void mwAbort( void ) { mwData *mw; mwMarker *mrk; char *data; time_t tid; int c, i, j; int errors; tid = time( NULL ); mwWrite( "\nStopped at %s\n", ctime( &tid) ); if( !mwInited ) mwWrite( "internal: mwAbort(): MEMWATCH not initialized!\n" ); /* release the grab list */ mwDropAll(); /* report mwMarked items */ while( mwFirstMark ) { mrk = mwFirstMark->next; mwWrite( "mark: %p: %s\n", mwFirstMark->host, mwFirstMark->text ); free( mwFirstMark->text ); free( mwFirstMark ); mwFirstMark = mrk; mwErrors ++; } /* release all still allocated memory */ errors = 0; while( mwHead != NULL && errors < 3 ) { if( !mwIsOwned(mwHead, __FILE__, __LINE__ ) ) { if( errors < 3 ) { errors ++; mwWrite( "internal: NML/unfreed scan restarting\n" ); FLUSH(); mwHead = mwHead; continue; } mwWrite( "internal: NML/unfreed scan aborted, heap too damaged\n" ); FLUSH(); break; } mwFlushW(0); if( !(mwHead->flag & MW_NML) ) { mwErrors++; data = ((char*)mwHead)+mwDataSize; mwWrite( "unfreed: <%ld> %s(%d), %ld bytes at %p ", mwHead->count, mwHead->file, mwHead->line, (long)mwHead->size, data+mwOverflowZoneSize ); if( mwCheckOF( data ) ) { mwWrite( "[underflowed] "); FLUSH(); } if( mwCheckOF( (data+mwOverflowZoneSize+mwHead->size) ) ) { mwWrite( "[overflowed] "); FLUSH(); } mwWrite( " \t{" ); j = 16; if( mwHead->size < 16 ) j = (int) mwHead->size; for( i=0;i<16;i++ ) { if( i 126 ) c = '.'; mwWrite( "%c", c ); } mwWrite( "}\n" ); mw = mwHead; mwUnlink( mw, __FILE__, __LINE__ ); free( mw ); } else { data = ((char*)mwHead) + mwDataSize + mwOverflowZoneSize; if( mwTestMem( data, mwHead->size, MW_VAL_NML ) ) { mwErrors++; mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n", mwHead->count, data + mwOverflowZoneSize, mwHead->file, mwHead->line ); FLUSH(); } mwNmlNumAlloc --; mwNmlCurAlloc -= mwHead->size; mw = mwHead; mwUnlink( mw, __FILE__, __LINE__ ); free( mw ); } } if( mwNmlNumAlloc ) mwWrite("internal: NoMansLand block counter %ld, not zero\n", mwNmlNumAlloc ); if( mwNmlCurAlloc ) mwWrite("internal: NoMansLand byte counter %ld, not zero\n", mwNmlCurAlloc ); /* report statistics */ mwStatReport(); FLUSH(); mwInited = 0; mwHead = mwTail = NULL; if( mwErrors ) fprintf(mwSTDERR,"MEMWATCH detected %ld anomalies\n",mwErrors); mwLogFile( NULL ); mwErrors = 0; MW_MUTEX_TERM(); } void mwTerm( void ) { if( mwInited == 1 ) { mwAbort(); return; } if( !mwInited ) mwWrite("internal: mwTerm(): MEMWATCH has not been started!\n"); else mwInited --; } void mwStatistics( int level ) { mwAutoInit(); if( level<0 ) level=0; if( mwStatLevel != level ) { mwWrite( "statistics: now collecting on a %s basis\n", level<1?"global":(level<2?"module":"line") ); mwStatLevel = level; } } void mwAutoCheck( int onoff ) { mwAutoInit(); mwTestAlways = onoff; if( onoff ) mwTestFlags = MW_TEST_ALL; } void mwSetOutFunc( void (*func)(int) ) { mwAutoInit(); mwOutFunction = func; } static void mwWriteOF( void *p ) { int i; unsigned char *ptr; ptr = (unsigned char*) p; for( i=0; inext is not always set? */ void * mwMark( void *p, const char *desc, const char *file, unsigned line ) { mwMarker *mrk; unsigned n, isnew; char *buf; int tot, oflow = 0; char wherebuf[128]; mwAutoInit(); TESTS(NULL,0); if( desc == NULL ) desc = "unknown"; if( file == NULL ) file = "unknown"; tot = sprintf( wherebuf, "%.48s called from %s(%d)", desc, file, line ); if( tot >= (int)sizeof(wherebuf) ) { wherebuf[sizeof(wherebuf)-1] = 0; oflow = 1; } if( p == NULL ) { mwWrite("mark: %s(%d), no mark for NULL:'%s' may be set\n", file, line, desc ); return p; } if( mwFirstMark != NULL && !mwIsReadAddr( mwFirstMark, sizeof( mwMarker ) ) ) { mwWrite("mark: %s(%d), mwFirstMark (%p) is trashed, can't mark for %s\n", file, line, mwFirstMark, desc ); return p; } for( mrk=mwFirstMark; mrk; mrk=mrk->next ) { if( mrk->next != NULL && !mwIsReadAddr( mrk->next, sizeof( mwMarker ) ) ) { mwWrite("mark: %s(%d), mark(%p)->next(%p) is trashed, can't mark for %s\n", file, line, mrk, mrk->next, desc ); return p; } if( mrk->host == p ) break; } if( mrk == NULL ) { isnew = 1; mrk = (mwMarker*) malloc( sizeof( mwMarker ) ); if( mrk == NULL ) { mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc ); return p; } mrk->next = NULL; n = 0; } else { isnew = 0; n = strlen( mrk->text ); } n += strlen( wherebuf ); buf = (char*) malloc( n+3 ); if( buf == NULL ) { if( isnew ) free( mrk ); mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc ); return p; } if( isnew ) { memcpy( buf, wherebuf, n+1 ); mrk->next = mwFirstMark; mrk->host = p; mrk->text = buf; mrk->level = 1; mwFirstMark = mrk; } else { strcpy( buf, mrk->text ); strcat( buf, ", " ); strcat( buf, wherebuf ); free( mrk->text ); mrk->text = buf; mrk->level ++; } if( oflow ) { mwIncErr(); mwTrace( " [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n" ); } return p; } void* mwUnmark( void *p, const char *file, unsigned line ) { mwMarker *mrk, *prv; mrk = mwFirstMark; prv = NULL; while( mrk ) { if( mrk->host == p ) { if( mrk->level < 2 ) { if( prv ) prv->next = mrk->next; else mwFirstMark = mrk->next; free( mrk->text ); free( mrk ); return p; } mrk->level --; return p; } prv = mrk; mrk = mrk->next; } mwWrite("mark: %s(%d), no mark found for %p\n", file, line, p ); return p; } /*********************************************************************** ** Abort/Retry/Ignore handlers ***********************************************************************/ static int mwARI( const char *estr ) { char inbuf[81]; int c; fprintf(mwSTDERR, "\n%s\nMEMWATCH: Abort, Retry or Ignore? ", estr); (void) fgets(inbuf,sizeof(inbuf),stdin); for( c=0; inbuf[c] && inbuf[c] <= ' '; c++ ) ; c = inbuf[c]; if( c == 'R' || c == 'r' ) { mwBreakOut( estr ); return MW_ARI_RETRY; } if( c == 'I' || c == 'i' ) return MW_ARI_IGNORE; return MW_ARI_ABORT; } /* standard ARI handler (exported) */ int mwAriHandler( const char *estr ) { mwAutoInit(); return mwARI( estr ); } /* used to set the ARI function */ void mwSetAriFunc( int (*func)(const char *) ) { mwAutoInit(); mwAriFunction = func; } /*********************************************************************** ** Allocation handlers ***********************************************************************/ void* mwMalloc( size_t size, const char* file, int line) { size_t needed; mwData *mw; char *ptr; void *p; mwAutoInit(); MW_MUTEX_LOCK(); TESTS(file,line); mwCounter ++; needed = mwDataSize + mwOverflowZoneSize*2 + size; if( needed < size ) { /* theoretical case: req size + mw overhead exceeded size_t limits */ return NULL; } /* if this allocation would violate the limit, fail it */ if( mwUseLimit && ((long)size + mwStatCurAlloc > mwAllocLimit) ) { mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n", mwCounter, file, line, (long)size, mwAllocLimit - mwStatCurAlloc ); mwIncErr(); FLUSH(); MW_MUTEX_UNLOCK(); return NULL; } mw = (mwData*) malloc( needed ); if( mw == NULL ) { if( mwFreeUp(needed,0) >= needed ) { mw = (mwData*) malloc(needed); if( mw == NULL ) { mwWrite( "internal: mwFreeUp(%u) reported success, but malloc() fails\n", needed ); mwIncErr(); FLUSH(); } } if( mw == NULL ) { mwWrite( "fail: <%ld> %s(%d), %ld wanted %ld allocated\n", mwCounter, file, line, (long)size, mwStatCurAlloc ); mwIncErr(); FLUSH(); MW_MUTEX_UNLOCK(); return NULL; } } mw->count = mwCounter; mw->prev = NULL; mw->next = mwHead; mw->file = file; mw->size = size; mw->line = line; mw->flag = 0; mw->check = CHKVAL(mw); if( mwHead ) mwHead->prev = mw; mwHead = mw; if( mwTail == NULL ) mwTail = mw; ptr = ((char*)mw) + mwDataSize; mwWriteOF( ptr ); /* '*(long*)ptr = PRECHK;' */ ptr += mwOverflowZoneSize; p = ptr; memset( ptr, MW_VAL_NEW, size ); ptr += size; mwWriteOF( ptr ); /* '*(long*)ptr = POSTCHK;' */ mwNumCurAlloc ++; mwStatCurAlloc += (long) size; mwStatTotAlloc += (long) size; if( mwStatCurAlloc > mwStatMaxAlloc ) mwStatMaxAlloc = mwStatCurAlloc; mwStatNumAlloc ++; if( mwStatLevel ) mwStatAlloc( size, file, line ); MW_MUTEX_UNLOCK(); return p; } void* mwRealloc( void *p, size_t size, const char* file, int line) { int oldUseLimit, i; mwData *mw; char *ptr; mwAutoInit(); if( p == NULL ) return mwMalloc( size, file, line ); if( size == 0 ) { mwFree( p, file, line ); return NULL; } MW_MUTEX_LOCK(); /* do the quick ownership test */ mw = (mwData*) mwBUFFER_TO_MW( p ); if( mwIsOwned( mw, file, line ) ) { /* if the buffer is an NML, treat this as a double-free */ if( mw->flag & MW_NML ) { mwIncErr(); if( *((unsigned char*)(mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML ) { mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n", mwCounter, file, line, mw ); } goto check_dbl_free; } /* if this allocation would violate the limit, fail it */ if( mwUseLimit && ((long)size + mwStatCurAlloc - (long)mw->size > mwAllocLimit) ) { TESTS(file,line); mwCounter ++; mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n", mwCounter, file, line, (unsigned long)size - mw->size, mwAllocLimit - mwStatCurAlloc ); mwIncErr(); FLUSH(); MW_MUTEX_UNLOCK(); return NULL; } /* fake realloc operation */ oldUseLimit = mwUseLimit; mwUseLimit = 0; ptr = (char*) mwMalloc( size, file, line ); if( ptr != NULL ) { if( size < mw->size ) memcpy( ptr, p, size ); else memcpy( ptr, p, mw->size ); mwFree( p, file, line ); } mwUseLimit = oldUseLimit; MW_MUTEX_UNLOCK(); return (void*) ptr; } /* Unknown pointer! */ /* using free'd pointer? */ check_dbl_free: for(i=0;i %s(%d), %p was" " freed from %s(%d)\n", mwCounter, file, line, p, mwLFfile[i], mwLFline[i] ); FLUSH(); MW_MUTEX_UNLOCK(); return NULL; } } /* some weird pointer */ mwIncErr(); mwWrite( "realloc: <%ld> %s(%d), unknown pointer %p\n", mwCounter, file, line, p ); FLUSH(); MW_MUTEX_UNLOCK(); return NULL; } char *mwStrdup( const char* str, const char* file, int line ) { size_t len; char *newstring; MW_MUTEX_LOCK(); if( str == NULL ) { mwIncErr(); mwWrite( "strdup: <%ld> %s(%d), strdup(NULL) called\n", mwCounter, file, line ); FLUSH(); MW_MUTEX_UNLOCK(); return NULL; } len = strlen( str ) + 1; newstring = (char*) mwMalloc( len, file, line ); if( newstring != NULL ) memcpy( newstring, str, len ); MW_MUTEX_UNLOCK(); return newstring; } void mwFree( void* p, const char* file, int line ) { int i; mwData* mw; char buffer[ sizeof(mwData) + (mwROUNDALLOC*3) + 64 ]; /* this code is in support of C++ delete */ if( file == NULL ) { mwFree_( p ); MW_MUTEX_UNLOCK(); return; } mwAutoInit(); MW_MUTEX_LOCK(); TESTS(file,line); mwCounter ++; /* on NULL free, write a warning and return */ if( p == NULL ) { mwWrite( "NULL free: <%ld> %s(%d), NULL pointer free'd\n", mwCounter, file, line ); FLUSH(); MW_MUTEX_UNLOCK(); return; } /* do the quick ownership test */ mw = (mwData*) mwBUFFER_TO_MW( p ); if( mwIsOwned( mw, file, line ) ) { (void) mwTestBuf( mw, file, line ); /* if the buffer is an NML, treat this as a double-free */ if( mw->flag & MW_NML ) { if( *(((unsigned char*)mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML ) { mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n", mwCounter, file, line, mw ); } goto check_dbl_free; } /* update the statistics */ mwNumCurAlloc --; mwStatCurAlloc -= (long) mw->size; if( mwStatLevel ) mwStatFree( mw->size, mw->file, mw->line ); /* we should either free the allocation or keep it as NML */ if( mwNML ) { mw->flag |= MW_NML; mwNmlNumAlloc ++; mwNmlCurAlloc += (long) mw->size; memset( ((char*)mw)+mwDataSize+mwOverflowZoneSize, MW_VAL_NML, mw->size ); } else { /* unlink the allocation, and enter the post-free data */ mwUnlink( mw, file, line ); memset( mw, MW_VAL_DEL, mw->size + mwDataSize+mwOverflowZoneSize+mwOverflowZoneSize ); if( mwFBI ) { memset( mw, '.', mwDataSize + mwOverflowZoneSize ); sprintf( buffer, "FBI<%ld>%s(%d)", mwCounter, file, line ); strncpy( (char*)(void*)mw, buffer, mwDataSize + mwOverflowZoneSize ); } free( mw ); } /* add the pointer to the last-free track */ mwLFfile[ mwLFcur ] = file; mwLFline[ mwLFcur ] = line; mwLastFree[ mwLFcur++ ] = p; if( mwLFcur == MW_FREE_LIST ) mwLFcur = 0; MW_MUTEX_UNLOCK(); return; } /* check for double-freeing */ check_dbl_free: for(i=0;i %s(%d), %p was" " freed from %s(%d)\n", mwCounter, file, line, p, mwLFfile[i], mwLFline[i] ); FLUSH(); MW_MUTEX_UNLOCK(); return; } } /* some weird pointer... block the free */ mwIncErr(); mwWrite( "WILD free: <%ld> %s(%d), unknown pointer %p\n", mwCounter, file, line, p ); FLUSH(); MW_MUTEX_UNLOCK(); return; } void* mwCalloc( size_t a, size_t b, const char *file, int line ) { void *p; size_t size = a * b; p = mwMalloc( size, file, line ); if( p == NULL ) return NULL; memset( p, 0, size ); return p; } void mwFree_( void *p ) { MW_MUTEX_LOCK(); TESTS(NULL,0); MW_MUTEX_UNLOCK(); free(p); } void* mwMalloc_( size_t size ) { MW_MUTEX_LOCK(); TESTS(NULL,0); MW_MUTEX_UNLOCK(); return malloc( size ); } void* mwRealloc_( void *p, size_t size ) { MW_MUTEX_LOCK(); TESTS(NULL,0); MW_MUTEX_UNLOCK(); return realloc( p, size ); } void* mwCalloc_( size_t a, size_t b ) { MW_MUTEX_LOCK(); TESTS(NULL,0); MW_MUTEX_UNLOCK(); return calloc( a, b ); } void mwFlushNow( void ) { if( mwLogR() ) fflush( mwLogR() ); return; } void mwDoFlush( int onoff ) { mwFlushW( onoff<1?0:onoff ); if( onoff ) if( mwLogR() ) fflush( mwLogR() ); return; } void mwLimit( long lim ) { TESTS(NULL,0); mwWrite("limit: old limit = "); if( !mwAllocLimit ) mwWrite( "none" ); else mwWrite( "%ld bytes", mwAllocLimit ); mwWrite( ", new limit = "); if( !lim ) { mwWrite( "none\n" ); mwUseLimit = 0; } else { mwWrite( "%ld bytes\n", lim ); mwUseLimit = 1; } mwAllocLimit = lim; FLUSH(); } void mwSetAriAction( int action ) { MW_MUTEX_LOCK(); TESTS(NULL,0); mwAriAction = action; MW_MUTEX_UNLOCK(); return; } int mwAssert( int expc, const char *exps, const char *fn, int ln ) { int i; char buffer[MW_TRACE_BUFFER+8]; if( expc ) { return 0; } mwAutoInit(); MW_MUTEX_LOCK(); TESTS(fn,ln); mwIncErr(); mwCounter++; mwWrite( "assert trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps ); if( mwAriFunction != NULL ) { sprintf( buffer, "MEMWATCH: assert trap: %s(%d), %s", fn, ln, exps ); i = (*mwAriFunction)(buffer); switch( i ) { case MW_ARI_IGNORE: mwWrite( "assert trap: <%ld> IGNORED - execution continues\n", mwCounter ); MW_MUTEX_UNLOCK(); return 0; case MW_ARI_RETRY: mwWrite( "assert trap: <%ld> RETRY - executing again\n", mwCounter ); MW_MUTEX_UNLOCK(); return 1; } } else { if( mwAriAction & MW_ARI_IGNORE ) { mwWrite( "assert trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter ); MW_MUTEX_UNLOCK(); return 0; } fprintf(mwSTDERR,"\nMEMWATCH: assert trap: %s(%d), %s\n", fn, ln, exps ); } FLUSH(); (void) mwTestNow( fn, ln, 1 ); FLUSH(); if( mwAriAction & MW_ARI_NULLREAD ) { /* This is made in an attempt to kick in */ /* any debuggers or OS stack traces */ FLUSH(); /*lint -save -e413 */ i = *((int*)NULL); mwDummy( (char)i ); /*lint -restore */ } MW_MUTEX_UNLOCK(); exit(255); /* NOT REACHED - the return statement is in to keep */ /* stupid compilers from squeaking about differing return modes. */ /* Smart compilers instead say 'code unreachable...' */ /*lint -save -e527 */ return 0; /*lint -restore */ } int mwVerify( int expc, const char *exps, const char *fn, int ln ) { int i; char buffer[MW_TRACE_BUFFER+8]; if( expc ) { return 0; } mwAutoInit(); MW_MUTEX_LOCK(); TESTS(fn,ln); mwIncErr(); mwCounter++; mwWrite( "verify trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps ); if( mwAriFunction != NULL ) { sprintf( buffer, "MEMWATCH: verify trap: %s(%d), %s", fn, ln, exps ); i = (*mwAriFunction)(buffer); if( i == 0 ) { mwWrite( "verify trap: <%ld> IGNORED - execution continues\n", mwCounter ); MW_MUTEX_UNLOCK(); return 0; } if( i == 1 ) { mwWrite( "verify trap: <%ld> RETRY - executing again\n", mwCounter ); MW_MUTEX_UNLOCK(); return 1; } } else { if( mwAriAction & MW_ARI_NULLREAD ) { /* This is made in an attempt to kick in */ /* any debuggers or OS stack traces */ FLUSH(); /*lint -save -e413 */ i = *((int*)NULL); mwDummy( (char)i ); /*lint -restore */ } if( mwAriAction & MW_ARI_IGNORE ) { mwWrite( "verify trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter ); MW_MUTEX_UNLOCK(); return 0; } fprintf(mwSTDERR,"\nMEMWATCH: verify trap: %s(%d), %s\n", fn, ln, exps ); } FLUSH(); (void) mwTestNow( fn, ln, 1 ); FLUSH(); MW_MUTEX_UNLOCK(); exit(255); /* NOT REACHED - the return statement is in to keep */ /* stupid compilers from squeaking about differing return modes. */ /* Smart compilers instead say 'code unreachable...' */ /*lint -save -e527 */ return 0; /*lint -restore */ } void mwTrace( const char *format, ... ) { int tot, oflow = 0; va_list mark; mwAutoInit(); MW_MUTEX_LOCK(); TESTS(NULL,0); if( mwOutFunction == NULL ) mwOutFunction = mwDefaultOutFunc; va_start( mark, format ); tot = vsprintf( mwPrintBuf, format, mark ); va_end( mark ); if( tot >= MW_TRACE_BUFFER ) { mwPrintBuf[MW_TRACE_BUFFER] = 0; oflow = 1; } for(tot=0;mwPrintBuf[tot];tot++) (*mwOutFunction)( mwPrintBuf[tot] ); if( oflow ) { mwIncErr(); mwTrace( " [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n" ); } FLUSH(); MW_MUTEX_UNLOCK(); } /*********************************************************************** ** Grab & Drop ***********************************************************************/ unsigned mwGrab( unsigned kb ) { TESTS(NULL,0); return mwGrab_( kb, MW_VAL_GRB, 0 ); } unsigned mwDrop( unsigned kb ) { TESTS(NULL,0); return mwDrop_( kb, MW_VAL_GRB, 0 ); } static void mwDropAll() { TESTS(NULL,0); (void) mwDrop_( 0, MW_VAL_GRB, 0 ); (void) mwDrop_( 0, MW_VAL_NML, 0 ); if( mwGrabList != NULL ) mwWrite( "internal: the grab list is not empty after mwDropAll()\n"); } static const char *mwGrabType( int type ) { switch( type ) { case MW_VAL_GRB: return "grabbed"; case MW_VAL_NML: return "no-mans-land"; default: /* do nothing */ ; } return ""; } static unsigned mwGrab_( unsigned kb, int type, int silent ) { unsigned i = kb; mwGrabData *gd; if( !kb ) i = kb = 65000U; for(;kb;kb--) { if( mwUseLimit && (mwStatCurAlloc + mwGrabSize + (long)sizeof(mwGrabData) > mwAllocLimit) ) { if( !silent ) { mwWrite("grabbed: all allowed memory to %s (%u kb)\n", mwGrabType(type), i-kb); FLUSH(); } return i-kb; } gd = (mwGrabData*) malloc( sizeof(mwGrabData) ); if( gd == NULL ) { if( !silent ) { mwWrite("grabbed: all available memory to %s (%u kb)\n", mwGrabType(type), i-kb); FLUSH(); } return i-kb; } mwGrabSize += (long) sizeof(mwGrabData); gd->next = mwGrabList; memset( gd->blob, type, sizeof(gd->blob) ); gd->type = type; mwGrabList = gd; } if( !silent ) { mwWrite("grabbed: %u kilobytes of %s memory\n", i, mwGrabType(type) ); FLUSH(); } return i; } static unsigned mwDrop_( unsigned kb, int type, int silent ) { unsigned i = kb; mwGrabData *gd,*tmp,*pr; const void *p; if( mwGrabList == NULL && kb == 0 ) return 0; if( !kb ) i = kb = 60000U; pr = NULL; gd = mwGrabList; for(;kb;) { if( gd == NULL ) { if( i-kb > 0 && !silent ) { mwWrite("dropped: all %s memory (%u kb)\n", mwGrabType(type), i-kb); FLUSH(); } return i-kb; } if( gd->type == type ) { if( pr ) pr->next = gd->next; kb --; tmp = gd; if( mwGrabList == gd ) mwGrabList = gd->next; gd = gd->next; p = mwTestMem( tmp->blob, sizeof( tmp->blob ), type ); if( p != NULL ) { mwWrite( "wild pointer: <%ld> %s memory hit at %p\n", mwCounter, mwGrabType(type), p ); FLUSH(); } mwGrabSize -= (long) sizeof(mwGrabData); free( tmp ); } else { pr = gd; gd = gd->next; } } if( !silent ) { mwWrite("dropped: %u kilobytes of %s memory\n", i, mwGrabType(type) ); FLUSH(); } return i; } /*********************************************************************** ** No-Mans-Land ***********************************************************************/ void mwNoMansLand( int level ) { mwAutoInit(); TESTS(NULL,0); switch( level ) { case MW_NML_NONE: (void) mwDrop_( 0, MW_VAL_NML, 0 ); break; case MW_NML_FREE: break; case MW_NML_ALL: (void) mwGrab_( 0, MW_VAL_NML, 0 ); break; default: return; } mwNML = level; } /*********************************************************************** ** Static functions ***********************************************************************/ static void mwAutoInit( void ) { if( mwInited ) return; mwUseAtexit = 1; mwInit(); return; } static FILE *mwLogR() { if( (mwLog == mwLogB1) && (mwLog == mwLogB2) ) return mwLog; if( mwLog == mwLogB1 ) mwLogB2 = mwLog; if( mwLog == mwLogB2 ) mwLogB1 = mwLog; if( mwLogB1 == mwLogB2 ) mwLog = mwLogB1; if( (mwLog == mwLogB1) && (mwLog == mwLogB2) ) { mwWrite("internal: log file handle damaged and recovered\n"); FLUSH(); return mwLog; } fprintf(mwSTDERR,"\nMEMWATCH: log file handle destroyed, using mwSTDERR\n" ); mwLog = mwLogB1 = mwLogB2 = mwSTDERR; return mwSTDERR; } static void mwLogW( FILE *p ) { mwLog = mwLogB1 = mwLogB2 = p; } static int mwFlushR() { if( (mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2) ) return mwFlushing; if( mwFlushing == mwFlushingB1 ) mwFlushingB2 = mwFlushing; if( mwFlushing == mwFlushingB2 ) mwFlushingB1 = mwFlushing; if( mwFlushingB1 == mwFlushingB2 ) mwFlushing = mwFlushingB1; if( (mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2) ) { mwWrite("internal: flushing flag damaged and recovered\n"); FLUSH(); return mwFlushing; } mwWrite("internal: flushing flag destroyed, so set to true\n"); mwFlushing = mwFlushingB1 = mwFlushingB2 = 1; return 1; } static void mwFlushW( int n ) { mwFlushing = mwFlushingB1 = mwFlushingB2 = n; } static void mwIncErr() { mwErrors++; mwFlushW( mwFlushR()+1 ); FLUSH(); } static void mwFlush() { if( mwLogR() == NULL ) return; #ifdef MW_FLUSH fflush( mwLogR() ); #else if( mwFlushR() ) fflush( mwLogR() ); #endif return; } static void mwUnlink( mwData* mw, const char* file, int line ) { if( mw->prev == NULL ) { if( mwHead != mw ) mwWrite( "internal: <%ld> %s(%d), MW-%p: link1 NULL, but not head\n", mwCounter, file, line, mw ); mwHead = mw->next; } else { if( mw->prev->next != mw ) mwWrite( "internal: <%ld> %s(%d), MW-%p: link1 failure\n", mwCounter, file, line, mw ); else mw->prev->next = mw->next; } if( mw->next == NULL ) { if( mwTail != mw ) mwWrite( "internal: <%ld> %s(%d), MW-%p: link2 NULL, but not tail\n", mwCounter, file, line, mw ); mwTail = mw->prev; } else { if( mw->next->prev != mw ) mwWrite( "internal: <%ld> %s(%d), MW-%p: link2 failure\n", mwCounter, file, line, mw ); else mw->next->prev = mw->prev; } } /* ** Relinking tries to repair a damaged mw block. ** Returns nonzero if it thinks it successfully ** repaired the heap chain. */ static int mwRelink( mwData* mw, const char* file, int line ) { int fails; mwData *mw1, *mw2; long count, size; mwStat *ms; if( file == NULL ) file = "unknown"; if( mw == NULL ) { mwWrite("relink: cannot repair MW at NULL\n"); FLUSH(); goto emergency; } if( !mwIsSafeAddr(mw, mwDataSize) ) { mwWrite("relink: MW-%p is a garbage pointer\n", mw); FLUSH(); goto emergency; } mwWrite("relink: <%ld> %s(%d) attempting to repair MW-%p...\n", mwCounter, file, line, mw ); FLUSH(); fails = 0; /* Repair from head */ if( mwHead != mw ) { if( !mwIsSafeAddr( mwHead, mwDataSize ) ) { mwWrite("relink: failed for MW-%p; head pointer destroyed\n", mw ); FLUSH(); goto emergency; } for( mw1=mwHead; mw1; mw1=mw1->next ) { if( mw1->next == mw ) { mw->prev = mw1; break; } if( mw1->next && ( !mwIsSafeAddr(mw1->next, mwDataSize ) || mw1->next->prev != mw1) ) { mwWrite("relink: failed for MW-%p; forward chain fragmented at MW-%p: 'next' is %p\n", mw, mw1, mw1->next ); FLUSH(); goto emergency; } } if( mw1 == NULL ) { mwWrite("relink: MW-%p not found in forward chain search\n", mw ); FLUSH(); fails ++; } } else { mwWrite( "relink: MW-%p is the head (first) allocation\n", mw ); if( mw->prev != NULL ) { mwWrite( "relink: MW-%p prev pointer is non-NULL, you have a wild pointer\n", mw ); mw->prev = NULL; } } /* Repair from tail */ if( mwTail != mw ) { if( !mwIsSafeAddr( mwTail, mwDataSize ) ) { mwWrite("relink: failed for MW-%p; tail pointer destroyed\n", mw ); FLUSH(); goto emergency; } for( mw1=mwTail; mw1; mw1=mw1->prev ) { if( mw1->prev == mw ) { mw->next = mw1; break; } if( mw1->prev && (!mwIsSafeAddr(mw1->prev, mwDataSize ) || mw1->prev->next != mw1) ) { mwWrite("relink: failed for MW-%p; reverse chain fragmented at MW-%p, 'prev' is %p\n", mw, mw1, mw1->prev ); FLUSH(); goto emergency; } } if( mw1 == NULL ) { mwWrite("relink: MW-%p not found in reverse chain search\n", mw ); FLUSH(); fails ++; } } else { mwWrite( "relink: MW-%p is the tail (last) allocation\n", mw ); if( mw->next != NULL ) { mwWrite( "relink: MW-%p next pointer is non-NULL, you have a wild pointer\n", mw ); mw->next = NULL; } } if( fails > 1 ) { mwWrite("relink: heap appears intact, MW-%p probably garbage pointer\n", mw ); FLUSH(); goto verifyok; } /* restore MW info where possible */ if( mwIsReadAddr( mw->file, 1 ) ) { ms = mwStatGet( mw->file, -1, 0 ); if( ms == NULL ) mw->file = ""; } mw->check = CHKVAL(mw); goto verifyok; /* Emergency repair */ emergency: if( mwHead == NULL && mwTail == NULL ) { if( mwStatCurAlloc == 0 ) mwWrite("relink: <%ld> %s(%d) heap is empty, nothing to repair\n", mwCounter, file, line ); else mwWrite("relink: <%ld> %s(%d) heap damaged beyond repair\n", mwCounter, file, line ); FLUSH(); return 0; } mwWrite("relink: <%ld> %s(%d) attempting emergency repairs...\n", mwCounter, file, line ); FLUSH(); if( mwHead == NULL || mwTail == NULL ) { if( mwHead == NULL ) mwWrite("relink: mwHead is NULL, but mwTail is %p\n", mwTail ); else mwWrite("relink: mwTail is NULL, but mwHead is %p\n", mwHead ); } mw1=NULL; if( mwHead != NULL ) { if( !mwIsReadAddr( mwHead, mwDataSize ) || mwHead->check != CHKVAL(mwHead) ) { mwWrite("relink: mwHead (MW-%p) is damaged, skipping forward scan\n", mwHead ); mwHead = NULL; goto scan_reverse; } if( mwHead->prev != NULL ) { mwWrite("relink: the mwHead pointer's 'prev' member is %p, not NULL\n", mwHead->prev ); } for( mw1=mwHead; mw1; mw1=mw1->next ) { if( mw1->next ) { if( !mwIsReadAddr(mw1->next,mwDataSize) || !mw1->next->check != CHKVAL(mw1) || mw1->next->prev != mw1 ) { mwWrite("relink: forward chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n", mw1, mw1->size, (mw->flag & MW_NML)?"NoMansLand ":"", mw1->file, mw1->line ); if( mwIsReadAddr(mw1->next,mwDataSize ) ) { mwWrite("relink: forward chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n", mw1->next, mw1->size, (mw->flag & MW_NML)?"NoMansLand ":"", mwIsReadAddr(mw1->file,16)?mw1->file:"", mw1->line ); } else { mwWrite("relink: the 'next' pointer of this MW points to %p, which is out-of-legal-access\n", mw1->next ); } break; } } } } scan_reverse: mw2=NULL; if( mwTail != NULL ) { if( !mwIsReadAddr(mwTail,mwDataSize) || mwTail->check != CHKVAL(mwTail) ) { mwWrite("relink: mwTail (%p) is damaged, skipping reverse scan\n", mwTail ); mwTail = NULL; goto analyze; } if( mwTail->next != NULL ) { mwWrite("relink: the mwTail pointer's 'next' member is %p, not NULL\n", mwTail->next ); } for( mw2=mwTail; mw2; mw2=mw2->prev ) { if( mw2->prev ) { if( !mwIsReadAddr(mw2->prev,mwDataSize) || !mw2->prev->check != CHKVAL(mw2) || mw2->prev->next != mw2 ) { mwWrite("relink: reverse chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n", mw2, mw2->size, (mw->flag & MW_NML)?"NoMansLand ":"", mw2->file, mw2->line ); if( mwIsReadAddr(mw2->prev,mwDataSize ) ) { mwWrite("relink: reverse chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n", mw2->prev, mw2->size, (mw->flag & MW_NML)?"NoMansLand ":"", mwIsReadAddr(mw2->file,16)?mw2->file:"", mw2->line ); } else { mwWrite("relink: the 'prev' pointer of this MW points to %p, which is out-of-legal-access\n", mw2->prev ); } break; } } } } analyze: if( mwHead == NULL && mwTail == NULL ) { mwWrite("relink: both head and tail pointers damaged, aborting program\n"); mwFlushW(1); FLUSH(); abort(); } if( mwHead == NULL ) { mwHead = mw2; mwWrite("relink: heap truncated, MW-%p designated as new mwHead\n", mw2 ); mw2->prev = NULL; mw1 = mw2 = NULL; } if( mwTail == NULL ) { mwTail = mw1; mwWrite("relink: heap truncated, MW-%p designated as new mwTail\n", mw1 ); mw1->next = NULL; mw1 = mw2 = NULL; } if( mw1 == NULL && mw2 == NULL && mwHead->prev == NULL && mwTail->next == NULL ) { mwWrite("relink: verifying heap integrity...\n" ); FLUSH(); goto verifyok; } if( mw1 && mw2 && mw1 != mw2 ) { mw1->next = mw2; mw2->prev = mw1; mwWrite("relink: emergency repairs successful, assessing damage...\n"); FLUSH(); } else { mwWrite("relink: heap totally destroyed, aborting program\n"); mwFlushW(1); FLUSH(); abort(); } /* Verify by checking that the number of active allocations */ /* match the number of entries in the chain */ verifyok: if( !mwIsHeapOK( NULL ) ) { mwWrite("relink: heap verification FAILS - aborting program\n"); mwFlushW(1); FLUSH(); abort(); } for( size=count=0, mw1=mwHead; mw1; mw1=mw1->next ) { count ++; size += (long) mw1->size; } if( count == mwNumCurAlloc ) { mwWrite("relink: successful, "); if( size == mwStatCurAlloc ) { mwWrite("no allocations lost\n"); } else { if( mw != NULL ) { mwWrite("size information lost for MW-%p\n", mw); mw->size = 0; } } } else { mwWrite("relink: partial, %ld MW-blocks of %ld bytes lost\n", mwNmlNumAlloc+mwNumCurAlloc-count, mwNmlCurAlloc+mwStatCurAlloc-size ); return 0; } return 1; } /* ** If mwData* is NULL: ** Returns 0 if heap chain is broken. ** Returns 1 if heap chain is intact. ** If mwData* is not NULL: ** Returns 0 if mwData* is missing or if chain is broken. ** Returns 1 if chain is intact and mwData* is found. */ static int mwIsHeapOK( mwData *includes_mw ) { int found = 0; mwData *mw; for( mw = mwHead; mw; mw=mw->next ) { if( includes_mw == mw ) found++; if( !mwIsSafeAddr( mw, mwDataSize ) ) return 0; if( mw->prev ) { if( !mwIsSafeAddr( mw->prev, mwDataSize ) ) return 0; if( mw==mwHead || mw->prev->next != mw ) return 0; } if( mw->next ) { if( !mwIsSafeAddr( mw->next, mwDataSize ) ) return 0; if( mw==mwTail || mw->next->prev != mw ) return 0; } else if( mw!=mwTail ) return 0; } if( includes_mw != NULL && !found ) return 0; return 1; } static int mwIsOwned( mwData* mw, const char *file, int line ) { int retv; mwStat *ms; /* see if the address is legal according to OS */ if( !mwIsSafeAddr( mw, mwDataSize ) ) return 0; /* make sure we have _anything_ allocated */ if( mwHead == NULL && mwTail == NULL && mwStatCurAlloc == 0 ) return 0; /* calculate checksum */ if( mw->check != CHKVAL(mw) ) { /* may be damaged checksum, see if block is in heap */ if( mwIsHeapOK( mw ) ) { /* damaged checksum, repair it */ mwWrite( "internal: <%ld> %s(%d), checksum for MW-%p is incorrect\n", mwCounter, file, line, mw ); mwIncErr(); if( mwIsReadAddr( mw->file, 1 ) ) { ms = mwStatGet( mw->file, -1, 0 ); if( ms == NULL ) mw->file = ""; } else mw->file = ""; mw->size = 0; mw->check = CHKVAL(mw); return 1; } /* no, it's just some garbage data */ return 0; } /* check that the non-NULL pointers are safe */ if( mw->prev && !mwIsSafeAddr( mw->prev, mwDataSize ) ) mwRelink( mw, file, line ); if( mw->next && !mwIsSafeAddr( mw->next, mwDataSize ) ) mwRelink( mw, file, line ); /* safe address, checksum OK, proceed with heap checks */ /* see if the block is in the heap */ retv = 0; if( mw->prev ) { if( mw->prev->next == mw ) retv ++; } else { if( mwHead == mw ) retv++; } if( mw->next ) { if( mw->next->prev == mw ) retv ++; } else { if( mwTail == mw ) retv++; } if( mw->check == CHKVAL(mw) ) retv ++; if( retv > 2 ) return 1; /* block not in heap, check heap for corruption */ if( !mwIsHeapOK( mw ) ) { if( mwRelink( mw, file, line ) ) return 1; } /* unable to repair */ mwWrite( "internal: <%ld> %s(%d), mwIsOwned fails for MW-%p\n", mwCounter, file, line, mw ); mwIncErr(); return 0; } /* ** mwTestBuf: ** Checks a buffers links and pre/postfixes. ** Writes errors found to the log. ** Returns zero if no errors found. */ static int mwTestBuf( mwData* mw, const char* file, int line ) { int retv = 0; char *p; if( file == NULL ) file = "unknown"; if( !mwIsSafeAddr( mw, mwDataSize + mwOverflowZoneSize ) ) { mwWrite( "internal: <%ld> %s(%d): pointer MW-%p is invalid\n", mwCounter, file, line, mw ); mwIncErr(); return 2; } if( mw->check != CHKVAL(mw) ) { mwWrite( "internal: <%ld> %s(%d), info trashed; relinking\n", mwCounter, file, line ); mwIncErr(); if( !mwRelink( mw, file, line ) ) return 2; } if( mw->prev && mw->prev->next != mw ) { mwWrite( "internal: <%ld> %s(%d), buffer <%ld> %s(%d) link1 broken\n", mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line ); mwIncErr(); if( !mwRelink( mw, file, line ) ) retv = 2; } if( mw->next && mw->next->prev != mw ) { mwWrite( "internal: <%ld> %s(%d), buffer <%ld> %s(%d) link2 broken\n", mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line ); mwIncErr(); if( !mwRelink( mw, file, line ) ) retv = 2; } p = ((char*)mw) + mwDataSize; if( mwCheckOF( p ) ) { mwWrite( "underflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n", mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line ); mwIncErr(); retv = 1; } p += mwOverflowZoneSize + mw->size; if( mwIsReadAddr( p, mwOverflowZoneSize ) && mwCheckOF( p ) ) { mwWrite( "overflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n", mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line ); mwIncErr(); retv = 1; } return retv; } static void mwDefaultOutFunc( int c ) { if( mwLogR() ) fputc( c, mwLogR() ); } static void mwWrite( const char *format, ... ) { int tot, oflow = 0; va_list mark; mwAutoInit(); if( mwOutFunction == NULL ) mwOutFunction = mwDefaultOutFunc; va_start( mark, format ); tot = vsprintf( mwPrintBuf, format, mark ); va_end( mark ); if( tot >= MW_TRACE_BUFFER ) { mwPrintBuf[MW_TRACE_BUFFER] = 0; oflow = 1; } for(tot=0;mwPrintBuf[tot];tot++) (*mwOutFunction)( mwPrintBuf[tot] ); if( oflow ) { mwWrite( "\ninternal: mwWrite(): WARNING! OUTPUT EXCEEDED %u CHARS: SYSTEM UNSTABLE\n", MW_TRACE_BUFFER-1 ); FLUSH(); } return; } static void mwLogFile( const char *name ) { time_t tid; (void) time( &tid ); if( mwLogR() != NULL ) { fclose( mwLogR() ); mwLogW( NULL ); } if( name == NULL ) return; mwLogW( fopen( name, "a" COMMIT ) ); if( mwLogR() == NULL ) mwWrite( "logfile: failed to open/create file '%s'\n", name ); } /* ** Try to free NML memory until a contiguous allocation of ** 'needed' bytes can be satisfied. If this is not enough ** and the 'urgent' parameter is nonzero, grabbed memory is ** also freed. */ static size_t mwFreeUp( size_t needed, int urgent ) { void *p; mwData *mw, *mw2; char *data; /* free grabbed NML memory */ for(;;) { if( mwDrop_( 1, MW_VAL_NML, 1 ) == 0 ) break; p = malloc( needed ); if( p == NULL ) continue; free( p ); return needed; } /* free normal NML memory */ mw = mwHead; while( mw != NULL ) { if( !(mw->flag & MW_NML) ) mw = mw->next; else { data = ((char*)mw)+mwDataSize+mwOverflowZoneSize; if( mwTestMem( data, mw->size, MW_VAL_NML ) ) { mwIncErr(); mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n", mw->count, data + mwOverflowZoneSize, mw->file, mw->line ); } mw2 = mw->next; mwUnlink( mw, "mwFreeUp", 0 ); free( mw ); mw = mw2; p = malloc( needed ); if( p == NULL ) continue; free( p ); return needed; } } /* if not urgent (for internal purposes), fail */ if( !urgent ) return 0; /* free grabbed memory */ for(;;) { if( mwDrop_( 1, MW_VAL_GRB, 1 ) == 0 ) break; p = malloc( needed ); if( p == NULL ) continue; free( p ); return needed; } return 0; } static const void * mwTestMem( const void *p, unsigned len, int c ) { const unsigned char *ptr; ptr = (const unsigned char *) p; while( len-- ) { if( *ptr != (unsigned char)c ) return (const void*)ptr; ptr ++; } return NULL; } static int mwStrCmpI( const char *s1, const char *s2 ) { if( s1 == NULL || s2 == NULL ) return 0; while( *s1 ) { if( toupper(*s2) == toupper(*s1) ) { s1++; s2++; continue; } return 1; } return 0; } #define AIPH() if( always_invoked ) { mwWrite("autocheck: <%ld> %s(%d) ", mwCounter, file, line ); always_invoked = 0; } static int mwTestNow( const char *file, int line, int always_invoked ) { int retv = 0; mwData *mw; char *data; if( file && !always_invoked ) mwWrite("check: <%ld> %s(%d), checking %s%s%s\n", mwCounter, file, line, (mwTestFlags & MW_TEST_CHAIN) ? "chain ": "", (mwTestFlags & MW_TEST_ALLOC) ? "alloc ": "", (mwTestFlags & MW_TEST_NML) ? "nomansland ": "" ); if( mwTestFlags & MW_TEST_CHAIN ) { for( mw = mwHead; mw; mw=mw->next ) { if( !mwIsSafeAddr(mw, mwDataSize) ) { AIPH(); mwWrite("check: heap corruption detected\n"); mwIncErr(); return retv + 1; } if( mw->prev ) { if( !mwIsSafeAddr(mw->prev, mwDataSize) ) { AIPH(); mwWrite("check: heap corruption detected\n"); mwIncErr(); return retv + 1; } if( mw==mwHead || mw->prev->next != mw ) { AIPH(); mwWrite("check: heap chain broken, prev link incorrect\n"); mwIncErr(); retv ++; } } if( mw->next ) { if( !mwIsSafeAddr(mw->next, mwDataSize) ) { AIPH(); mwWrite("check: heap corruption detected\n"); mwIncErr(); return retv + 1; } if( mw==mwTail || mw->next->prev != mw ) { AIPH(); mwWrite("check: heap chain broken, next link incorrect\n"); mwIncErr(); retv ++; } } else if( mw!=mwTail ) { AIPH(); mwWrite("check: heap chain broken, tail incorrect\n"); mwIncErr(); retv ++; } } } if( mwTestFlags & MW_TEST_ALLOC ) { for( mw = mwHead; mw; mw=mw->next ) { if( mwTestBuf( mw, file, line ) ) retv ++; } } if( mwTestFlags & MW_TEST_NML ) { for( mw = mwHead; mw; mw=mw->next ) { if( (mw->flag & MW_NML) ) { data = ((char*)mw)+mwDataSize+mwOverflowZoneSize; if( mwTestMem( data, mw->size, MW_VAL_NML ) ) { mwIncErr(); mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n", mw->count, data + mwOverflowZoneSize, mw->file, mw->line ); } } } } if( file && !always_invoked && !retv ) mwWrite("check: <%ld> %s(%d), complete; no errors\n", mwCounter, file, line ); return retv; } /********************************************************************** ** Statistics **********************************************************************/ static void mwStatReport() { mwStat* ms, *ms2; const char *modname; int modnamelen; /* global statistics report */ mwWrite( "\nMemory usage statistics (global):\n" ); mwWrite( " N)umber of allocations made: %ld\n", mwStatNumAlloc ); mwWrite( " L)argest memory usage : %ld\n", mwStatMaxAlloc ); mwWrite( " T)otal of all alloc() calls: %ld\n", mwStatTotAlloc ); mwWrite( " U)nfreed bytes totals : %ld\n", mwStatCurAlloc ); FLUSH(); if( mwStatLevel < 1 ) return; /* on a per-module basis */ mwWrite( "\nMemory usage statistics (detailed):\n"); mwWrite( " Module/Line Number Largest Total Unfreed \n"); for( ms=mwStatList; ms; ms=ms->next ) { if( ms->line == -1 ) { if( ms->file == NULL || !mwIsReadAddr(ms->file,22) ) modname = ""; else modname = ms->file; modnamelen = strlen(modname); if( modnamelen > 42 ) { modname = modname + modnamelen - 42; } mwWrite(" %-42s %-8ld %-8ld %-8ld %-8ld\n", modname, ms->num, ms->max, ms->total, ms->curr ); if( ms->file && mwStatLevel > 1 ) { for( ms2=mwStatList; ms2; ms2=ms2->next ) { if( ms2->line!=-1 && ms2->file!=NULL && !mwStrCmpI( ms2->file, ms->file ) ) { mwWrite( " %-8d %-8ld %-8ld %-8ld %-8ld\n", ms2->line, ms2->num, ms2->max, ms2->total, ms2->curr ); } } } } } } static mwStat* mwStatGet( const char *file, int line, int makenew ) { mwStat* ms; if( mwStatLevel < 2 ) line = -1; for( ms=mwStatList; ms!=NULL; ms=ms->next ) { if( line != ms->line ) continue; if( file==NULL ) { if( ms->file == NULL ) break; continue; } if( ms->file == NULL ) continue; if( !strcmp( ms->file, file ) ) break; } if( ms != NULL ) return ms; if( !makenew ) return NULL; ms = (mwStat*) malloc( sizeof(mwStat) ); if( ms == NULL ) { if( mwFreeUp( sizeof(mwStat), 0 ) < sizeof(mwStat) || (ms=(mwStat*)malloc(sizeof(mwStat))) == NULL ) { mwWrite("internal: memory low, statistics incomplete for '%s'\n", file ); return NULL; } } ms->file = file; ms->line = line; ms->total = 0L; ms->max = 0L; ms->num = 0L; ms->curr = 0L; ms->next = mwStatList; mwStatList = ms; return ms; } static void mwStatAlloc( size_t size, const char* file, int line ) { mwStat* ms; /* update the module statistics */ ms = mwStatGet( file, -1, 1 ); if( ms != NULL ) { ms->total += (long) size; ms->curr += (long) size; ms->num ++; if( ms->curr > ms->max ) ms->max = ms->curr; } /* update the line statistics */ if( mwStatLevel > 1 && line != -1 && file ) { ms = mwStatGet( file, line, 1 ); if( ms != NULL ) { ms->total += (long) size; ms->curr += (long) size; ms->num ++; if( ms->curr > ms->max ) ms->max = ms->curr; } } } static void mwStatFree( size_t size, const char* file, int line ) { mwStat* ms; /* update the module statistics */ ms = mwStatGet( file, -1, 1 ); if( ms != NULL ) ms->curr -= (long) size; /* update the line statistics */ if( mwStatLevel > 1 && line != -1 && file ) { ms = mwStatGet( file, line, 1 ); if( ms != NULL ) ms->curr -= (long) size; } } /*********************************************************************** ** Safe memory checkers ** ** Using ifdefs, implement the operating-system specific mechanism ** of identifying a piece of memory as legal to access with read ** and write priviliges. Default: return nonzero for non-NULL pointers. ***********************************************************************/ static char mwDummy( char c ) { return c; } #ifndef MW_SAFEADDR #ifdef WIN32 #define MW_SAFEADDR #define WIN32_LEAN_AND_MEAN #include int mwIsReadAddr( const void *p, unsigned len ) { if( p == NULL ) return 0; if( IsBadReadPtr(p,len) ) return 0; return 1; } int mwIsSafeAddr( void *p, unsigned len ) { /* NOTE: For some reason, under Win95 the IsBad... */ /* can return false for invalid pointers. */ if( p == NULL ) return 0; if( IsBadReadPtr(p,len) || IsBadWritePtr(p,len) ) return 0; return 1; } #endif /* WIN32 */ #endif /* MW_SAFEADDR */ #ifndef MW_SAFEADDR #ifdef SIGSEGV #define MW_SAFEADDR typedef void (*mwSignalHandlerPtr)( int ); mwSignalHandlerPtr mwOldSIGSEGV = (mwSignalHandlerPtr) 0; jmp_buf mwSIGSEGVjump; static void mwSIGSEGV( int n ); static void mwSIGSEGV( int n ) { n = n; longjmp( mwSIGSEGVjump, 1 ); } int mwIsReadAddr( const void *p, unsigned len ) { const char *ptr; if( p == NULL ) return 0; if( !len ) return 1; /* set up to catch the SIGSEGV signal */ mwOldSIGSEGV = signal( SIGSEGV, mwSIGSEGV ); if( setjmp( mwSIGSEGVjump ) ) { signal( SIGSEGV, mwOldSIGSEGV ); return 0; } /* read all the bytes in the range */ ptr = (const char *)p; ptr += len; /* the reason for this rather strange construct is that */ /* we want to keep the number of used parameters and locals */ /* to a minimum. if we use len for a counter gcc will complain */ /* it may get clobbered by longjmp() at high warning levels. */ /* it's a harmless warning, but this way we don't have to see it. */ do { ptr --; if( *ptr == 0x7C ) (void) mwDummy( (char)0 ); } while( (const void*) ptr != p ); /* remove the handler */ signal( SIGSEGV, mwOldSIGSEGV ); return 1; } int mwIsSafeAddr( void *p, unsigned len ) { char *ptr; if( p == NULL ) return 0; if( !len ) return 1; /* set up to catch the SIGSEGV signal */ mwOldSIGSEGV = signal( SIGSEGV, mwSIGSEGV ); if( setjmp( mwSIGSEGVjump ) ) { signal( SIGSEGV, mwOldSIGSEGV ); return 0; } /* read and write-back all the bytes in the range */ ptr = (char *)p; ptr += len; /* the reason for this rather strange construct is that */ /* we want to keep the number of used parameters and locals */ /* to a minimum. if we use len for a counter gcc will complain */ /* it may get clobbered by longjmp() at high warning levels. */ /* it's a harmless warning, but this way we don't have to see it. */ do { ptr --; *ptr = mwDummy( *ptr ); } while( (void*) ptr != p ); /* remove the handler */ signal( SIGSEGV, mwOldSIGSEGV ); return 1; } #endif /* SIGSEGV */ #endif /* MW_SAFEADDR */ #ifndef MW_SAFEADDR int mwIsReadAddr( const void *p, unsigned len ) { if( p == NULL ) return 0; if( len == 0 ) return 1; return 1; } int mwIsSafeAddr( void *p, unsigned len ) { if( p == NULL ) return 0; if( len == 0 ) return 1; return 1; } #endif /********************************************************************** ** Mutex handling **********************************************************************/ #if defined(WIN32) || defined(__WIN32__) static void mwMutexInit( void ) { mwGlobalMutex = CreateMutex( NULL, FALSE, NULL); return; } static void mwMutexTerm( void ) { CloseHandle( mwGlobalMutex ); return; } static void mwMutexLock( void ) { if( WaitForSingleObject(mwGlobalMutex, 1000 ) == WAIT_TIMEOUT ) { mwWrite( "mwMutexLock: timed out, possible deadlock\n" ); } return; } static void mwMutexUnlock( void ) { ReleaseMutex( mwGlobalMutex ); return; } #endif #if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H) static void mwMutexInit( void ) { pthread_mutex_init( &mwGlobalMutex, NULL ); return; } static void mwMutexTerm( void ) { pthread_mutex_destroy( &mwGlobalMutex ); return; } static void mwMutexLock( void ) { pthread_mutex_lock(&mwGlobalMutex); return; } static void mwMutexUnlock( void ) { pthread_mutex_unlock(&mwGlobalMutex); return; } #endif /********************************************************************** ** C++ new & delete **********************************************************************/ #if 0 /* 980317: disabled C++ */ #ifdef __cplusplus #ifndef MEMWATCH_NOCPP int mwNCur = 0; const char *mwNFile = NULL; int mwNLine = 0; class MemWatch { public: MemWatch(); ~MemWatch(); }; MemWatch::MemWatch() { if( mwInited ) return; mwUseAtexit = 0; mwInit(); } MemWatch::~MemWatch() { if( mwUseAtexit ) return; mwTerm(); } /* ** This global new will catch all 'new' calls where MEMWATCH is ** not active. */ void* operator new( unsigned size ) { mwNCur = 0; return mwMalloc( size, "", 0 ); } /* ** This is the new operator that's called when a module uses mwNew. */ void* operator new( unsigned size, const char *file, int line ) { mwNCur = 0; return mwMalloc( size, file, line ); } /* ** This is the new operator that's called when a module uses mwNew[]. ** -- hjc 07/16/02 */ void* operator new[] ( unsigned size, const char *file, int line ) { mwNCur = 0; return mwMalloc( size, file, line ); } /* ** Since this delete operator will recieve ALL delete's ** even those from within libraries, we must accept ** delete's before we've been initialized. Nor can we ** reliably check for wild free's if the mwNCur variable ** is not set. */ void operator delete( void *p ) { if( p == NULL ) return; if( !mwInited ) { free( p ); return; } if( mwNCur ) { mwFree( p, mwNFile, mwNLine ); mwNCur = 0; return; } mwFree_( p ); } void operator delete[]( void *p ) { if( p == NULL ) return; if( !mwInited ) { free( p ); return; } if( mwNCur ) { mwFree( p, mwNFile, mwNLine ); mwNCur = 0; return; } mwFree_( p ); } #endif /* MEMWATCH_NOCPP */ #endif /* __cplusplus */ #endif /* 980317: disabled C++ */ /* MEMWATCH.C */ flasm-1.62/memwatch.h0000666000175000017500000007610407737014250013177 0ustar pabspabs/* ** MEMWATCH.H ** Nonintrusive ANSI C memory leak / overwrite detection ** Copyright (C) 1992-2002 Johan Lindh ** All rights reserved. ** Version 2.71 ** ************************************************************************ ** ** PURPOSE: ** ** MEMWATCH has been written to allow guys and gals that like to ** program in C a public-domain memory error control product. ** I hope you'll find it's as advanced as most commercial packages. ** The idea is that you use it during the development phase and ** then remove the MEMWATCH define to produce your final product. ** MEMWATCH is distributed in source code form in order to allow ** you to compile it for your platform with your own compiler. ** It's aim is to be 100% ANSI C, but some compilers are more stingy ** than others. If it doesn't compile without warnings, please mail ** me the configuration of operating system and compiler you are using ** along with a description of how to modify the source, and the version ** number of MEMWATCH that you are using. ** ************************************************************************ This file is part of MEMWATCH. MEMWATCH is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. MEMWATCH is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with MEMWATCH; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************ ** ** REVISION HISTORY: ** ** 920810 JLI [1.00] ** 920830 JLI [1.10 double-free detection] ** 920912 JLI [1.15 mwPuts, mwGrab/Drop, mwLimit] ** 921022 JLI [1.20 ASSERT and VERIFY] ** 921105 JLI [1.30 C++ support and TRACE] ** 921116 JLI [1.40 mwSetOutFunc] ** 930215 JLI [1.50 modified ASSERT/VERIFY] ** 930327 JLI [1.51 better auto-init & PC-lint support] ** 930506 JLI [1.55 MemWatch class, improved C++ support] ** 930507 JLI [1.60 mwTest & CHECK()] ** 930809 JLI [1.65 Abort/Retry/Ignore] ** 930820 JLI [1.70 data dump when unfreed] ** 931016 JLI [1.72 modified C++ new/delete handling] ** 931108 JLI [1.77 mwSetAssertAction() & some small changes] ** 940110 JLI [1.80 no-mans-land alloc/checking] ** 940328 JLI [2.00 version 2.0 rewrite] ** Improved NML (no-mans-land) support. ** Improved performance (especially for free()ing!). ** Support for 'read-only' buffers (checksums) ** ^^ NOTE: I never did this... maybe I should? ** FBI (free'd block info) tagged before freed blocks ** Exporting of the mwCounter variable ** mwBreakOut() localizes debugger support ** Allocation statistics (global, per-module, per-line) ** Self-repair ability with relinking ** 950913 JLI [2.10 improved garbage handling] ** 951201 JLI [2.11 improved auto-free in emergencies] ** 960125 JLI [X.01 implemented auto-checking using mwAutoCheck()] ** 960514 JLI [2.12 undefining of existing macros] ** 960515 JLI [2.13 possibility to use default new() & delete()] ** 960516 JLI [2.20 suppression of file flushing on unfreed msgs] ** 960516 JLI [2.21 better support for using MEMWATCH with DLL's] ** 960710 JLI [X.02 multiple logs and mwFlushNow()] ** 960801 JLI [2.22 merged X.01 version with current] ** 960805 JLI [2.30 mwIsXXXXAddr() to avoid unneeded GP's] ** 960805 JLI [2.31 merged X.02 version with current] ** 961002 JLI [2.32 support for realloc() + fixed STDERR bug] ** 961222 JLI [2.40 added mwMark() & mwUnmark()] ** 970101 JLI [2.41 added over/underflow checking after failed ASSERT/VERIFY] ** 970113 JLI [2.42 added support for PC-Lint 7.00g] ** 970207 JLI [2.43 added support for strdup()] ** 970209 JLI [2.44 changed default filename to lowercase] ** 970405 JLI [2.45 fixed bug related with atexit() and some C++ compilers] ** 970723 JLI [2.46 added MW_ARI_NULLREAD flag] ** 970813 JLI [2.47 stabilized marker handling] ** 980317 JLI [2.48 ripped out C++ support; wasn't working good anyway] ** 980318 JLI [2.50 improved self-repair facilities & SIGSEGV support] ** 980417 JLI [2.51 more checks for invalid addresses] ** 980512 JLI [2.52 moved MW_ARI_NULLREAD to occur before aborting] ** 990112 JLI [2.53 added check for empty heap to mwIsOwned] ** 990217 JLI [2.55 improved the emergency repairs diagnostics and NML] ** 990224 JLI [2.56 changed ordering of members in structures] ** 990303 JLI [2.57 first maybe-fixit-for-hpux test] ** 990516 JLI [2.58 added 'static' to the definition of mwAutoInit] ** 990517 JLI [2.59 fixed some high-sensitivity warnings] ** 990610 JLI [2.60 fixed some more high-sensitivity warnings] ** 990715 JLI [2.61 changed TRACE/ASSERT/VERIFY macro names] ** 991001 JLI [2.62 added CHECK_BUFFER() and mwTestBuffer()] ** 991007 JLI [2.63 first shot at a 64-bit compatible version] ** 991009 JLI [2.64 undef's strdup() if defined, mwStrdup made const] ** 000704 JLI [2.65 added some more detection for 64-bits] ** 010502 JLI [2.66 incorporated some user fixes] ** [mwRelink() could print out garbage pointer (thanks mac@phobos.ca)] ** [added array destructor for C++ (thanks rdasilva@connecttel.com)] ** [added mutex support (thanks rdasilva@connecttel.com)] ** 010531 JLI [2.67 fix: mwMutexXXX() was declared even if MW_HAVE_MUTEX was not defined] ** 010619 JLI [2.68 fix: mwRealloc() could leave the mutex locked] ** 020918 JLI [2.69 changed to GPL, added C++ array allocation by Howard Cohen] ** 030212 JLI [2.70 mwMalloc() bug for very large allocations (4GB on 32bits)] ** 030520 JLI [2.71 added ULONG_LONG_MAX as a 64-bit detector (thanks Sami Salonen)] ** ** To use, simply include 'MEMWATCH.H' as a header file, ** and add MEMWATCH.C to your list of files, and define the macro ** 'MEMWATCH'. If this is not defined, MEMWATCH will disable itself. ** ** To call the standard C malloc / realloc / calloc / free; use mwMalloc_(), ** mwCalloc_() and mwFree_(). Note that mwFree_() will correctly ** free both malloc()'d memory as well as mwMalloc()'d. ** ** 980317: C++ support has been disabled. ** The code remains, but is not compiled. ** ** For use with C++, which allows use of inlining in header files ** and class specific new/delete, you must also define 'new' as ** 'mwNew' and 'delete' as 'mwDelete'. Do this *after* you include ** C++ header files from libraries, otherwise you can mess up their ** class definitions. If you don't define these, the C++ allocations ** will not have source file and line number information. Also note, ** most C++ class libraries implement their own C++ memory management, ** and don't allow anyone to override them. MFC belongs to this crew. ** In these cases, the only thing to do is to use MEMWATCH_NOCPP. ** ** You can capture output from MEMWATCH using mwSetOutFunc(). ** Just give it the adress of a "void myOutFunc(int c)" function, ** and all characters to be output will be redirected there. ** ** A failing ASSERT() or VERIFY() will normally always abort your ** program. This can be changed using mwSetAriFunc(). Give it a ** pointer to a "int myAriFunc(const char *)" function. Your function ** must ask the user whether to Abort, Retry or Ignore the trap. ** Return 2 to Abort, 1 to Retry or 0 to Ignore. Beware retry; it ** causes the expression to be evaluated again! MEMWATCH has a ** default ARI handler. It's disabled by default, but you can enable ** it by calling 'mwDefaultAri()'. Note that this will STILL abort ** your program unless you define MEMWATCH_STDIO to allow MEMWATCH ** to use the standard C I/O streams. Also, setting the ARI function ** will cause MEMWATCH *NOT* to write the ARI error to stderr. The ** error string is passed to the ARI function instead, as the ** 'const char *' parameter. ** ** You can disable MEMWATCH's ASSERT/VERIFY and/or TRACE implementations. ** This can be useful if you're using a debug terminal or smart debugger. ** Disable them by defining MW_NOASSERT, MW_NOVERIFY or MW_NOTRACE. ** ** MEMWATCH fills all allocated memory with the byte 0xFE, so if ** you're looking at erroneous data which are all 0xFE:s, the ** data probably was not initialized by you. The exception is ** calloc(), which will fill with zero's. All freed buffers are ** zapped with 0xFD. If this is what you look at, you're using ** data that has been freed. If this is the case, be aware that ** MEMWATCH places a 'free'd block info' structure immediately ** before the freed data. This block contains info about where ** the block was freed. The information is in readable text, ** in the format "FBIfilename(line)", for example: ** "FBI<267>test.c(12)". Using FBI's slows down free(), so it's ** disabled by default. Use mwFreeBufferInfo(1) to enable it. ** ** To aid in tracking down wild pointer writes, MEMWATCH can perform ** no-mans-land allocations. No-mans-land will contain the byte 0xFC. ** MEMWATCH will, when this is enabled, convert recently free'd memory ** into NML allocations. ** ** MEMWATCH protects it's own data buffers with checksums. If you ** get an internal error, it means you're overwriting wildly, ** or using an uninitialized pointer. ** ************************************************************************ ** ** Note when compiling with Microsoft C: ** - MSC ignores fflush() by default. This is overridden, so that ** the disk log will always be current. ** ** This utility has been tested with: ** PC-lint 7.0k, passed as 100% ANSI C compatible ** Microsoft Visual C++ on Win16 and Win32 ** Microsoft C on DOS ** SAS C on an Amiga 500 ** Gnu C on a PC running Red Hat Linux ** ...and using an (to me) unknown compiler on an Atari machine. ** ************************************************************************ ** ** Format of error messages in MEMWATCH.LOG: ** message: filename(linenumber), information ** ** Errors caught by MemWatch, when they are detected, and any ** actions taken besides writing to the log file MEMWATCH.LOG: ** ** Double-freeing: ** A pointer that was recently freed and has not since been ** reused was freed again. The place where the previous free() ** was executed is displayed. ** Detect: delete or free() using the offending pointer. ** Action: The delete or free() is cancelled, execution continues. ** Underflow: ** You have written just ahead of the allocated memory. ** The size and place of the allocation is displayed. ** Detect: delete or free() of the damaged buffer. ** Action: The buffer is freed, but there may be secondary damage. ** Overflow: ** Like underflow, but you've written after the end of the buffer. ** Detect: see Underflow. ** Action: see Underflow. ** WILD free: ** An unrecognized pointer was passed to delete or free(). ** The pointer may have been returned from a library function; ** in that case, use mwFree_() to force free() of it. ** Also, this may be a double-free, but the previous free was ** too long ago, causing MEMWATCH to 'forget' it. ** Detect: delete or free() of the offending pointer. ** Action: The delete or free() is cancelled, execution continues. ** NULL free: ** It's unclear to me whether or not freeing of NULL pointers ** is legal in ANSI C, therefore a warning is written to the log file, ** but the error counter remains the same. This is legal using C++, ** so the warning does not appear with delete. ** Detect: When you free(NULL). ** Action: The free() is cancelled. ** Failed: ** A request to allocate memory failed. If the allocation is ** small, this may be due to memory depletion, but is more likely ** to be memory fragmentation problems. The amount of memory ** allocated so far is displayed also. ** Detect: When you new, malloc(), realloc() or calloc() memory. ** Action: NULL is returned. ** Realloc: ** A request to re-allocate a memory buffer failed for reasons ** other than out-of-memory. The specific reason is shown. ** Detect: When you realloc() ** Action: realloc() is cancelled, NULL is returned ** Limit fail: ** A request to allocate memory failed since it would violate ** the limit set using mwLimit(). mwLimit() is used to stress-test ** your code under simulated low memory conditions. ** Detect: At new, malloc(), realloc() or calloc(). ** Action: NULL is returned. ** Assert trap: ** An ASSERT() failed. The ASSERT() macro works like C's assert() ** macro/function, except that it's interactive. See your C manual. ** Detect: On the ASSERT(). ** Action: Program ends with an advisory message to stderr, OR ** Program writes the ASSERT to the log and continues, OR ** Program asks Abort/Retry/Ignore? and takes that action. ** Verify trap: ** A VERIFY() failed. The VERIFY() macro works like ASSERT(), ** but if MEMWATCH is not defined, it still evaluates the ** expression, but it does not act upon the result. ** Detect: On the VERIFY(). ** Action: Program ends with an advisory message to stderr, OR ** Program writes the VERIFY to the log and continues, OR ** Program asks Abort/Retry/Ignore? and takes that action. ** Wild pointer: ** A no-mans-land buffer has been written into. MEMWATCH can ** allocate and distribute chunks of memory solely for the ** purpose of trying to catch random writes into memory. ** Detect: Always on CHECK(), but can be detected in several places. ** Action: The error is logged, and if an ARI handler is installed, ** it is executed, otherwise, execution continues. ** Unfreed: ** A memory buffer you allocated has not been freed. ** You are informed where it was allocated, and whether any ** over or underflow has occured. MemWatch also displays up to ** 16 bytes of the data, as much as it can, in hex and text. ** Detect: When MemWatch terminates. ** Action: The buffer is freed. ** Check: ** An error was detected during a CHECK() operation. ** The associated pointer is displayed along with ** the file and line where the CHECK() was executed. ** Followed immediately by a normal error message. ** Detect: When you CHECK() ** Action: Depends on the error ** Relink: ** After a MEMWATCH internal control block has been trashed, ** MEMWATCH tries to repair the damage. If successful, program ** execution will continue instead of aborting. Some information ** about the block may be gone permanently, though. ** Detect: N/A ** Action: Relink successful: program continues. ** Relink fails: program aborts. ** Internal: ** An internal error is flagged by MEMWATCH when it's control ** structures have been damaged. You are likely using an uninitialized ** pointer somewhere in your program, or are zapping memory all over. ** The message may give you additional diagnostic information. ** If possible, MEMWATCH will recover and continue execution. ** Detect: Various actions. ** Action: Whatever is needed ** Mark: ** The program terminated without umarking all marked pointers. Marking ** can be used to track resources other than memory. mwMark(pointer,text,...) ** when the resource is allocated, and mwUnmark(pointer) when it's freed. ** The 'text' is displayed for still marked pointers when the program ** ends. ** Detect: When MemWatch terminates. ** Action: The error is logged. ** ** ************************************************************************ ** ** The author may be reached by e-mail at the address below. If you ** mail me about source code changes in MEMWATCH, remember to include ** MW's version number. ** ** Johan Lindh ** johan@linkdata.se ** ** The latest version of MEMWATCH may be downloaded from ** http://www.linkdata.se/ */ #ifndef __MEMWATCH_H #define __MEMWATCH_H /* Make sure that malloc(), realloc(), calloc() and free() are declared. */ /*lint -save -e537 */ #include /*lint -restore */ #ifdef __cplusplus extern "C" { #endif /* ** Constants used ** All MEMWATCH constants start with the prefix MW_, followed by ** a short mnemonic which indicates where the constant is used, ** followed by a descriptive text about it. */ #define MW_ARI_NULLREAD 0x10 /* Null read (to start debugger) */ #define MW_ARI_ABORT 0x04 /* ARI handler says: abort program! */ #define MW_ARI_RETRY 0x02 /* ARI handler says: retry action! */ #define MW_ARI_IGNORE 0x01 /* ARI handler says: ignore error! */ #define MW_VAL_NEW 0xFE /* value in newly allocated memory */ #define MW_VAL_DEL 0xFD /* value in newly deleted memory */ #define MW_VAL_NML 0xFC /* value in no-mans-land */ #define MW_VAL_GRB 0xFB /* value in grabbed memory */ #define MW_TEST_ALL 0xFFFF /* perform all tests */ #define MW_TEST_CHAIN 0x0001 /* walk the heap chain */ #define MW_TEST_ALLOC 0x0002 /* test allocations & NML guards */ #define MW_TEST_NML 0x0004 /* test all-NML areas for modifications */ #define MW_NML_NONE 0 /* no NML */ #define MW_NML_FREE 1 /* turn FREE'd memory into NML */ #define MW_NML_ALL 2 /* all unused memory is NML */ #define MW_NML_DEFAULT 0 /* the default NML setting */ #define MW_STAT_GLOBAL 0 /* only global statistics collected */ #define MW_STAT_MODULE 1 /* collect statistics on a module basis */ #define MW_STAT_LINE 2 /* collect statistics on a line basis */ #define MW_STAT_DEFAULT 0 /* the default statistics setting */ /* ** MemWatch internal constants ** You may change these and recompile MemWatch to change the limits ** of some parameters. Respect the recommended minimums! */ #define MW_TRACE_BUFFER 2048 /* (min 160) size of TRACE()'s output buffer */ #define MW_FREE_LIST 64 /* (min 4) number of free()'s to track */ /* ** Exported variables ** In case you have to remove the 'const' keyword because your compiler ** doesn't support it, be aware that changing the values may cause ** unpredictable behaviour. ** - mwCounter contains the current action count. You can use this to ** place breakpoints using a debugger, if you want. */ #ifndef __MEMWATCH_C extern const unsigned long mwCounter; #endif /* ** System functions ** Normally, it is not nessecary to call any of these. MEMWATCH will ** automatically initialize itself on the first MEMWATCH function call, ** and set up a call to mwAbort() using atexit(). Some C++ implementations ** run the atexit() chain before the program has terminated, so you ** may have to use mwInit() or the MemWatch C++ class to get good ** behaviour. ** - mwInit() can be called to disable the atexit() usage. If mwInit() ** is called directly, you must call mwTerm() to end MemWatch, or ** mwAbort(). ** - mwTerm() is usually not nessecary to call; but if called, it will ** call mwAbort() if it finds that it is cancelling the 'topmost' ** mwInit() call. ** - mwAbort() cleans up after MEMWATCH, reports unfreed buffers, etc. */ void mwInit( void ); void mwTerm( void ); void mwAbort( void ); /* ** Setup functions ** These functions control the operation of MEMWATCH's protective features. ** - mwFlushNow() causes MEMWATCH to flush it's buffers. ** - mwDoFlush() controls whether MEMWATCH flushes the disk buffers after ** writes. The default is smart flushing: MEMWATCH will not flush buffers ** explicitly until memory errors are detected. Then, all writes are ** flushed until program end or mwDoFlush(0) is called. ** - mwLimit() sets the allocation limit, an arbitrary limit on how much ** memory your program may allocate in bytes. Used to stress-test app. ** Also, in virtual-memory or multitasking environs, puts a limit on ** how much MW_NML_ALL can eat up. ** - mwGrab() grabs up X kilobytes of memory. Allocates actual memory, ** can be used to stress test app & OS both. ** - mwDrop() drops X kilobytes of grabbed memory. ** - mwNoMansLand() sets the behaviour of the NML logic. See the ** MW_NML_xxx for more information. The default is MW_NML_DEFAULT. ** - mwStatistics() sets the behaviour of the statistics collector. See ** the MW_STAT_xxx defines for more information. Default MW_STAT_DEFAULT. ** - mwFreeBufferInfo() enables or disables the tagging of free'd buffers ** with freeing information. This information is written in text form, ** using sprintf(), so it's pretty slow. Disabled by default. ** - mwAutoCheck() performs a CHECK() operation whenever a MemWatch function ** is used. Slows down performance, of course. ** - mwCalcCheck() calculates checksums for all data buffers. Slow! ** - mwDumpCheck() logs buffers where stored & calc'd checksums differ. Slow!! ** - mwMark() sets a generic marker. Returns the pointer given. ** - mwUnmark() removes a generic marker. If, at the end of execution, some ** markers are still in existence, these will be reported as leakage. ** returns the pointer given. */ void mwFlushNow( void ); void mwDoFlush( int onoff ); void mwLimit( long bytes ); unsigned mwGrab( unsigned kilobytes ); unsigned mwDrop( unsigned kilobytes ); void mwNoMansLand( int mw_nml_level ); void mwStatistics( int level ); void mwFreeBufferInfo( int onoff ); void mwAutoCheck( int onoff ); void mwCalcCheck( void ); void mwDumpCheck( void ); void * mwMark( void *p, const char *description, const char *file, unsigned line ); void * mwUnmark( void *p, const char *file, unsigned line ); /* ** Testing/verification/tracing ** All of these macros except VERIFY() evaluates to a null statement ** if MEMWATCH is not defined during compilation. ** - mwIsReadAddr() checks a memory area for read privilige. ** - mwIsSafeAddr() checks a memory area for both read & write privilige. ** This function and mwIsReadAddr() is highly system-specific and ** may not be implemented. If this is the case, they will default ** to returning nonzero for any non-NULL pointer. ** - CHECK() does a complete memory integrity test. Slow! ** - CHECK_THIS() checks only selected components. ** - CHECK_BUFFER() checks the indicated buffer for errors. ** - mwASSERT() or ASSERT() If the expression evaluates to nonzero, execution continues. ** Otherwise, the ARI handler is called, if present. If not present, ** the default ARI action is taken (set with mwSetAriAction()). ** ASSERT() can be disabled by defining MW_NOASSERT. ** - mwVERIFY() or VERIFY() works just like ASSERT(), but when compiling without ** MEMWATCH the macro evaluates to the expression. ** VERIFY() can be disabled by defining MW_NOVERIFY. ** - mwTRACE() or TRACE() writes some text and data to the log. Use like printf(). ** TRACE() can be disabled by defining MW_NOTRACE. */ int mwIsReadAddr( const void *p, unsigned len ); int mwIsSafeAddr( void *p, unsigned len ); int mwTest( const char *file, int line, int mw_test_flags ); int mwTestBuffer( const char *file, int line, void *p ); int mwAssert( int, const char*, const char*, int ); int mwVerify( int, const char*, const char*, int ); /* ** User I/O functions ** - mwTrace() works like printf(), but dumps output either to the ** function specified with mwSetOutFunc(), or the log file. ** - mwPuts() works like puts(), dumps output like mwTrace(). ** - mwSetOutFunc() allows you to give the adress of a function ** where all user output will go. (exeption: see mwSetAriFunc) ** Specifying NULL will direct output to the log file. ** - mwSetAriFunc() gives MEMWATCH the adress of a function to call ** when an 'Abort, Retry, Ignore' question is called for. The ** actual error message is NOT printed when you've set this adress, ** but instead it is passed as an argument. If you call with NULL ** for an argument, the ARI handler is disabled again. When the ** handler is disabled, MEMWATCH will automatically take the ** action specified by mwSetAriAction(). ** - mwSetAriAction() sets the default ARI return value MEMWATCH should ** use if no ARI handler is specified. Defaults to MW_ARI_ABORT. ** - mwAriHandler() is an ANSI ARI handler you can use if you like. It ** dumps output to stderr, and expects input from stdin. ** - mwBreakOut() is called in certain cases when MEMWATCH feels it would ** be nice to break into a debugger. If you feel like MEMWATCH, place ** an execution breakpoint on this function. */ void mwTrace( const char* format_string, ... ); void mwPuts( const char* text ); void mwSetOutFunc( void (*func)(int) ); void mwSetAriFunc( int (*func)(const char*) ); void mwSetAriAction( int mw_ari_value ); int mwAriHandler( const char* cause ); void mwBreakOut( const char* cause ); /* ** Allocation/deallocation functions ** These functions are the ones actually to perform allocations ** when running MEMWATCH, for both C and C++ calls. ** - mwMalloc() debugging allocator ** - mwMalloc_() always resolves to a clean call of malloc() ** - mwRealloc() debugging re-allocator ** - mwRealloc_() always resolves to a clean call of realloc() ** - mwCalloc() debugging allocator, fills with zeros ** - mwCalloc_() always resolves to a clean call of calloc() ** - mwFree() debugging free. Can only free memory which has ** been allocated by MEMWATCH. ** - mwFree_() resolves to a) normal free() or b) debugging free. ** Can free memory allocated by MEMWATCH and malloc() both. ** Does not generate any runtime errors. */ void* mwMalloc( size_t, const char*, int ); void* mwMalloc_( size_t ); void* mwRealloc( void *, size_t, const char*, int ); void* mwRealloc_( void *, size_t ); void* mwCalloc( size_t, size_t, const char*, int ); void* mwCalloc_( size_t, size_t ); void mwFree( void*, const char*, int ); void mwFree_( void* ); char* mwStrdup( const char *, const char*, int ); /* ** Enable/disable precompiler block ** This block of defines and if(n)defs make sure that references ** to MEMWATCH is completely removed from the code if the MEMWATCH ** manifest constant is not defined. */ #ifndef __MEMWATCH_C #ifdef MEMWATCH #define mwASSERT(exp) while(mwAssert((int)(exp),#exp,__FILE__,__LINE__)) #ifndef MW_NOASSERT #ifndef ASSERT #define ASSERT mwASSERT #endif /* !ASSERT */ #endif /* !MW_NOASSERT */ #define mwVERIFY(exp) while(mwVerify((int)(exp),#exp,__FILE__,__LINE__)) #ifndef MW_NOVERIFY #ifndef VERIFY #define VERIFY mwVERIFY #endif /* !VERIFY */ #endif /* !MW_NOVERIFY */ #define mwTRACE mwTrace #ifndef MW_NOTRACE #ifndef TRACE #define TRACE mwTRACE #endif /* !TRACE */ #endif /* !MW_NOTRACE */ /* some compilers use a define and not a function */ /* for strdup(). */ #ifdef strdup #undef strdup #endif #define malloc(n) mwMalloc(n,__FILE__,__LINE__) #define strdup(p) mwStrdup(p,__FILE__,__LINE__) #define realloc(p,n) mwRealloc(p,n,__FILE__,__LINE__) #define calloc(n,m) mwCalloc(n,m,__FILE__,__LINE__) #define free(p) mwFree(p,__FILE__,__LINE__) #define CHECK() mwTest(__FILE__,__LINE__,MW_TEST_ALL) #define CHECK_THIS(n) mwTest(__FILE__,__LINE__,n) #define CHECK_BUFFER(b) mwTestBuffer(__FILE__,__LINE__,b) #define MARK(p) mwMark(p,#p,__FILE__,__LINE__) #define UNMARK(p) mwUnmark(p,__FILE__,__LINE__) #else /* MEMWATCH */ #define mwASSERT(exp) #ifndef MW_NOASSERT #ifndef ASSERT #define ASSERT mwASSERT #endif /* !ASSERT */ #endif /* !MW_NOASSERT */ #define mwVERIFY(exp) exp #ifndef MW_NOVERIFY #ifndef VERIFY #define VERIFY mwVERIFY #endif /* !VERIFY */ #endif /* !MW_NOVERIFY */ /*lint -esym(773,mwTRACE) */ #define mwTRACE /*lint -save -e506 */ 1?(void)0:mwDummyTraceFunction /*lint -restore */ #ifndef MW_NOTRACE #ifndef TRACE /*lint -esym(773,TRACE) */ #define TRACE mwTRACE #endif /* !TRACE */ #endif /* !MW_NOTRACE */ extern void mwDummyTraceFunction(const char *,...); /*lint -save -e652 */ #define mwDoFlush(n) #define mwPuts(s) #define mwInit() #define mwGrab(n) #define mwDrop(n) #define mwLimit(n) #define mwTest(f,l) #define mwSetOutFunc(f) #define mwSetAriFunc(f) #define mwDefaultAri() #define mwNomansland() #define mwStatistics(f) #define mwMark(p,t,f,n) (p) #define mwUnmark(p,f,n) (p) #define mwMalloc(n,f,l) malloc(n) #define mwStrdup(p,f,l) strdup(p) #define mwRealloc(p,n,f,l) realloc(p,n) #define mwCalloc(n,m,f,l) calloc(n,m) #define mwFree(p) free(p) #define mwMalloc_(n) malloc(n) #define mwRealloc_(p,n) realloc(p,n) #define mwCalloc_(n,m) calloc(n,m) #define mwFree_(p) free(p) #define mwAssert(e,es,f,l) #define mwVerify(e,es,f,l) (e) #define mwTrace mwDummyTrace #define mwTestBuffer(f,l,b) (0) #define CHECK() #define CHECK_THIS(n) #define CHECK_BUFFER(b) #define MARK(p) (p) #define UNMARK(p) (p) /*lint -restore */ #endif /* MEMWATCH */ #endif /* !__MEMWATCH_C */ #ifdef __cplusplus } #endif #if 0 /* 980317: disabled C++ */ /* ** C++ support section ** Implements the C++ support. Please note that in order to avoid ** messing up library classes, C++ support is disabled by default. ** You must NOT enable it until AFTER the inclusion of all header ** files belonging to code that are not compiled with MEMWATCH, and ** possibly for some that are! The reason for this is that a C++ ** class may implement it's own new() function, and the preprocessor ** would substitute this crucial declaration for MEMWATCH new(). ** You can forcibly deny C++ support by defining MEMWATCH_NOCPP. ** To enble C++ support, you must be compiling C++, MEMWATCH must ** be defined, MEMWATCH_NOCPP must not be defined, and finally, ** you must define 'new' to be 'mwNew', and 'delete' to be 'mwDelete'. ** Unlike C, C++ code can begin executing *way* before main(), for ** example if a global variable is created. For this reason, you can ** declare a global variable of the class 'MemWatch'. If this is ** is the first variable created, it will then check ALL C++ allocations ** and deallocations. Unfortunately, this evaluation order is not ** guaranteed by C++, though the compilers I've tried evaluates them ** in the order encountered. */ #ifdef __cplusplus #ifndef __MEMWATCH_C #ifdef MEMWATCH #ifndef MEMWATCH_NOCPP extern int mwNCur; extern const char *mwNFile; extern int mwNLine; class MemWatch { public: MemWatch(); ~MemWatch(); }; void * operator new(size_t); void * operator new(size_t,const char *,int); void * operator new[] (size_t,const char *,int); /* hjc 07/16/02 */ void operator delete(void *); #define mwNew new(__FILE__,__LINE__) #define mwDelete (mwNCur=1,mwNFile=__FILE__,mwNLine=__LINE__),delete #endif /* MEMWATCH_NOCPP */ #endif /* MEMWATCH */ #endif /* !__MEMWATCH_C */ #endif /* __cplusplus */ #endif /* 980317: disabled C++ */ #endif /* __MEMWATCH_H */ /* EOF MEMWATCH.H */ flasm-1.62/Makefile0000666000175000017500000000205510323573711012650 0ustar pabspabsUNAME = $(shell uname) CC = gcc CFLAGS = -g -Wall -O2 LIBS = -lz OFILES = util.o keywords.o flasm.o unflasm.o lex.yy.o assembler.tab.o GARBAGE = assembler.tab.* lex.yy.c memwatch.o gmon.out memwatch.log core # if make debug, include memwatch and all symbols ifneq (,$(findstring debug,$(MAKECMDGOALS))) CFLAGS += -DMEMWATCH -pg -p -pedantic -W -Wcast-align -Wcast-qual -Wshadow -Wnested-externs -Wstrict-prototypes -Waggregate-return -Wmissing-prototypes -Wpointer-arith OFILES += memwatch.o else CFLAGS += -s endif # executable should not depend on cygwin.dll ifneq (,$(findstring CYGWIN,$(UNAME))) CFLAGS += -mno-cygwin endif all: flasm debug: flasm clean: -rm -f ${OFILES} ${GARBAGE} flasm: ${OFILES} ${CC} $(CFLAGS) -o flasm ${OFILES} ${LIBS} assembler.tab.c assembler.tab.h: assembler.y bison --defines --debug assembler.y lex.yy.c: assembler.flex assembler.tab.h flex -i assembler.flex keywords.c: keywords.gperf assembler.tab.h gperf --language=ANSI-C -t -T -E -o -k 1,$$,2,5 -S8 keywords.gperf > keywords.cflasm-1.62/assembler.y0000666000175000017500000021264210632542740013365 0ustar pabspabs/* flasm, command line assembler & disassembler of flash actionscript bytecode Copyright (c) 2001 Opaque Industries, (c) 2002-2007 Igor Kogan, (c) 2005 Wang Zhen All rights reserved. See LICENSE.TXT for terms of use. */ %{ #include #include #include #ifdef MEMWATCH #include "memwatch.h" #endif #include "util.h" #include "flasm.h" void yyerror(char *s); void warning(char *msg); int yylex(void); extern char *yytext; extern long int len; extern unsigned int nConstants; extern int mode; extern char compressAfter; extern int numActions; extern int clearregisterargs; extern char *arNames[]; extern unsigned int arPreFlags[]; extern unsigned int arSupFlags[]; static char *func_args[MAX_FUNCARGS]; static char *regfunc_args[MAX_FUNCDEPTH][MAX_REGISTERS]; static unsigned int numRegisters[MAX_FUNCDEPTH]; static int curFunc = -1; static unsigned int numAssets; static unsigned long int curEvent, allEvents; static int frameloadedStart = -1; static unsigned int numArgs; static unsigned int autoregFlags, nosuppressFlags; %} %union { long int num; char *str; } %expect 1 /* double nots shift/reduce warning */ %token MOVIENAME %token STRING %token HEX %token FLOAT %token DOUBLE %token TRUEVAL %token FALSEVAL %token NULLVAL %token UNDEFVAL %token LABEL %token INTEGER %token CONSTANT %token MOVIE %token COMPRESSED %token PROTECT %token SCRIPTLIMITS %token RECURSION %token TIMEOUT %token ENABLEDEBUGGER %token ENABLEDEBUGGER2 %token FRAME %token PLACEMOVIECLIP %token DEFINEMOVIECLIP %token INITMOVIECLIP %token DEFINEBUTTON %token ON %token ONCLIPEVENT %token AS %token IMPORTASSETS %token EXPORTASSETS %token METADATA %token FILEATTRIBUTES %token FROM // special double values %token _NAN %token POSITIVE_INFINITY %token NEGATIVE_INFINITY // special float values - push type 1 %token _NANF %token POSITIVE_INFINITYF %token NEGATIVE_INFINITYF // button events %token BIDLETOOVERUP %token BOVERUPTOIDLE %token BOVERUPTOOVERDOWN %token BOVERDOWNTOOVERUP %token BOVERDOWNTOOUTDOWN %token BOUTDOWNTOOVERDOWN %token BOUTDOWNTOIDLE %token BIDLETOOVERDOWN %token BOVERDOWNTOIDLE %token KEYPRESS // keyPress pre-defined %token _LEFT %token _RIGHT %token _HOME %token _END %token _INSERT %token _DELETE %token _BACKSPACE %token _ENTER %token _UP %token _DOWN %token _PAGEUP %token _PAGEDOWN %token _TAB %token _ESCAPE %token _SPACE // onClipEvents %token MCLOAD %token MCENTERFRAME %token MCUNLOAD %token MCMOUSEMOVE %token MCMOUSEDOWN %token MCMOUSEUP %token MCKEYDOWN %token MCKEYUP %token MCINITIALIZE %token MCCONSTRUCT %token MCDATA %token MCPRESS %token MCRELEASE %token MCRELEASEOUTSIDE %token MCROLLOVER %token MCROLLOUT %token MCDRAGOVER %token MCDRAGOUT // old-style properties %token X_PROPERTY %token Y_PROPERTY %token XSCALE_PROPERTY %token YSCALE_PROPERTY %token WIDTH_PROPERTY %token HEIGHT_PROPERTY %token ALPHA_PROPERTY %token VISIBLE_PROPERTY %token ROTATION_PROPERTY %token CURRENTFRAME_PROPERTY %token TOTALFRAMES_PROPERTY %token TARGET_PROPERTY %token FRAMESLOADED_PROPERTY %token NAME_PROPERTY %token DROPTARGET_PROPERTY %token URL_PROPERTY %token QUALITY_PROPERTY %token XMOUSE_PROPERTY %token YMOUSE_PROPERTY %token HIGHQUALITY_PROPERTY %token FOCUSRECT_PROPERTY %token SOUNDBUFTIME_PROPERTY %token NEXTFRAME %token PREVFRAME %token GOTOFRAME %token GOTOLABEL %token PLAY %token STOP %token TOGGLEQUALITY %token STOPSOUNDS %token FUNCTION %token FUNCTION2 %token CONSTANTPOOL %token END %token DUP %token SWAP %token POP %token WITH %token PUSH %token SETREGISTER %token CALLFUNCTION %token RETURN %token NEWMETHOD %token CALLMETHOD %token BITWISEAND %token BITWISEOR %token BITWISEXOR %token MODULO %token NEWADD %token NEWLESSTHAN %token NEWEQUALS %token TONUMBER %token TOSTRING %token INCREMENT %token DECREMENT %token TYPEOF %token TARGETPATH %token ENUMERATE %token ENUMERATEVALUE %token INSTANCEOF %token DELETE %token DELETE2 %token NEW %token INITARRAY %token INITOBJECT %token GETMEMBER %token SETMEMBER %token SHIFTLEFT %token SHIFTRIGHT %token SHIFTRIGHT2 %token VAR %token VAREQUALS %token OLDADD %token SUBTRACT %token MULTIPLY %token DIVIDE %token OLDEQUALS %token OLDLESSTHAN %token STRICTEQUALS %token GREATERTHAN %token LOGICALAND %token LOGICALOR %token LOGICALNOT %token STRINGEQ %token STRINGLENGTH %token SUBSTRING %token INT %token GETVARIABLE %token SETVARIABLE %token SETTARGET %token SETTARGETEXPR %token STRINGCONCAT %token GETPROPERTY %token SETPROPERTY %token DUPLICATECLIP %token REMOVECLIP %token TRACE %token STARTDRAGMOVIE %token STOPDRAGMOVIE %token STRINGLESSTHAN %token STRINGGREATERTHAN %token RANDOM %token MBLENGTH %token ORD %token CHR %token GETTIMER %token MBSUBSTRING %token MBORD %token MBCHR %token BRANCHALWAYS %token GETURL %token GETURL2 %token LOADMOVIE %token LOADMOVIENUM %token LOADVARIABLES %token LOADVARIABLESNUM %token POST %token GET %token BRANCHIFTRUE %token CALLFRAME %token GOTOANDPLAY %token GOTOANDSTOP %token SKIP %token IFFRAMELOADEDEXPR %token IFFRAMELOADED %token ELSE %token STRICTMODE %token OFF %token IMPLEMENTS %token EXTENDS %token THROW %token CAST %token TRY %token CATCH %token FINALLY %token FSCOMMAND2 %token REGISTER /* file attributes */ %token ATTRUSENETWORK %token ATTRRELATIVEURLS %token ATTRSUPPRESSCROSSDOMAINCACHE %token ATTRACTIONSCRIPT3 %token ATTRHASMETADATA /* unknown instruction handling */ %token SWFACTION %token HEXDATA %token '(' ')' ',' ':' '"' '.' '=' %type name_opt mcname_opt %type statements statement statements_opt %type function function_args function2 regarglist regarg autoregarglist autoregarg %type trycatchfinally catch_opt finally_opt %type impassets impassetsblocks_opt impassetsblocks impassetsblock %type expassets expassetsblocks_opt expassetsblocks expassetsblock %type push_list push_item %type register %type with %type settarget settargetexpression ifframeloaded ifframeloadedexpression %type actionblocks actionblock actionblocks_opt %type buttoneventblocks buttoneventblock buttoneventblocks_opt %type mceventblocks mceventblock mceventblocks_opt %type mcblocks mcblock mcblocks_opt %type frame definebutton definemc placemc initmc %type buttonevent buttonevents mcevent mcevents key property fileattr fileattrs %type opcode hex_list hexlist_opt %type urlmethod %% /* rules */ program : MOVIE MOVIENAME { if (mode != MODE_ASBYTECODE) startUpdate($2); else yyerror("Only single action block can be assembled to __bytecode__, not the whole Flasm project"); } actionblocks_opt END { finishUpdate(); } | statements { if (mode == MODE_ASBYTECODE) writeASBytecode(); else yyerror("Flasm project should start with movie 'moviename.swf'"); } ; actionblocks_opt : /* empty */ { $$ = 0; } | actionblocks { $$ = $1; } ; actionblocks : actionblock { $$ = $1; } | actionblocks actionblock { $$ = $1 + $2; } ; actionblock : frame { $$ = $1; } | definebutton { $$ = $1; } | definemc { $$ = $1; } | placemc { $$ = $1; } | initmc { $$ = $1; } | impassets { $$ = $1; } | expassets { $$ = $1; } | COMPRESSED { compressAfter = 1; $$ = 0; } | PROTECT { writeProtect(""); $$ = 0; } | PROTECT STRING { writeProtect($2); $$ = 0; } | ENABLEDEBUGGER { writeEnableDebugger(""); $$ = 0; } | ENABLEDEBUGGER STRING { writeEnableDebugger($2); $$ = 0; } | ENABLEDEBUGGER2 STRING { writeEnableDebugger2($2); $$ = 0; } | METADATA STRING { writeMetadata($2); $$ = 0; } | FILEATTRIBUTES fileattrs { writeFileAttrs($2); $$ = 0; } | SCRIPTLIMITS RECURSION INTEGER TIMEOUT INTEGER { if ($3>65535) yyerror("Recursion depth out of range"); if ($5>65535) yyerror("Timeout out of range"); writeScriptLimits((unsigned int)$3, (unsigned int)$5); $$ = 0; } ; frame : FRAME INTEGER statements_opt END { $$ = $3; /*action end*/ $$ += writeByte(0); writeDoAction(); $$ = 0; } ; initmc : INITMOVIECLIP INTEGER statements_opt END { $$ = $3; /*action end*/ $$ += writeByte(0); writeInitMC($2); $$ = 0; } ; definebutton : DEFINEBUTTON INTEGER { writeButtonStart($2); } buttoneventblocks_opt END { $$ = $4; writeButtonEnd(); } ; buttoneventblocks_opt : /* empty */ { $$ = 0; } | buttoneventblocks { $$ = $1; } ; buttoneventblocks : buttoneventblock { $$ = $1; } | buttoneventblocks buttoneventblock { $$ = $1 + $2; } ; buttoneventblock : ON { curEvent = 0; } buttonevents statements_opt END { $$ = $4; /*event action end*/ $$ += writeByte(0); writeButtonEvent((unsigned int)curEvent); } ; buttonevents : buttonevent { $$ = $1; curEvent += $1; } | buttonevents ',' buttonevent { $$ += $3; curEvent += $3; } ; buttonevent : BIDLETOOVERUP { $$ = 0x01; } | BOVERUPTOIDLE { $$ = 0x02; } | BOVERUPTOOVERDOWN { $$ = 0x04; } | BOVERDOWNTOOVERUP { $$ = 0x08; } | BOVERDOWNTOOUTDOWN { $$ = 0x10; } | BOUTDOWNTOOVERDOWN { $$ = 0x20; } | BOUTDOWNTOIDLE { $$ = 0x40; } | BIDLETOOVERDOWN { $$ = 0x80; } | BOVERDOWNTOIDLE { $$ = 0x100; } | KEYPRESS key { $$ = $2<<9; } ; key : _LEFT { $$ = 1; } | _RIGHT { $$ = 2; } | _HOME { $$ = 3; } | _END { $$ = 4; } | _INSERT { $$ = 5; } | _DELETE { $$ = 6; } | _BACKSPACE { $$ = 8; } | _ENTER { $$ = 13; } | _UP { $$ = 14; } | _DOWN { $$ = 15; } | _PAGEUP { $$ = 16; } | _PAGEDOWN { $$ = 17; } | _TAB { $$ = 18; } | _ESCAPE { $$ = 19; } | _SPACE { $$ = 32; } | STRING { $$ = $1[0]; } | HEX { if (xtoi($1)>0x7f) yyerror("key code should be not greater than 0x7F"); $$ = (char)xtoi($1); } ; register : REGISTER INTEGER { /* only 255 regs can be allocated within function2, r:0 being the first and r:254 the last; outside 4 global registers exist */ if ($2 >= 255) yyerror("Register number out of range"); if ((byte)$2 > 3 && curFunc < 0) warning("Local registers r:4 to r:255 work in function2 context only"); /* if needed, increase the number of registers to allocate for function2 */ if (curFunc >= 0 && $2 + 1 > (long int) numRegisters[curFunc]) numRegisters[curFunc] = $2 + 1; $$ = $2; } | REGISTER STRING { unsigned int m; int r = -1; if (curFunc >= 0) { for(m = 1; m < numRegisters[curFunc]; ++m) { if (regfunc_args[curFunc][m] != NULL && !strcmp($2, regfunc_args[curFunc][m])) { r = m; break; } } } if (r == -1) yyerror("Register alias not found"); $$ = r; } ; definemc : DEFINEMOVIECLIP INTEGER { writeDefineMCStart($2); } mcblocks_opt END { $$ = $4; writeDefineMCEnd(); } ; mcblocks_opt : /* empty */ { $$ = 0; } | mcblocks { $$ = $1; } ; mcblocks : mcblock { $$ = $1; } | mcblocks mcblock { $$ = $1 + $2; } ; mcblock : frame { $$ = $1; } | placemc { $$ = $1; } ; placemc : PLACEMOVIECLIP INTEGER mcname_opt { allEvents = 0; writePlaceMCStart($2); } mceventblocks_opt END { $$ = $5; writePlaceMCEnd(allEvents); } ; mcname_opt : /* empty */ { $$ = ""; } | AS STRING { $$ = $2; } ; mceventblocks_opt : /* empty */ { $$ = 0; } | mceventblocks { $$ = $1; } ; mceventblocks : mceventblock { $$ = $1; } | mceventblocks mceventblock { $$ = $1 + $2; } ; mceventblock : ONCLIPEVENT mcevents { curEvent = $2; allEvents |= curEvent; } statements_opt END { $$ = $4; /*event action end*/ $$ += writeByte(0); writeOnClipEvent(curEvent); curEvent = 0; } ; mcevents : mcevent { $$ = $1; curEvent += $1; } | mcevents ',' mcevent { $$ += $3; curEvent += $3; } ; mcevent : /* empty */ { yyerror("Missing mc event condition"); } | MCLOAD { $$ = 0x01; } | MCENTERFRAME { $$ = 0x02; } | MCUNLOAD { $$ = 0x04; } | MCMOUSEMOVE { $$ = 0x08; } | MCMOUSEDOWN { $$ = 0x10; } | MCMOUSEUP { $$ = 0x20; } | MCKEYDOWN { $$ = 0x40; } | MCKEYUP { $$ = 0x80; } | MCDATA { $$ = 0x100; } | MCINITIALIZE { $$ = 0x200; } | MCPRESS { $$ = 0x400; } | MCRELEASE { $$ = 0x800; } | MCRELEASEOUTSIDE { $$ = 0x1000; } | MCROLLOVER { $$ = 0x2000; } | MCROLLOUT { $$ = 0x4000; } | MCDRAGOVER { $$ = 0x8000; } | MCDRAGOUT { $$ = 0x10000; } | KEYPRESS key { $$ = 0x20000; writeByte($2); } | MCCONSTRUCT { $$ = 0x40000; } ; fileattrs : fileattr { $$ = $1; } | fileattrs ',' fileattr { $$ += $3; } ; fileattr : ATTRUSENETWORK { $$ = 0x01; } | ATTRRELATIVEURLS { $$ = 0x02; } | ATTRSUPPRESSCROSSDOMAINCACHE { $$ = 0x04; } | ATTRACTIONSCRIPT3 { $$ = 0x08; } | ATTRHASMETADATA { $$ = 0x10; } | INTEGER { $$ = $1; } ; catch_opt : /* empty */ { $$ = 0; } | CATCH statements_opt { $$ = $2; } ; finally_opt : /* empty */ { $$ = 0; } | FINALLY statements_opt { $$ = $2; } ; trycatchfinally : TRY name_opt { $$ = writeByte(SWFACTION_TRY); /* action length */ $$ += writeShort(strlen($2)+8); /* zero flag */ $$ += writeByte(0); /* zero try length */ $$ += writeShort(0); /* zero catch length */ $$ += writeShort(0); /* zero finally length */ $$ += writeShort(0); /* error variable name */ $$ += writeString($2); } statements_opt { $$ = $3 + $4; patchLength($$ - 6, $4); } catch_opt { $$ = $5 + $6; patchLength($$ - 8, $6); } finally_opt { $$ = $7 + $8; patchLength($$ - 10, $8); } END { byte flag = 0; $$ = $9; if ($6>0) flag = flag + 1; if ($8>0) flag = flag + 2; patchFlag($$ - 4, flag); } | TRY register { $$ = writeByte(SWFACTION_TRY); /* action length */ $$ += writeShort(8); /* zero flag */ $$ += writeByte(0); /* zero try length */ $$ += writeShort(0); /* zero catch length */ $$ += writeShort(0); /* zero finally length */ $$ += writeShort(0); /* error register number */ $$ += writeByte((byte) $2); } statements_opt { $$ = $3 + $4; patchLength($$ - 6, $4); } catch_opt { $$ = $5 + $6; patchLength($$ - 8, $6); } finally_opt { $$ = $7 + $8; patchLength($$ - 10, $8); } END { byte flag = 4; $$ = $9; if ($6>0) flag = flag + 1; if ($8>0) flag = flag + 2; patchFlag($$ - 4, flag); } ; statements : statement { $$ = $1; } | statements statement { $$ = $1 + $2; } ; statement : label { $$ = 0; } | function { $$ = $1; } | function2 { $$ = $1; } | with { $$ = $1; } | settarget { $$ = $1; } | settargetexpression { $$ = $1; } | ifframeloaded { $$ = $1; } | ifframeloadedexpression { $$ = $1; } | trycatchfinally { $$ = $1; } | opcode { $$ = $1; } ; label : LABEL { addLabel($1); } ; /* this is weird/dumb- now we're returning the number of args instead of their length, and the args are stored in a global array func_args. */ function_args : /* empty */ { $$ = 0; } | STRING { func_args[0] = $1; $$ = 1; } | function_args ',' STRING { func_args[$1] = $3; $$ = $1 + 1; } ; statements_opt : /* empty */ { $$ = 0; } | statements { $$ = $1; } ; name_opt : /* empty */ { $$ = ""; } | STRING { $$ = $1; } ; function : FUNCTION name_opt { $$ = writeByte(SWFACTION_DEFINEFUNCTION); /* zero block length */ $$ += writeShort(0); $$ += writeString($2); } '(' function_args ')' { unsigned int i; numArgs = $5; $$ = $3 + writeShort(numArgs); for(i = 0; i < numArgs; ++i) $$ += writeString(func_args[i]); /* zero function length */ $$ += writeShort(0); /* patch block length */ patchLength($$-3, $$-3); } statements_opt END { $$ = $7 + $8; /* patch function length */ patchLength($8, $8); } ; regarg : REGISTER INTEGER '=' STRING { numArgs++; $$ = writeByte((byte) $2); if ($2 == 0) yyerror("Function argument can't be stored in r:0"); if ($2 + 1 >= MAX_REGISTERS) yyerror("Too many registers"); if (numArgs >= MAX_FUNCARGS) yyerror("Too many function arguments"); if (regfunc_args[curFunc][$2] != NULL) yyerror("Duplicate register"); regfunc_args[curFunc][$2] = $4; if ($2 + 1 > (long int) numRegisters[curFunc]) numRegisters[curFunc] = $2 + 1; /* if parameter is stored in register, may skip its name */ if ($2 > 0 && clearregisterargs && mode >= MODE_UPDATE) $$ += writeString(""); else $$ += writeString($4); } | STRING { numArgs++; /* r:0 - not stored in register */ $$ = writeByte(0); $$ += writeString($1); } ; regarglist : /* empty */ { $$ = 0; } | regarg { $$ = $1; } | regarglist ',' regarg { $$ = $1 + $3; } ; autoregarg : REGISTER INTEGER '=' STRING { unsigned int preloadFlag = 0, i; if ($2 == 0 || $2 > MAX_AUTO_REGS) yyerror("Automatic values should be placed into consequent registers starting with r:1\nin this order: this, arguments, super, _root, _parent, _global"); if (regfunc_args[curFunc][$2] != NULL && strcmp(regfunc_args[curFunc][$2], $4)) yyerror("Duplicate register"); regfunc_args[curFunc][$2] = $4; if ($2 + 1 > (long int) numRegisters[curFunc]) numRegisters[curFunc] = $2 + 1; for (i = 0; i < MAX_AUTO_REGS; i++) { if (!strcmp($4, arNames[i])) { preloadFlag = arPreFlags[i]; if (nosuppressFlags & (1 << i)) yyerror("Duplicate automatic value"); break; } } if (preloadFlag == 0) yyerror("Only automatic values (this, arguments, super, _root, _parent, _global) are allowed here"); if ((autoregFlags & preloadFlag) != preloadFlag) autoregFlags += preloadFlag; else yyerror("Duplicate automatic value"); } | STRING { unsigned int nosuppressFlag = 0, i; for (i = 0; i < MAX_AUTO_REGS; i++) { if (!strcmp($1, arNames[i])) { nosuppressFlag = 1 << i; if ((autoregFlags & arPreFlags[i]) == arPreFlags[i]) yyerror("Duplicate automatic value"); break; } } if (nosuppressFlag == 0) yyerror("Only automatic values (this, arguments, super, _root, _parent, _global) are allowed here"); if (!(nosuppressFlags & nosuppressFlag)) nosuppressFlags += nosuppressFlag; else yyerror("Duplicate automatic value"); } ; autoregarglist : /* empty */ { $$ = 0; } | autoregarg { $$ = 1; } | autoregarglist ',' autoregarg { $$ = $1 + 1; } ; function2 : FUNCTION2 name_opt { $$ = writeByte(SWFACTION_DEFINEFUNCTION2); /* zero block length */ $$ += writeShort(0); /* function name */ $$ += writeString($2); curFunc++; memset(regfunc_args[curFunc], 0, sizeof (regfunc_args[curFunc])); numArgs = 0; /* zero num of function arguments */ $$ += writeShort(numArgs); /* allocate zero registers */ numRegisters[curFunc] = 0; $$ += writeByte(numRegisters[curFunc]); /* zero automatic register flags */ $$ += writeShort(0); } '(' regarglist ')' { $$ = $3 + $5; /* patch num of function arguments */ patchLength($5 + 3, numArgs); autoregFlags = 0; nosuppressFlags = 0; } '(' autoregarglist ')' { byte curautoreg = 1; unsigned int i; $$ = $7; /* zero body length */ $$ += writeShort(0); /* make sure auto registers are allocated in the right order */ for (i = 0; i < MAX_AUTO_REGS; i++) { if ((autoregFlags & arPreFlags[i]) == arPreFlags[i]) { if (regfunc_args[curFunc][curautoreg] != NULL && !strcmp(regfunc_args[curFunc][curautoreg], arNames[i])) curautoreg++; else yyerror("Automatic values should be placed into consequent registers starting with r:1\nin this order: this, arguments, super, _root, _parent, _global"); } else if (!(nosuppressFlags & (1 << i))) autoregFlags += arSupFlags[i]; } /* patch automatic register flags */ patchLength($$ - $3, autoregFlags); /* patch block length */ patchLength($$ - 3, $$ - 3); } statements_opt END { $$ = $11 + $12; /* patch number of registers to allocate */ if (numRegisters[curFunc] < MAX_REGISTERS) patchFlag($$ - $3 + 2, (byte) numRegisters[curFunc]); else yyerror("Too many registers."); /* patch function length */ patchLength($12, $12); curFunc--; } ; with : WITH { $$ = writeByte(SWFACTION_WITH); /* length of with action */ $$ += writeShort(2); /* length of with block - will be patched */ $$ += writeShort(0); } statements_opt END { $$ = $2 + $3; patchLength($3, $3); } ; settarget : SETTARGET STRING { $$ = writeByte(SWFACTION_SETTARGET); $$ += writeShort(strlen($2)+1); $$ += writeString($2); } statements_opt END { $$ = $4 + writeByte(SWFACTION_SETTARGET); $$ += $3 + writeShort(1); $$ += writeByte(0); } ; settargetexpression : SETTARGETEXPR { $$ = writeByte(SWFACTION_SETTARGETEXPRESSION); } statements_opt END { $$ = $3 + writeByte(SWFACTION_SETTARGET); $$ += $2 + writeShort(1); $$ += writeByte(0); } ; ifframeloadedexpression : IFFRAMELOADEDEXPR { if (frameloadedStart>-1) yyerror("IfFrameLoaded actions can't be nested"); $$ = writeByte(SWFACTION_IFFRAMELOADEDEXPRESSION); $$ += writeShort(1); $$ += writeByte(0); frameloadedStart = numActions; } statements_opt END { $$ = $2 + $3; patchFrameLoaded($3, numActions-frameloadedStart); frameloadedStart = -1; } ; ifframeloaded : IFFRAMELOADED INTEGER { if (frameloadedStart>-1) yyerror("IfFrameLoaded actions can't be nested"); $$ = writeByte(SWFACTION_IFFRAMELOADED); $$ += writeShort(3); $$ += writeShort($2); $$ += writeByte(0); frameloadedStart = numActions; } statements_opt END { $$ = $3 + $4; patchFrameLoaded($4, numActions-frameloadedStart); frameloadedStart = -1; } ; impassets : IMPORTASSETS FROM STRING { numAssets = 0; } impassetsblocks_opt END { $$ = $5; writeImportAssets($3, numAssets); $$ = 0; } ; impassetsblocks_opt : /* empty */ { $$ = 0; } | impassetsblocks { $$ = $1; } ; impassetsblocks : impassetsblock { $$ = $1; } | impassetsblocks impassetsblock { $$ = $1 + $2; } ; impassetsblock : STRING AS INTEGER { $$ = strlen($1)+3; writeShort($3); writeString($1); numAssets++; } ; expassets : EXPORTASSETS { numAssets = 0; } expassetsblocks_opt END { $$ = $3; writeExportAssets(numAssets); $$ = 0; } ; expassetsblocks_opt : /* empty */ { $$ = 0; } | expassetsblocks { $$ = $1; } ; expassetsblocks : expassetsblock { $$ = $1; } | expassetsblocks expassetsblock { $$ = $1 + $2; } ; expassetsblock : INTEGER AS STRING { $$ = strlen($3)+3; writeShort($1); writeString($3); numAssets++; } ; push_item : STRING { $$ = writePushString($1); } | CONSTANT { if ($1 < 256) { /* constant, 1-byte reference */ $$ = writeByte(0x08); $$ += writeByte((byte)$1); } else { /* constant, 2-byte reference */ $$ = writeByte(0x09); $$ += writeShort($1); } } | property { $$ = writeByte(0x01); $$ += writeFloat((float)$1); } | FLOAT { float f; sscanf($1, "%f", &f); $$ = writeByte(0x01); $$ += writeFloat(f); } | _NANF { $$ = writeByte(0x01); $$ += writeFloat(0.0f/0.0f); } | POSITIVE_INFINITYF { $$ = writeByte(0x01); $$ += writeFloat(1.0/0.0f); } | NEGATIVE_INFINITYF { $$ = writeByte(0x01); $$ += writeFloat(-1.0/0.0f); } | HEX { unsigned long int li = xtoi($1); if (li > 65535) yyerror("Hex number should be unsigned integer in the range from 0x0000 to 0xFFFF"); $$ = writeByte(0x07); $$ += writeLongInt(xtoi($1)); } | INTEGER { $$ = writeByte(0x07); $$ += writeLongInt($1); } | DOUBLE { $$ = writeByte(0x06); $$ += writeDouble(atof($1)); } | _NAN { $$ = writeByte(0x06); $$ += writeDouble(0.0/0.0); } | POSITIVE_INFINITY { $$ = writeByte(0x06); $$ += writeDouble(1.0/0.0); } | NEGATIVE_INFINITY { $$ = writeByte(0x06); $$ += writeDouble(-1.0/0.0); } | TRUEVAL { $$ = writeByte(0x05); $$ += writeByte(1); } | FALSEVAL { $$ = writeByte(0x05); $$ += writeByte(0); } | NULLVAL { $$ = writeByte(2); } | UNDEFVAL { $$ = writeByte(3); } | register { $$ = writeByte(0x04); $$ += writeByte((byte)$1); } ; property : X_PROPERTY { $$ = 0; } | Y_PROPERTY { $$ = 1; } | XSCALE_PROPERTY { $$ = 2; } | YSCALE_PROPERTY { $$ = 3; } | CURRENTFRAME_PROPERTY { $$ = 4; } | TOTALFRAMES_PROPERTY { $$ = 5; } | ALPHA_PROPERTY { $$ = 6; } | VISIBLE_PROPERTY { $$ = 7; } | WIDTH_PROPERTY { $$ = 8; } | HEIGHT_PROPERTY { $$ = 9; } | ROTATION_PROPERTY { $$ = 10; } | TARGET_PROPERTY { $$ = 11; } | FRAMESLOADED_PROPERTY { $$ = 12; } | NAME_PROPERTY { $$ = 13; } | DROPTARGET_PROPERTY { $$ = 14; } | URL_PROPERTY { $$ = 15; } | HIGHQUALITY_PROPERTY { $$ = 16; } | FOCUSRECT_PROPERTY { $$ = 17; } | SOUNDBUFTIME_PROPERTY { $$ = 18; } | QUALITY_PROPERTY { $$ = 19; } | XMOUSE_PROPERTY { $$ = 20; } | YMOUSE_PROPERTY { $$ = 21; } ; push_list : push_item { $$ = $1; } | push_list ',' push_item { $$ += $3; } ; constant_list : STRING { addConstant($1); } | constant_list ',' STRING { addConstant($3); } ; constant_list_opt : /* empty */ { } | constant_list { } ; hex_list : HEX { if (xtoi($1)>0xff) yyerror("Action data must be a byte list"); $$ = writeByte((char)xtoi($1)); } | hex_list ',' HEX { if (xtoi($3)>0xff) yyerror("Action data must be a byte list"); $$ += writeByte((char)xtoi($3)); } ; hexlist_opt : /* empty */ { $$ = 0; } | HEXDATA hex_list { $$ = $2; } ; urlmethod : /* empty */ { $$ = 0; } | GET { $$ = 1; } | POST { $$ = 2; } ; opcode : CONSTANTPOOL { nConstants = 0; } constant_list_opt { $$ = writeConstants(); } | PUSH { $$ = writeByte(SWFACTION_PUSHDATA); /* length */ $$ += writeShort(0); } push_list { $$ = $2 + $3; patchLength($3, $3); } | SWFACTION HEX { if (xtoi($2)>0xff) yyerror("Action code out of range"); $$ = writeByte((char)xtoi($2)); if (xtoi($2)>=0x80) /* length */ $$ += writeShort(0); } hexlist_opt { if (($4>0) && (xtoi($2)>=0x80)) patchLength($4, $4); $$ = $3 + $4; } | SETREGISTER register { $$ = writeByte(SWFACTION_SETREGISTER); $$ += writeShort(1); $$ += writeByte((byte)$2); } | STRICTEQUALS { $$ = writeByte(SWFACTION_STRICTEQUALS); } | GREATERTHAN { $$ = writeByte(SWFACTION_GREATERTHAN); } | ENUMERATEVALUE { $$ = writeByte(SWFACTION_ENUMERATEVALUE); } | INSTANCEOF { $$ = writeByte(SWFACTION_INSTANCEOF); } | NEXTFRAME { $$ = writeByte(SWFACTION_NEXTFRAME); } | PREVFRAME { $$ = writeByte(SWFACTION_PREVFRAME); } | PLAY { $$ = writeByte(SWFACTION_PLAY); } | STOP { $$ = writeByte(SWFACTION_STOP); } | TOGGLEQUALITY { $$ = writeByte(SWFACTION_TOGGLEQUALITY); } | STOPSOUNDS { $$ = writeByte(SWFACTION_STOPSOUNDS); } | CALLFUNCTION { $$ = writeByte(SWFACTION_CALLFUNCTION); } | RETURN { $$ = writeByte(SWFACTION_RETURN); } | NEWMETHOD { $$ = writeByte(SWFACTION_NEWMETHOD); } | CALLMETHOD { $$ = writeByte(SWFACTION_CALLMETHOD); } | BITWISEAND { $$ = writeByte(SWFACTION_BITWISEAND); } | BITWISEOR { $$ = writeByte(SWFACTION_BITWISEOR); } | BITWISEXOR { $$ = writeByte(SWFACTION_BITWISEXOR); } | MODULO { $$ = writeByte(SWFACTION_MODULO); } | NEWADD { $$ = writeByte(SWFACTION_NEWADD); } | NEWLESSTHAN { $$ = writeByte(SWFACTION_NEWLESSTHAN); } | NEWEQUALS { $$ = writeByte(SWFACTION_NEWEQUALS); } | TONUMBER { $$ = writeByte(SWFACTION_TONUMBER); } | TOSTRING { $$ = writeByte(SWFACTION_TOSTRING); } | INCREMENT { $$ = writeByte(SWFACTION_INCREMENT); } | DECREMENT { $$ = writeByte(SWFACTION_DECREMENT); } | TYPEOF { $$ = writeByte(SWFACTION_TYPEOF); } | TARGETPATH { $$ = writeByte(SWFACTION_TARGETPATH); } | ENUMERATE { $$ = writeByte(SWFACTION_ENUMERATE); } | DELETE { $$ = writeByte(SWFACTION_DELETE); } | DELETE2 { $$ = writeByte(SWFACTION_DELETE2); } | NEW { $$ = writeByte(SWFACTION_NEW); } | INITARRAY { $$ = writeByte(SWFACTION_INITARRAY); } | INITOBJECT { $$ = writeByte(SWFACTION_INITOBJECT); } | GETMEMBER { $$ = writeByte(SWFACTION_GETMEMBER); } | SETMEMBER { $$ = writeByte(SWFACTION_SETMEMBER); } | SHIFTLEFT { $$ = writeByte(SWFACTION_SHIFTLEFT); } | SHIFTRIGHT { $$ = writeByte(SWFACTION_SHIFTRIGHT); } | SHIFTRIGHT2 { $$ = writeByte(SWFACTION_SHIFTRIGHT2); } | VAR { $$ = writeByte(SWFACTION_VAR); } | VAREQUALS { $$ = writeByte(SWFACTION_VAREQUALS); } | OLDADD { $$ = writeByte(SWFACTION_ADD); } | SUBTRACT { $$ = writeByte(SWFACTION_SUBTRACT); } | MULTIPLY { $$ = writeByte(SWFACTION_MULTIPLY); } | DIVIDE { $$ = writeByte(SWFACTION_DIVIDE); } | OLDEQUALS { $$ = writeByte(SWFACTION_EQUALS); } | OLDLESSTHAN { $$ = writeByte(SWFACTION_LESSTHAN); } | FSCOMMAND2 { $$ = writeByte(SWFACTION_FSCOMMAND2); } | LOGICALAND { $$ = writeByte(SWFACTION_LOGICALAND); } | LOGICALOR { $$ = writeByte(SWFACTION_LOGICALOR); } | LOGICALNOT LOGICALNOT { if (mode >= MODE_UPDATE) { /* strip double nots */ $$ = 0; numActions -= 2; } else { $$ = writeByte(SWFACTION_LOGICALNOT); $$ += writeByte(SWFACTION_LOGICALNOT); } } | LOGICALNOT { $$ = writeByte(SWFACTION_LOGICALNOT); } | STRINGEQ { $$ = writeByte(SWFACTION_STRINGEQ); } | STRINGLENGTH { $$ = writeByte(SWFACTION_STRINGLENGTH); } | SUBSTRING { $$ = writeByte(SWFACTION_SUBSTRING); } | INT { $$ = writeByte(SWFACTION_INT); } | DUP { $$ = writeByte(SWFACTION_DUP); } | SWAP { $$ = writeByte(SWFACTION_SWAP); } | POP { $$ = writeByte(SWFACTION_POP); } | GETVARIABLE { $$ = writeByte(SWFACTION_GETVARIABLE); } | SETVARIABLE { $$ = writeByte(SWFACTION_SETVARIABLE); } | STRINGCONCAT { $$ = writeByte(SWFACTION_STRINGCONCAT); } | GETPROPERTY { $$ = writeByte(SWFACTION_GETPROPERTY); } | SETPROPERTY { $$ = writeByte(SWFACTION_SETPROPERTY); } | DUPLICATECLIP { $$ = writeByte(SWFACTION_DUPLICATECLIP); } | REMOVECLIP { $$ = writeByte(SWFACTION_REMOVECLIP); } | TRACE { $$ = writeByte(SWFACTION_TRACE); } | STARTDRAGMOVIE { $$ = writeByte(SWFACTION_STARTDRAGMOVIE); } | STOPDRAGMOVIE { $$ = writeByte(SWFACTION_STOPDRAGMOVIE); } | STRINGLESSTHAN { $$ = writeByte(SWFACTION_STRINGLESSTHAN); } | STRINGGREATERTHAN { $$ = writeByte(SWFACTION_STRINGGREATERTHAN);} | RANDOM { $$ = writeByte(SWFACTION_RANDOM); } | MBLENGTH { $$ = writeByte(SWFACTION_MBLENGTH); } | ORD { $$ = writeByte(SWFACTION_ORD); } | CHR { $$ = writeByte(SWFACTION_CHR); } | GETTIMER { $$ = writeByte(SWFACTION_GETTIMER); } | MBSUBSTRING { $$ = writeByte(SWFACTION_MBSUBSTRING); } | MBORD { $$ = writeByte(SWFACTION_MBORD); } | MBCHR { $$ = writeByte(SWFACTION_MBCHR); } | IMPLEMENTS { $$ = writeByte(SWFACTION_IMPLEMENTS); } | EXTENDS { $$ = writeByte(SWFACTION_EXTENDS); } | THROW { $$ = writeByte(SWFACTION_THROW); } | CAST { $$ = writeByte(SWFACTION_CAST); } | CALLFRAME { $$ = writeByte(SWFACTION_CALLFRAME); $$ += writeShort(0); } | GOTOANDSTOP { $$ = writeByte(SWFACTION_GOTOEXPRESSION); $$ += writeShort(1); $$ += writeByte(0); } | GOTOANDSTOP SKIP INTEGER { $$ = writeByte(SWFACTION_GOTOEXPRESSION); $$ += writeShort(3); $$ += writeByte(2); $$ += writeShort($3); } | GOTOANDPLAY { $$ = writeByte(SWFACTION_GOTOEXPRESSION); $$ += writeShort(1); $$ += writeByte(1); } | GOTOANDPLAY SKIP INTEGER { $$ = writeByte(SWFACTION_GOTOEXPRESSION); $$ += writeShort(3); $$ += writeByte(3); $$ += writeShort($3); } | GOTOLABEL STRING { $$ = writeByte(SWFACTION_GOTOLABEL); $$ += writeShort(strlen($2)+1); $$ += writeString($2); } | BRANCHALWAYS STRING { $$ = writeByte(SWFACTION_BRANCHALWAYS); $$ += writeShort(2); $$ += branchTarget($2); } | BRANCHALWAYS INTEGER { $$ = writeByte(SWFACTION_BRANCHALWAYS); $$ += writeShort(2); $$ += addNumLabel($2); } | BRANCHIFTRUE STRING { $$ = writeByte(SWFACTION_BRANCHIFTRUE); $$ += writeShort(2); $$ += branchTarget($2); } | BRANCHIFTRUE INTEGER { $$ = writeByte(SWFACTION_BRANCHIFTRUE); $$ += writeShort(2); $$ += addNumLabel($2); } | GOTOFRAME INTEGER { $$ = writeByte(SWFACTION_GOTOFRAME); $$ += writeShort(2); $$ += writeShort($2); } | GETURL STRING STRING { $$ = writeByte(SWFACTION_GETURL); $$ += writeShort(strlen($2)+strlen($3)+2); $$ += writeString($2); $$ += writeString($3); } | GETURL2 urlmethod { $$ = writeByte(SWFACTION_GETURL2); $$ += writeShort(1); $$ += writeByte($2); } | LOADVARIABLES urlmethod { $$ = writeByte(SWFACTION_GETURL2); $$ += writeShort(1); $$ += writeByte(0xc0 + $2); } | LOADVARIABLESNUM urlmethod { $$ = writeByte(SWFACTION_GETURL2); $$ += writeShort(1); $$ += writeByte(0x80 + $2); } | LOADMOVIE urlmethod { $$ = writeByte(SWFACTION_GETURL2); $$ += writeShort(1); $$ += writeByte(0x40 + $2); } | LOADMOVIENUM urlmethod { $$ = writeByte(SWFACTION_GETURL2); $$ += writeShort(1); $$ += writeByte($2); } | STRICTMODE ON { $$ = writeByte(SWFACTION_STRICTMODE); $$ += writeShort(1); $$ += writeByte(1); } | STRICTMODE OFF { $$ = writeByte(SWFACTION_STRICTMODE); $$ += writeShort(1); $$ += writeByte(0); } | MOVIE STRING { yyerror("Movie declaration inside of the action block"); } ; flasm-1.62/flasm.ini0000666000175000017500000000337610124267635013026 0ustar pabspabs# show (or not) action offsets in disassembly # 0: no offsets # 1: relative offsets from the start of action block # 2: absolute offsets from the start of SWF showoffset = 0 # 0: show offsets above in decimal form, 1: in hexadecimal form hexoffset = 0 # show literal registers for function2 arguments # i.e. r:arg1 instead of r:2 literalregisters = 1 # delete function2 argument names from SWF in -u mode # if these arguments are stored in registers, their names are useless clearregisterargs = 0 # 0: constant pool members will be shown as c:0, c:1, ... # 1: constant pool members will be shown as normal strings when possible # 2: always show as strings, even if there are multiple or broken pools # 2: resembles the behavior of Flasm's < 1.52, may be inaccurate literalconstants = 1 # to log errors in a file instead of printing to the console # uncomment the next line and adjust the path #logto = "c:\FlasmErrors.log" # if file doesn't exist, it will be created # logmode controls how the log file is written # 0: append messages to the end of the file # 1: overwrite log file every time #logmode = 0 # 0: Flasm -b will print __bytecode__ instruction # 1: Flasm -b will print byte sequence boutput = 0 # these options control what happens when Flasm is called from Flash IDE # Flash 5 and Flash MX only, see "Embedding Flasm code" in flasm.html # flaplayer: path to flash player # flabrowser: path to flash browser # on windows, path should contain no spaces, therefore DOS format # flatest: if set to flaplayer, calls the player after update # flatest: if set to flabrowser, calls the browser after update flaplayer = C:\PROGRA~1\FLASHM~1\PLAYERS\SAFLASHPLAYER.EXE flabrowser = C:\PROGRA~1\INTERN~1\IEXPLORE.EXE flatest = flabrowser flasm-1.62/flasm.c0000666000175000017500000012602410632601625012460 0ustar pabspabs/* flasm, command line assembler & disassembler of flash actionscript bytecode Copyright (c) 2001 Opaque Industries, (c) 2002-2007 Igor Kogan, (c) 2005 Wang Zhen All rights reserved. See LICENSE.TXT for terms of use. */ #include #include #include #include #include #include #include #include #ifdef MEMWATCH #include "memwatch.h" #endif #ifdef __MINGW32__ #include #include #endif #ifdef __APPLE__ #include #endif #include "zlib.h" #include "util.h" #include "flasm.h" extern FILE *yyin; extern void skipProtected(FILE * f, unsigned long int length); extern void disassembleSWF(FILE * in, char *fname); int swfVersion; char *inputName; /* actual command-line parameter */ static char *updateName = NULL; /* swf file name found in disassembly's first line (movie foo.swf...) */ static char *tempName = NULL; /* temporary file during assembling */ static char *flmName = NULL; /* temporary disassembly during update */ static char *backupName = NULL; /* foo.$wf if foo.swf is updated */ static FILE *updateFile = NULL; static FILE *tempFile = NULL; static FILE *inputFile = NULL; static FILE *logFile = NULL; static char swfHeader[4]; static long int flength = 0; static char backupCreated = 0; char wasCompressed = 0; char compressAfter = 0; int mode; /* ini entries */ int showoffset = 0, hexoffset = 0, boutput = 0, literalconstants = 0, literalregisters = 1, clearregisterargs = 1, logmode = 0; static char *logto = NULL, *flaplayer = NULL, *flabrowser = NULL, *flatest = NULL; static char flapath[MAX_PATH_LEN]; void yyerror(char *s); void warning(char *s); int yyparse(void); static byte *output = NULL; static void decompressSWF(FILE * f, char *fname); static void compressSWF(FILE * f, char *fname); static long int defineMCLengthPos = 0; static long int buttonLengthPos = 0; static long int buttonLastOffsetPos = 0; static long int placeMCLengthPos = 0; static long int MCAllEventsPos = 0; static long int len; static unsigned int nLabels = 0; struct _label { char *name; /* offset >=0 if actual label definition, -1 if predefinition from branch */ long int offset; }; static struct _label *labels = NULL; static void waitUserInput(void) { #ifdef __MINGW32__ /* make sure we have kbhit(), otherwise do nothing */ fprintf(stderr, "Hit any key to continue.."); while (!kbhit()) { } #endif } unsigned long int nStrings = 0; char **aStrings = NULL; static void mexit(int msg) { unsigned long int i; for (i = 0; i < nStrings; ++i) free(aStrings[i]); if (output != NULL) free(output); if (aStrings != NULL) free(aStrings); if (nLabels > 0 && labels != NULL) free(labels); exit(msg); } void tellUser(int isError, char *s, ...) { va_list ap; static int usestderr = 1; static int firsttime = 1; if (logto != NULL && firsttime == 1) { firsttime = 0; if (logmode == 0) logFile = fopen(logto, "ab"); else logFile = fopen(logto, "wb"); if (logFile == NULL) fprintf(stderr, "Couldn't write to log file %s\nUsing stderr instead\n", logto); else { usestderr = 0; if (logmode == 0) { time_t now; time(&now); fprintf(logFile, "________________________\n%.24s\n\n", ctime(&now)); } } } if (s != NULL && strlen(s) > 0) { va_start(ap, s); if (usestderr) { vfprintf(stderr, s, ap); fputc('\n', stderr); } else { vfprintf(logFile, s, ap); fputc('\n', logFile); } va_end(ap); } if (isError) { if (mode >= MODE_IDE && usestderr) waitUserInput(); if (tempFile != NULL) { fclose(tempFile); remove(tempName); } if (updateFile != NULL) fclose(updateFile); if (wasCompressed && backupCreated && updateName != NULL && access(backupName, R_OK) == 0) { remove(updateName); rename(backupName, updateName); } if (logFile != NULL) fclose(logFile); mexit(EXIT_FAILURE); } } char *mstrdup(const char *String) { SUREALLOC(nStrings, aStrings, sizeof (char *)); if (String == NULL) { if ((aStrings[nStrings] = malloc(256)) == NULL) tellUser(1, "Not enough memory to allocate a string"); } else { size_t slen = strlen(String); if ((aStrings[nStrings] = malloc(slen + 1)) != NULL) memcpy(aStrings[nStrings], String, slen + 1); else tellUser(1, "Not enough memory to allocate a string"); } return aStrings[nStrings++]; } static unsigned int newLabel(char *name, long int offset) { unsigned int i = nLabels; /* first search if label exists, and update offset if needed */ if (name != NULL && nLabels>0) { do { i--; if (labels[i].name != NULL && strcmp(name, labels[i].name) == 0) { if (offset >= 0) { if (labels[i].offset == -1) labels[i].offset = offset; else yyerror("Label defined twice"); } return i; } } while (i>0); } SUREALLOC(nLabels, labels, sizeof (struct _label)); labels[nLabels].name = name; labels[nLabels].offset = offset; return (nLabels++); } void addLabel(char *label) { newLabel(label, len); } int branchTarget(char *label) { return writeShort(newLabel(label, -1)); } int addNumLabel(int numLabel) { /* numerical offset given in a branch */ if (len + numLabel + 2 < 0) yyerror("Branch target out of range"); return writeShort(newLabel(NULL, len + numLabel + 2)); } int writeByte(byte num) { if (len % OUTPUT_INC == 0) output = realloc(output, len + OUTPUT_INC); output[len++] = num; return 1; } int writeShort(unsigned int num) { writeByte(num & 0xff); writeByte((num >> 8) & 0xff); return 2; } int writeFloat(float num) { byte *p = (byte *) # if (byteorder == FLASM_BIG_ENDIAN) { writeByte(p[3]); writeByte(p[2]); writeByte(p[1]); writeByte(p[0]); } else { writeByte(p[0]); writeByte(p[1]); writeByte(p[2]); writeByte(p[3]); } return 4; } int writeDouble(double num) { byte *p = (byte *) # if (byteorder == FLASM_BIG_ENDIAN) { writeByte(p[3]); writeByte(p[2]); writeByte(p[1]); writeByte(p[0]); writeByte(p[7]); writeByte(p[6]); writeByte(p[5]); writeByte(p[4]); } else { writeByte(p[4]); writeByte(p[5]); writeByte(p[6]); writeByte(p[7]); writeByte(p[0]); writeByte(p[1]); writeByte(p[2]); writeByte(p[3]); } return 8; } int writeLongInt(long int num) { byte *p = (byte *) # if (byteorder == FLASM_BIG_ENDIAN) { writeByte(p[3]); writeByte(p[2]); writeByte(p[1]); writeByte(p[0]); } else { writeByte(p[0]); writeByte(p[1]); writeByte(p[2]); writeByte(p[3]); } return 4; } int writeString(char *str) { int i; for (i = 0; str[i] != '\0'; ++i) writeByte((byte) str[i]); writeByte(0); return i + 1; } unsigned int nConstants = 0; static char *constants[MAX_CONSTANTS]; void addConstant(char *str) { constants[nConstants] = str; ++nConstants; if (nConstants > MAX_CONSTANTS) yyerror("Too many constants"); } unsigned int writeConstants(void) { unsigned int i; unsigned long int clen = 2; writeByte(SWFACTION_CONSTANTPOOL); /* length */ writeShort(0); writeShort(nConstants); for (i = 0; i < nConstants; ++i) clen += writeString(constants[i]); if (clen < 65533) patchLength((unsigned int) clen, (unsigned int) clen); else tellUser(1, "Constant pool exceeds 64k"); return (unsigned int) clen + 3; } unsigned int writePushString(char *str) { unsigned int i; for (i = 0; i < nConstants; ++i) { if (strcmp(constants[i], str) == 0) break; } if (i < nConstants) { if (i < 256) { /* constant, 1-byte reference */ writeByte(0x08); writeByte((byte) i); return 2; } else { /* constant, 2-byte reference */ writeByte(0x09); writeShort(i); return 3; } } /* string */ writeByte(0); return writeString(str) + 1; } static void patchTargets(void) { long int i = 0; while (i < len) { Action op = (Action) output[i++]; if (op & 0x80) { unsigned int blocklen = S16(output + i); i += 2; if (op == SWFACTION_BRANCHALWAYS || op == SWFACTION_BRANCHIFTRUE) { long int offset; unsigned int target = S16(output+i); if (labels[target].offset < 0) { char msg[256] = "Label not found: "; yyerror(strcat(msg, labels[target].name)); } offset = labels[target].offset - (i + 2); if (offset > 32767 || offset < -32768) { char msg[256] = "Label too far away from branch: "; yyerror(strcat(msg, labels[target].name)); } output[i] = (byte) (offset & 0xff); output[i+1] = (byte) ((offset >> 8) & 0xff); } i += blocklen; } } } static int flput(char b) { ++flength; return fputc(b, tempFile); } static void flputShort(unsigned int w) { fputc(0xff & w, tempFile); fputc(0xff & (w >> 8), tempFile); flength += 2; } static void flputLong(unsigned long int l) { fputc(0xff & l, tempFile); fputc(0xff & (l >> 8), tempFile); fputc(0xff & (l >> 16), tempFile); fputc(0xff & (l >> 24), tempFile); flength += 4; } static void flputString(char *s) { fwrite(s, strlen(s)+1, 1, tempFile); flength += strlen(s)+1; } static int dupByte(void) { return flput(fgetc(updateFile)); } static unsigned int dupWord(void) { int b1, b2; b1 = fgetc(updateFile); flput(b1); b2 = fgetc(updateFile); flput(b2); return ((unsigned int) b1 + ((unsigned int) b2 << 8)); } static unsigned long int dupLong(void) { return ((unsigned long int) dupWord() + ((unsigned long int) dupWord() << 16)); } static void dupBuffered(unsigned long int length) { static byte dupBuffer[MAX_BUFFER]; flength += length; while (length > MAX_BUFFER) { if (fread(dupBuffer, 1, MAX_BUFFER, updateFile) == MAX_BUFFER) { fwrite(dupBuffer, 1, MAX_BUFFER, tempFile); length -= MAX_BUFFER; } else yyerror("Couldn't update because of unexpected SWF structure"); } if (length > 0) { if (fread(dupBuffer, 1, length, updateFile) == length) fwrite(dupBuffer, 1, length, tempFile); else yyerror("Couldn't update because of unexpected SWF structure"); } } void patchLength(unsigned int back, unsigned int blen) { output[len - back - 1] = (blen >> 8) & 0xff; output[len - back - 2] = blen & 0xff; } void patchFlag(unsigned int back, byte flag) { output[len - back - 1] = flag; } void patchFrameLoaded(unsigned int back, int numActions) { if (numActions > 0 || numActions < 256) output[len - back - 1] = (byte) numActions; else yyerror("IfFrameLoaded block larger then 255 bytes is not allowed"); } static void updateTmpLength(long int lenpos, long int reallen) { long int curpos; curpos = ftell(tempFile); fseek(tempFile, lenpos, SEEK_SET); /* update long length */ /* dont use flput to not affect the file size calculation */ fputc((reallen & 0xff), tempFile); fputc((reallen >> 8) & 0xff, tempFile); fputc((reallen >> 16) & 0xff, tempFile); fputc((reallen >> 24) & 0xff, tempFile); fseek(tempFile, curpos, SEEK_SET); } static void updateShort(long int lenpos, unsigned int reallen) { long int curpos = ftell(tempFile); fseek(tempFile, lenpos, SEEK_SET); /* update short length */ /* dont use flput to not affect the file size calculation */ fputc(reallen & 0xff, tempFile); fputc((reallen >> 8) & 0xff, tempFile); fseek(tempFile, curpos, SEEK_SET); } static void flushOutput(void) { patchTargets(); if (fwrite(output, 1, (size_t) len, tempFile) != (size_t) len) tellUser(1, "Error writing action block"); flength += len; len = 0; if (nLabels > 0) { nLabels = 0; free(labels); labels = NULL; } nConstants = 0; } static void writeTagHeader(unsigned int type, unsigned long int length) { if (length >= 63 || type == TAG_DEFINEBITSLOSSLESS || type == TAG_DEFINEBITSLOSSLESS2 || type == TAG_SOUNDSTREAMBLOCK || type == TAG_DEFINEBITS || type == TAG_DEFINEBITSJPEG2 || type == TAG_DEFINEBITSJPEG3) { /* long length, and also workaround for a really strange bug in flash player: the above tags must always have long length to work */ flputShort((type << 6) + 63); flputLong(length); } else { /* short length */ flputShort((type << 6) + length); } } static int handledTag(unsigned int tag) { return (tag == TAG_PROTECT || tag == TAG_ENABLEDEBUGGER || tag == TAG_ENABLEDEBUGGER2 || tag == TAG_SCRIPTLIMITS || tag == TAG_FILEATTRIBUTES || tag == TAG_METADATA); } static unsigned long int findNextTag(unsigned int typeneeded) { /* dumps all tags of the updateFile to the tempFile until required tag is found, returns its length */ unsigned int type; unsigned long int length; while (!feof(updateFile)) { parseTagHeader(updateFile, &type, &length); if (type == typeneeded) return length; if (handledTag(type)) skipProtected(updateFile, length); else { writeTagHeader(type, length); dupBuffered(length); } } tellUser(1, "Couldn't update because of unexpected SWF structure:\nunexpected end of SWF looking for Tag %04x", typeneeded); return (0); } static unsigned long int findNextTags(unsigned int *typefound, int numtypes, ...) { /* dumps all blocks of the updateFile to the tempFile until one of the required tags is found, returns its length */ va_list typelist; unsigned int type; unsigned long int length; int i; while (!feof(updateFile)) { parseTagHeader(updateFile, &type, &length); va_start(typelist, numtypes); for (i = 1; i <= numtypes; i++) { if (type == va_arg(typelist, unsigned int)) { *typefound = type; va_end(typelist); return length; } } va_end(typelist); if (handledTag(type)) skipProtected(updateFile, length); else { writeTagHeader(type, length); dupBuffered(length); } } tellUser(1, "Couldn't update because of unexpected SWF structure:\nunexpected end of SWF."); return (0); } void writeDoAction() { unsigned long int oldLength = findNextTag(TAG_DOACTION); /* skip old action */ skipProtected(updateFile, oldLength); writeTagHeader(TAG_DOACTION, len); /* write bison output from buffer to file and empty buffer */ flushOutput(); } void writeInitMC(unsigned int clipID) { unsigned int oldID; unsigned long int oldLength = findNextTag(TAG_INITMOVIECLIP); writeTagHeader(TAG_INITMOVIECLIP, len + 2); oldID = dupWord(); if (oldID != clipID) tellUser(1, "Couldn't update because of unexpected SWF structure:\nold initMovieClip ID %u doesn't match new ID %u", oldID, clipID); skipProtected(updateFile, oldLength - 2); flushOutput(); } static unsigned int bitPos; static unsigned int bitBuf; static unsigned int dupBits(unsigned int n) { unsigned long int v = 0; while (n > bitPos) { n -= bitPos; v |= bitBuf << n; bitBuf = (unsigned int) dupByte(); bitPos = 8; } bitPos -= n; v |= bitBuf >> bitPos; bitBuf &= 0xff >> (8 - bitPos); /* never need more than 16 bits */ return (unsigned int) v; } static void dupMatrix(void) { bitPos = 8; bitBuf = (unsigned int) dupByte(); if (dupBits(1)) dupBits(dupBits(5) * 2); if (dupBits(1)) dupBits(dupBits(5) * 2); dupBits(dupBits(5) * 2); } static void dupColorTransform(void) { unsigned int needAdd, needMul, nBits; bitPos = 8; bitBuf = (unsigned int) dupByte(); needAdd = dupBits(1); needMul = dupBits(1); nBits = dupBits(4); if (needMul) dupBits(nBits * 4); if (needAdd) dupBits(nBits * 4); } static void dupFilters(void) { byte numfilters = dupByte(); while(numfilters--) switch(dupByte()){ case FILTER_DROPSHADOW: dupBuffered(23); break; case FILTER_BLUR: dupBuffered(9); break; case FILTER_GLOW: dupBuffered(15); break; case FILTER_BEVEL: dupBuffered(27); break; case FILTER_GRADIENTGLOW: dupBuffered(dupByte()*5 + 19); break; case FILTER_ADJUSTCOLOR: dupBuffered(80); break; case FILTER_GRADIENTBEVEL: dupBuffered(dupByte()*5 + 19); break; default: tellUser(1, "Unknown filter"); } } void writePlaceMCStart(unsigned int clipID) { unsigned int oldID = 0; unsigned int typefound; unsigned int flags; unsigned long int length, eventLength; /* skip all mcs without onClipEvent actions */ do { length = findNextTags(&typefound, 2, TAG_PLACEOBJECT2, TAG_PLACEOBJECT3); if (typefound == TAG_PLACEOBJECT2) { flags = fgetc(updateFile); if (flags & PF_ONCLIPEVENTS) break; writeTagHeader(TAG_PLACEOBJECT2, length); flput(flags); /* copy the rest of the placeobject2 */ dupBuffered(length - 1); } else { flags = getWord(updateFile); if (flags & PF_ONCLIPEVENTS) break; writeTagHeader(TAG_PLACEOBJECT3, length); flputShort(flags); /* copy the rest of the placeobject3 */ dupBuffered(length - 2); } } while (1); /* placemc header, temporary long length, save length pos */ writeTagHeader(typefound, 0xFF); placeMCLengthPos = ftell(tempFile) - 4; if (typefound == TAG_PLACEOBJECT3) flputShort(flags); else flput(flags); /* character depth */ dupWord(); if (flags & PF_CHARACTER) oldID = dupWord(); else tellUser(1, "Couldn't update because of unexpected SWF structure:\nold placeMovieClip ID missing, new ID %u", clipID); if (oldID != clipID) tellUser(1, "Couldn't update because of unexpected SWF structure:\nold placeMovieClip ID %u doesn't match new ID %u", oldID, clipID); if (flags & PF_MATRIX) dupMatrix(); if (flags & PF_COLORTRANSFORM) dupColorTransform(); if (flags & PF_RATIO) dupWord(); if (flags & PF_NAME) while (dupByte() != 0); if (flags & PF_DEFINECLIP) dupWord(); if (flags & PF_FILTERS) dupFilters(); if (flags & PF_BLENDMODE) dupByte(); if (flags & PF_BITMAPCACHING) dupByte(); /* reserved: always 0 */ dupWord(); /* save the pos for logical or of all events */ MCAllEventsPos = ftell(tempFile); /* skip old events */ if (swfVersion <= 5) { /* dup logical or of all events - temporary */ dupWord(); while (getWord(updateFile) > 0) { eventLength = getDoubleWord(updateFile); skipProtected(updateFile, eventLength); } } else if (swfVersion >= 6) { /* dup logical or of all events - temporary */ dupLong(); while (getDoubleWord(updateFile) > 0) { eventLength = getDoubleWord(updateFile); skipProtected(updateFile, eventLength); } } } void writePlaceMCEnd(unsigned long int flags) { /* write empty event */ flputShort(0); if (swfVersion >= 6) flputShort(0); updateTmpLength(placeMCLengthPos, ftell(tempFile) - placeMCLengthPos - 4); if (swfVersion <= 5) { updateShort(MCAllEventsPos, (unsigned int) flags); } else if (swfVersion >= 6) { updateShort(MCAllEventsPos, (unsigned int) (flags & 0xFFFF)); updateShort(MCAllEventsPos + 2, (unsigned int) ((flags >> 16) & 0xFFFF)); } placeMCLengthPos = 0; MCAllEventsPos = 0; } void writeOnClipEvent(unsigned long int flag) { /* write event flag */ flputShort(flag); if (swfVersion >= 6) flputShort((unsigned int) (flag >> 16)); /* write action length */ flputLong(len); flushOutput(); } void writeButtonStart(unsigned int buttonID) { int trackAsMenu; unsigned int oldID, actionOffset; unsigned long int length; /* skip all buttons without actions */ do { length = findNextTag(TAG_DEFINEBUTTON2); oldID = getWord(updateFile); trackAsMenu = fgetc(updateFile); actionOffset = getWord(updateFile); if (actionOffset > 0) break; writeTagHeader(TAG_DEFINEBUTTON2, length); flputShort(oldID); flput(trackAsMenu); /* put action offset = 0 */ flputShort(0); /* copy the rest of the button */ dupBuffered(length - 5); } while(1); if (oldID != buttonID) tellUser(1, "Couldn't update because of unexpected SWF structure:\nold defineButton ID %u doesn't match new ID %u", oldID, buttonID); /* defineButton2 header, temporary long length, save length pos */ writeTagHeader(TAG_DEFINEBUTTON2, 0xFF); buttonLengthPos = ftell(tempFile) - 4; flputShort(oldID); flput(trackAsMenu); flputShort(actionOffset); /* copy button visual part */ dupBuffered(actionOffset - 2); /* skip old button actions part */ skipProtected(updateFile, length - 3 - actionOffset); } void writeButtonEnd(void) { updateTmpLength(buttonLengthPos, ftell(tempFile) - buttonLengthPos - 4); /* last offset is always 0 */ updateShort(buttonLastOffsetPos, 0); buttonLengthPos = 0; buttonLastOffsetPos = 0; } void writeButtonEvent(unsigned int flags) { /* write offset to the next condition */ buttonLastOffsetPos = ftell(tempFile); flputShort(len+4); /* write button event flags */ flputShort(flags); flushOutput(); } void writeDefineMCStart(unsigned int clipID) { unsigned int oldID; findNextTag(TAG_DEFINEMOVIECLIP); /* defineMovieClip header, temporary long length, save length pos */ writeTagHeader(TAG_DEFINEMOVIECLIP, 0xFF); defineMCLengthPos = ftell(tempFile) - 4; oldID = dupWord(); if (oldID != clipID) tellUser(1, "Couldn't update because of unexpected SWF structure:\nold defineMovieClip ID %u doesn't match new ID %u", oldID, clipID); /* frames total */ dupWord(); } void writeDefineMCEnd(void) { /* dump the rest of movie clip */ findNextTag(TAG_END); /* write movie clip end tag */ flputShort(0); updateTmpLength(defineMCLengthPos, ftell(tempFile) - defineMCLengthPos - 4); defineMCLengthPos = 0; } void writeScriptLimits(unsigned int recursion, unsigned int timeout) { writeTagHeader(TAG_SCRIPTLIMITS, 4); flputShort(recursion); flputShort(timeout); } void writeProtect(char *str) { if (strlen(str) == 0) { /* no password */ writeTagHeader(TAG_PROTECT, 0); } else { /* password found */ writeTagHeader(TAG_PROTECT, strlen(str) + 3); flputShort(0); flputString(str); } } void writeEnableDebugger(char *str) { if (strlen(str) == 0) { /* no password, probably impossible? */ writeTagHeader(TAG_ENABLEDEBUGGER, 0); } else { /* password found */ writeTagHeader(TAG_ENABLEDEBUGGER, strlen(str) + 3); flputShort(0); flputString(str); } } void writeEnableDebugger2(char *str) { if (strlen(str) == 0) { /* no password, probably impossible? */ writeTagHeader(TAG_ENABLEDEBUGGER2, 0); } else { /* password found */ writeTagHeader(TAG_ENABLEDEBUGGER2, strlen(str) + 3); flputShort(0); flputString(str); } } void writeMetadata(char *str) { writeTagHeader(TAG_METADATA, strlen(str) + 1); flputString(str); } void writeFileAttrs(unsigned long int attrs) { writeTagHeader(TAG_FILEATTRIBUTES, 4); flputLong(attrs); } void writeImportAssets(char *from, unsigned int numAssets) { if (numAssets>0) { unsigned int typefound; skipProtected(updateFile, findNextTags(&typefound, 2, TAG_IMPORTASSETS, TAG_IMPORTASSETS2)); if (typefound == TAG_IMPORTASSETS) { writeTagHeader(TAG_IMPORTASSETS, len + strlen(from) + 3); flputString(from); flputShort(numAssets); } else { writeTagHeader(TAG_IMPORTASSETS2, len + strlen(from) + 5); flputString(from); /* Reserved: always 1 */ flputShort(1); flputShort(numAssets); } if (fwrite(output, 1, (size_t) len, tempFile) != (size_t) len) tellUser(1, "Error writing importAssets"); flength += len; } len = 0; } void writeExportAssets(unsigned int numAssets) { if (numAssets>0) { skipProtected(updateFile, findNextTag(TAG_EXPORTASSETS)); writeTagHeader(TAG_EXPORTASSETS, len+2); flputShort(numAssets); if (fwrite(output, 1, (size_t) len, tempFile) != (size_t) len) tellUser(1, "Error writing exportAssets"); flength += len; } len = 0; } static void finalizeTemporaryFile(char *name) { fclose(tempFile); if (!backupCreated) { backupName = mstrdup(name); backupCreated = 1; strcpy(backupName + strlen(backupName) - 4, ".$wf"); /* remove evtl. existing previous backup foo.$wf */ remove(backupName); /* foo.swf -> foo.$wf */ if (rename(name, backupName) != 0) tellUser(1, "couldn't update: file %s is in use", name); /* foo.tmp (assemble) or flasm.tmp (compress/decompress) -> foo.swf */ rename(tempName, name); } else { /* backup already here, just make temp file our final file */ if (remove(name) != 0) tellUser(1, "couldn't update: file %s is in use", name); rename(tempName, name); } } static void getSWFHeader(FILE * f) { fread(swfHeader, 1, 3, f); swfHeader[3] = '\0'; } void startUpdate(char *outputName) { int b, i, bitstotal; if ((updateFile = fopen(outputName, "rb")) == NULL) tellUser(1, "Couldn't open file %s for update", outputName); getSWFHeader(updateFile); if (strcmp(swfHeader, "FWS") != 0) { if (strcmp(swfHeader, "CWS") == 0) { /* decompresses foo.swf, foo.$wf backup is created */ decompressSWF(updateFile, outputName); updateFile = fopen(outputName, "rb"); /* now we are just after the header in decompressed file */ getSWFHeader(updateFile); } else tellUser(1, "Input file %s doesn't appear to be an SWF file", outputName); } flength = 0; updateName = mstrdup(outputName); tempName = mstrdup(outputName); strcpy(tempName + strlen(tempName) - 4, ".tmp"); if ((tempFile = fopen(tempName, "wb")) == NULL) tellUser(1, "Couldn't create temporary file"); /* SWF header */ flput('F'); flput('W'); flput('S'); /* version */ flput(swfVersion = fgetc(updateFile)); /* file length - temporary! */ flput(fgetc(updateFile)); flput(fgetc(updateFile)); flput(fgetc(updateFile)); flput(fgetc(updateFile)); /* movie bounds */ b = fgetc(updateFile); bitstotal = 5 + 4 * ((b & 0xf8) >> 3); flput(b); for (i = 0; i < (bitstotal + 7) / 8 - 1; ++i) flput(fgetc(updateFile)); /* frame rate and # of frames */ flput(fgetc(updateFile)); flput(fgetc(updateFile)); flput(fgetc(updateFile)); flput(fgetc(updateFile)); } void finishUpdate(void) { /* dump the rest of SWF */ findNextTag(0); /* write movie end */ flputShort(0); updateTmpLength(4, flength); fclose(updateFile); finalizeTemporaryFile(updateName); if (compressAfter) { updateFile = fopen(updateName, "rb"); getSWFHeader(updateFile); compressSWF(updateFile, updateName); } if (mode == MODE_UPDATE) tellUser(0, "%s successfully updated, %li bytes", updateName, flength); else if (mode == MODE_ASSEMBLE) tellUser(0, "%s successfully assembled to %s, %li bytes", inputName, updateName, flength); } void writeASBytecode(void) { long int n = 0; patchTargets(); if (nLabels > 0) { nLabels = 0; free(labels); labels = NULL; } if (boutput == 0) { printf("%s", "__bytecode__(\""); while(n++ < len) printf("%02x", *(output+n-1)); printf("%s", "\");\n"); } else fwrite(output, 1, len, stdout); } static void createTemporaryFile(void) { tempName = mstrdup("flasm.tmp"); if ((tempFile = fopen(tempName, "wb+")) == NULL) tellUser(1, "Couldn't create file: %s", tempName); } static void decompressSWF(FILE *f, char *fname) { z_stream stream; static byte inputBuffer[MAX_BUFFER], outputBuffer[MAX_BUFFER]; int status, count; flength = 0; createTemporaryFile(); /* SWF header */ flput('F'); flput('W'); flput('S'); /* version */ flput(fgetc(f)); flput(fgetc(f)); flput(fgetc(f)); flput(fgetc(f)); flput(fgetc(f)); stream.avail_in = 0; stream.next_in = inputBuffer; stream.next_out = outputBuffer; stream.zalloc = (alloc_func) NULL; stream.zfree = (free_func) NULL; stream.opaque = (voidpf) 0; stream.avail_out = MAX_BUFFER; status = inflateInit(&stream); if (status != Z_OK) tellUser(0, "Error %i decompressing SWF: %s\n", status, stream.msg); do { if (stream.avail_in == 0) { stream.next_in = inputBuffer; stream.avail_in = fread(inputBuffer, 1, MAX_BUFFER, f); } if (stream.avail_in == 0) break; status = inflate(&stream, Z_SYNC_FLUSH); count = MAX_BUFFER - stream.avail_out; if (count) { fwrite(outputBuffer, 1, count, tempFile); flength += count; } stream.next_out = outputBuffer; stream.avail_out = MAX_BUFFER; } while (status == Z_OK); if (status != Z_STREAM_END && status != Z_OK) tellUser(0, "Error %i decompressing SWF: %s\n", status, stream.msg); status = inflateEnd(&stream); if (status != Z_OK) tellUser(0, "Error %i decompressing SWF: %s\n", status, stream.msg); fclose(f); if (mode != MODE_DISASSEMBLE) finalizeTemporaryFile(fname); if (mode == MODE_DECOMPRESS) tellUser(0, "%s successfully decompressed, %li bytes", fname, flength); wasCompressed = 1; } static void compressSWF(FILE *f, char *fname) { z_stream stream; static byte inputBuffer[MAX_BUFFER], outputBuffer[MAX_BUFFER]; int status, count; flength = 0; createTemporaryFile(); /* SWF header */ flput('C'); flput('W'); flput('S'); /* version */ flput(fgetc(f)); flput(fgetc(f)); flput(fgetc(f)); flput(fgetc(f)); flput(fgetc(f)); stream.avail_in = 0; stream.next_out = outputBuffer; stream.avail_out = MAX_BUFFER; stream.zalloc = (alloc_func) NULL; stream.zfree = (free_func) NULL; stream.opaque = (voidpf) 0; stream.next_in = inputBuffer; status = deflateInit(&stream, Z_BEST_COMPRESSION); if (status != Z_OK) tellUser(1, "Error %i compressing SWF: %s\n", status, stream.msg); while (1) { if (stream.avail_in == 0) { stream.next_in = inputBuffer; stream.avail_in = fread(inputBuffer, 1, MAX_BUFFER, f); } if (stream.avail_in == 0) break; status = deflate(&stream, Z_NO_FLUSH); if (status != Z_OK) tellUser(1, "Error %i compressing SWF: %s\n", status, stream.msg); count = MAX_BUFFER - stream.avail_out; if (count) { fwrite(outputBuffer, 1, count, tempFile); flength += count; } stream.next_out = outputBuffer; stream.avail_out = MAX_BUFFER; } stream.next_out = outputBuffer; stream.avail_out = MAX_BUFFER; do { status = deflate(&stream, Z_FINISH); count = MAX_BUFFER - stream.avail_out; if (count) { fwrite(outputBuffer, 1, count, tempFile); flength += count; } stream.next_out = outputBuffer; stream.avail_out = MAX_BUFFER; } while (status == Z_OK); if (status != Z_STREAM_END) tellUser(1, "Error %i compressing SWF: %s\n", status, stream.msg); status = deflateEnd(&stream); if (status != Z_OK) tellUser(1, "Error %i compressing SWF: %s\n", status, stream.msg); fclose(f); finalizeTemporaryFile(fname); if (mode == MODE_COMPRESS) tellUser(0, "%s successfully compressed, %li bytes", fname, flength); } extern int yydebug; static void usage(void) { printf("\nFlasm %s build %s", FLASM_VERSION, __DATE__); // printf(" (zlib %s)", ZLIB_VERSION); puts(""); puts(""); puts("(c) 2001 Opaque Industries, (c) 2002-2007 Igor Kogan, (c) 2005 Wang Zhen"); puts("All rights reserved. See LICENSE.TXT for terms of use."); puts(""); puts("Usage: flasm [command] filename"); puts(""); puts("Commands:"); puts(" -d Disassemble SWF file to the console"); puts(" -a Assemble Flasm project (FLM)"); puts(" -u Update SWF file, replace Flasm macros"); puts(" -b Assemble actions to __bytecode__ instruction or byte sequence"); puts(" -z Compress SWF with zLib"); puts(" -x Decompress SWF"); puts(""); puts("Backups with $wf extension are created for altered SWF files."); puts(""); puts("To save disassembly or __bytecode__ to file, redirect it:"); puts("flasm -d foo.swf > foo.flm"); puts("flasm -b foo.txt > foo.as"); puts(""); puts("Read flasm.html for more information."); mexit(EXIT_FAILURE); } static void unescapePath(char *argument) { int n = 0; char esc[3]; char *s = argument; inputName = mstrdup(NULL); while ((inputName[n] = *s++) != 0) { if ((inputName[n] == '.') && (*s == 'h') && (*(s + 1) == 't') && (*(s + 2) == 'm')) /* extension found */ break; if (inputName[n] == '%') { /* unescape URL encoded char */ esc[0] = *s++; esc[1] = *s++; esc[2] = 0; inputName[n] = (char) (xtoi(esc)); } n++; } inputName[n] = '\0'; strcat(inputName, ".swf"); } static int readINI(char *exepath, char *names, char *types, ...) { /* slightly changed INITVARS from SNIPPETS collection, public domain by Raymond Gardner, Sept. 1991 */ FILE *iniFile; char ln[256], *p, *namep, *typep, name[40], *e; va_list arglist; void *argp; int k; if ((iniFile = fopen("flasm.ini", "r")) == NULL) { /* flasm.ini not found in current directory, look at executable's path */ int pathlen = strlen(exepath); char inipath[pathlen + 10], *inipathptr; if (exepath == NULL || *exepath == '\0') return -1; strcpy(inipath, exepath); inipathptr = inipath + pathlen; while (*inipathptr != '\\' && *inipathptr != '/' && *inipathptr != ':' && inipathptr>=inipath) inipathptr--; strcpy(inipathptr + 1, "flasm.ini"); if ((iniFile = fopen(inipath, "r")) == NULL) return -1; } while (fgets(ln, 256, iniFile)) { /* read ini file */ while (isspace(ln[0])) /* drop leading whitespace */ memmove(ln, ln + 1, strlen(ln)); if (ln[0] == '\0' || ln[0] == '#') /* skip if blank line or comment */ continue; p = strchr(ln, '='); /* find equal sign */ if (p == NULL) /* error if none */ tellUser(1, "Error parsing flasm.ini: %s", ln); while (p > ln && isspace(p[-1])) { /* remove whitespace before eq sign */ memmove(p - 1, p, strlen(p - 1)); --p; } *p++ = '\0'; /* plug EOS over eq sign */ while (isspace(p[0])) /* remove leading space on init string */ memmove(p, p + 1, strlen(p)); k = strlen(p) - 1; /* init string length */ if (k < 0) tellUser(1, "Error parsing flasm.ini: %s", ln); if (p[k] == '\n') p[k] = '\0'; /* plug EOS over newline */ else if (feof(iniFile)) /* '\n' is missing - last line or buffer exceeded? */ p[k + 1] = '\0'; else tellUser(1, "Line too long in flasm.ini: %s", ln); va_start(arglist, types); /* setup for arglist search */ namep = names; /* init ptr to var names */ typep = types; /* init ptr to var types */ while (*namep == ' ') /* skip blanks before namelist */ ++namep; while (*typep) { /* while any typelist items left... */ argp = (void *) va_arg(arglist, void *); /* get var arg */ k = strcspn(namep, " "); /* length of namelist entry */ memmove(name, namep, k); /* put into name hold area */ name[k] = '\0'; /* terminate it */ if (strIcmp(name, ln) != 0) { /* if it doesn't match... */ namep += k; /* get next name */ while (*namep == ' ') ++namep; ++typep; /* get next type */ } else { /* else name is found... */ if (*typep == 'i') { /* if it's an int, init it */ *(int *) argp = atoi(p); } else if (*typep == 's' || *typep == 'p') { if (*p == '"') { /* is string in quotes? */ ++p; /* skip leading quote, and */ e = strchr(p, '"'); /* look for trailing quote */ if (e) /* terminate string if found */ *e = '\0'; } if (*typep == 'p') /* if it's a char *ptr */ *(char **) argp = mstrdup(p); else /* must be char array */ strcpy(argp, p); /* copy in string */ } else tellUser(1, "Contact developer: bad argument type in readINI() call"); break; /* break search; get next line */ } } va_end(arglist); } fclose(iniFile); return 0; } static char *executablePath(void) { char path[MAX_PATH_LEN]; #ifdef __MINGW32__ if(GetModuleFileName(NULL, path, MAX_PATH_LEN - 1)) return mstrdup(path); #elif defined(__APPLE__) uint32_t pathlen = MAX_PATH_LEN - 1; if (_NSGetExecutablePath(path, &pathlen) == 0) return mstrdup(path); #else int length = readlink("/proc/self/exe", path, sizeof(path)); if (length > 0 && length < sizeof(path)) { path[length] = '\0'; return mstrdup(path); } #endif return NULL; } static void parseArgs(int argc, char *argv[]) { int ini; char *exepath; if (argv[1] == NULL) usage(); exepath = executablePath(); if (exepath == NULL) exepath = argv[0]; ini = readINI(exepath, "logto flabrowser flaplayer flatest showoffset hexoffset boutput literalconstants literalregisters clearregisterargs logmode", "ppppiiiiiii", &logto, &flabrowser, &flaplayer, &flatest, &showoffset, &hexoffset, &boutput, &literalconstants, &literalregisters, &clearregisterargs, &logmode); if (ini != 0) tellUser(0, "Flasm configuration file flasm.ini not found, using default values"); if ((strstr(argv[1], ".htm") != NULL) || (strstr(argv[1], "http://") != NULL)) { mode = MODE_IDE; if ((strIstr(argv[1], "ContextHelp") != NULL) || (strIstr(argv[1], "http://") != NULL)) { /* an attempt to access flash help */ mode = MODE_FLASH_HELP; if (strlen(flabrowser) == 0) tellUser(1, "You must define 'flabrowser' value in flasm.ini to access flash help"); if (access(flabrowser, X_OK) != 0) tellUser(1, "Couldn't start browser: %s", flabrowser); strcpy(flapath, flabrowser); strcat(flapath, " "); strcat(flapath, argv[1]); return; } if (strIstr(flatest, "FLAPLAYER") != NULL) strcpy(flapath, flaplayer); else if (strIstr(flatest, "FLABROWSER") != NULL) strcpy(flapath, flabrowser); else tellUser(1, "Invalid or missing 'flatest' value in flasm.ini"); if (strlen(flapath) == 0) tellUser(1, "You must define '%s' value in flasm.ini for debugging", flatest); if (access(flapath, X_OK) != 0) tellUser(1, "Couldn't start: %s", flapath); if (strstr(argv[1], "file:///") != NULL) /* skip "file:///" (8 chars) */ unescapePath(argv[1] + 8); else unescapePath(argv[1]); if ((inputFile = fopen(inputName, "rb")) == NULL) tellUser(1, "Couldn't open input file %s for reading", inputName); strcat(flapath, " "); strcat(flapath, argv[1]); if (strIstr(flatest, "FLAPLAYER") != NULL) strcpy(strstr(flapath, ".htm"), ".swf\0"); return; } if (argv[1][0] == '-') { int i; if (argc < 3) usage(); inputName = mstrdup(NULL); strcpy(inputName, argv[2]); /* join all arguments into one string - to support spaces in file names */ for (i = 3; i < argc; i++) { strcat(inputName, " "); strcat(inputName, argv[i]); } switch (argv[1][1]) { case 'd': mode = MODE_DISASSEMBLE; break; case 'a': mode = MODE_ASSEMBLE; break; case 'u': mode = MODE_UPDATE; break; case 'b': mode = MODE_ASBYTECODE; break; case 'x': mode = MODE_DECOMPRESS; break; case 'z': mode = MODE_COMPRESS; break; default: usage(); } } else { int i; inputName = mstrdup(NULL); strcpy(inputName, argv[1]); /* join all arguments into one string - to support spaces in file names */ for (i = 2; i < argc; i++) { strcat(inputName, " "); strcat(inputName, argv[i]); } if (strIstr(inputName, ".swf") != NULL) { flmName = mstrdup(inputName); strcpy(strIstr(flmName, ".swf"), ".flm"); /* redirect stdout to inputName.flm; should close it later? */ if (freopen(flmName, "wb", stdout) == NULL) tellUser(1, "Couldn't open output file %s for writing", flmName); mode = MODE_DISASSEMBLE; } else if (strIstr(inputName, ".flm") != NULL) { mode = MODE_ASSEMBLE; } else usage(); } if (inputName == NULL) usage(); if ((inputFile = fopen(inputName, "rb")) == NULL) tellUser(1, "Couldn't open input file %s for reading", inputName); } int main(int argc, char *argv[]) { yydebug = 0; #ifdef MEMWATCH mwStatistics(2); #endif checkByteOrder(); parseArgs(argc, argv); if (mode == MODE_DISASSEMBLE) { getSWFHeader(inputFile); if (strcmp(swfHeader, "FWS") != 0) { if (strcmp(swfHeader, "CWS") == 0) { decompressSWF(inputFile, inputName); /* skip SWF header, we know it's 'FWS' */ fseek(tempFile, 3, SEEK_SET); disassembleSWF(tempFile, inputName); remove(tempName); } else tellUser(1, "Input file doesn't appear to be an SWF file.."); } else disassembleSWF(inputFile, inputName); mexit(EXIT_SUCCESS); } if (mode == MODE_DECOMPRESS) { getSWFHeader(inputFile); if (strcmp(swfHeader, "CWS") != 0) { if (strcmp(swfHeader, "FWS") == 0) tellUser(1, "Know what, the SWF isn't compressed"); else tellUser(1, "Input file doesn't appear to be an SWF file.."); } decompressSWF(inputFile, inputName); mexit(EXIT_SUCCESS); } if (mode == MODE_COMPRESS) { getSWFHeader(inputFile); if (strcmp(swfHeader, "FWS") != 0) { if (strcmp(swfHeader, "CWS") == 0) tellUser(1, "Know what, the SWF is already compressed"); else tellUser(1, "Input file doesn't appear to be an SWF file.."); } compressSWF(inputFile, inputName); mexit(EXIT_SUCCESS); } if (mode == MODE_ASSEMBLE || mode == MODE_ASBYTECODE) { yyin = inputFile; yyparse(); fclose(inputFile); mexit(EXIT_SUCCESS); } if ((mode >= MODE_UPDATE) && (mode != MODE_FLASH_HELP)) { FILE *stdoutTempFile; getSWFHeader(inputFile); /* overwrite user settings */ showoffset = 0; literalconstants = 1; literalregisters = 0; if (strcmp(swfHeader, "FWS") != 0) { if (strcmp(swfHeader, "CWS") == 0) { decompressSWF(inputFile, inputName); inputFile = fopen(inputName, "rb"); getSWFHeader(inputFile); } else tellUser(1, "Input file doesn't appear to be an SWF file.."); } flmName = mstrdup(inputName); strcpy(strIstr(flmName, ".swf"), ".$lm"); /* redirect stdout to inputName.flm */ stdoutTempFile = freopen(flmName, "wb", stdout); if (stdoutTempFile == NULL) tellUser(1, "Couldn't create temporary file"); disassembleSWF(inputFile, inputName); fclose(stdoutTempFile); inputFile = fopen(flmName, "rb"); inputName = mstrdup(flmName); yyin = inputFile; yyparse(); fclose(inputFile); remove(flmName); } if (mode >= MODE_IDE) { if (system(flapath) == -1) tellUser(1, "Couldn't execute: %s", flapath); } mexit(EXIT_SUCCESS); /* to make compiler happy */ exit(0); } /* indent -l200 --ignore-newlines -bap -nbc -fc1 -npsl -sob -ncdb -brs -br -nce -cdw -npcs -i4 -ts4 -c60 -cd30 -cli4 -cbi4 -bs -nss flasm.c */ /* splint -retvalint +posixlib -boolops -retvalother -realcompare -predboolint unflasm.c flasm.c util.c assembler.tab.c > flasm.lint */ flasm-1.62/flasm.h0000666000175000017500000000341210632542740012462 0ustar pabspabs/* flasm, command line assembler & disassembler of flash actionscript bytecode Copyright (c) 2001 Opaque Industries, (c) 2002-2007 Igor Kogan All rights reserved. See LICENSE.TXT for terms of use. */ #ifndef FLASM_H_INCLUDED #define FLASM_H_INCLUDED #include "action.h" #include "util.h" extern int swfVersion; char *mstrdup(const char *String); int writeByte(unsigned char num); int writeShort(unsigned int num); int writeLongInt(long int num); int writeFloat(float num); int writeDouble(double num); int writeString(char *str); unsigned int writePushString(char *str); void addConstant(char *string); unsigned int writeConstants(void); void patchFlag(unsigned int back, byte flag); void patchLength(unsigned int back, unsigned int blen); void patchFrameLoaded(unsigned int len, int numActions); int branchTarget(char *label); int addNumLabel(int numLabel); void addLabel(char *label); void writeDoAction(void); void writeInitMC(unsigned int clipID); void writeOnClipEvent(unsigned long int flag); void writePlaceMCStart(unsigned int clipID); void writePlaceMCEnd(unsigned long int flags); void writeButtonStart(unsigned int buttonID); void writeButtonEnd(void); void writeButtonEvent(unsigned int flags); void writeDefineMCStart(unsigned int clipID); void writeDefineMCEnd(void); void writeProtect(char *str); void writeEnableDebugger(char *str); void writeEnableDebugger2(char *str); void writeMetadata(char *str); void writeFileAttrs(unsigned long int attrs); void writeScriptLimits(unsigned int recursion, unsigned int timeout); void writeExportAssets(unsigned int numAssets); void writeImportAssets(char *str, unsigned int numAssets); void startUpdate(char *str); void finishUpdate(void); void writeASBytecode(void); void tellUser(int isError, char *s, ...); #endif /* FLASM_H_INCLUDED */ flasm-1.62/LICENSE.TXT0000666000175000017500000000355010632542740012675 0ustar pabspabsFlasm, command line assembler & disassembler of Flash ActionScript bytecode Copyright (c) 2001 Opaque Industries, (c) 2002-2007 Igor Kogan, (c) 2005 Wang Zhen All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Opaque Industries nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Macromedia and Flash are either registered trademarks or trademarks of Adobe Systems Incorporated in the United States and/or other countries. Adobe does not sponsor, affiliate, or endorse this product and/or services.flasm-1.62/flasm.html0000600000175000017500000025013510634444407013174 0ustar pabspabs no|wrap.de - Flasm
About · Download · What's new · Usage · Flash virtual machine · Assembler syntax · Embedding Flasm · Optimization techniques · __bytecode__ · File size difference · Huge scripts · Quirks, bugs and crashes · History · Project state · Resources · Terms of use · Enjoy

About

Flasm disassembles your entire SWF including all the timelines and events. Looking at disassembly, you learn how the Flash compiler works, which improves your ActionScript skills. You can also do some optimizations on the disassembled code by hand or adjust the code as you wish. Flasm then applies your changes to the original SWF, replacing original actions.

It's also possible to embed Flasm actions in your ActionScript, making optimizing of large projects more comfortable.

Flasm is not a decompiler. What you get is the human readable representation of SWF bytecodes, not ActionScript source. If you're looking for a decompiler, Flare may suit your needs. However, Flare can't alter the SWF.

Page too long? You don't have to read it all. First, make yourself familiar with usage. Then read flash virtual machine topic to understand the concept of registers and stack. Disassemble some of your SWFs, starting with simpler ones, to see the inner workings of the Flash compiler. The rest of this page tries to address questions you may have at this point.

Download

Most recent Flasm version is 1.62.

Windows binary:  flasm16win.zip
Mac OS X binary:  flasm16mac.tgz
Linux x86 binary:  flasm16linux.tgz

There is no installation procedure. Just create a folder named flasm somewhere and unpack the archive there. To uninstall, delete the folder and you're done. Flasm doesn't touch your system files or registry.

Third-party distributions and translations

FreeBSD port is maintained by Jun Kuriyama, Redhat RPMs are built by Daichi Shinozaki. They may be some versions behind the current and are not tested by me. If something goes wrong, please contact the maintainers. Jaco has translated Flasm manual into Italian.

Want to compile from sources?

Source code, platform independent: flasm16src.zip

You will need gcc or cc compiler with flex, bison, gperf, zlib and zlib-devel packages installed. It should compile well without any changes. Tested on Windows 2000 (Cygwin), Mac OS X, and Linux. For Cygwin, please install mingw, mingw-runtime and mingw-zlib packages too. On Windows, MS Visual C++ and other not entirely POSIX compatible compilers will require plenty of changes to the source. Consider Cygwin.

What's new in Flasm 1.6 series

Flasm 1.62

  • Bug fixes, thanks to Petr Ovtchenkov et al.

Flasm 1.61

  • importAssets2 tag fix
  • placeObject2 tag fix (Flash 4)
  • Calculate path to the flasm.ini in a more reliable way

Flasm 1.6

  • Flash 8: support for metadata, fileAttributes tags
  • Flash 8: support for placeObject3, importAssets2 tags (Wang Zhen)
  • "Update with Flasm and Preview" JSFL action now works in Flash 8 IDE
  • Windows binary includes zlib 1.2.3
  • Fixed: names of register parameters of function2 may contain non-English characters
  • Calling Flasm without a command: flasm foo.flm has the same effect as flasm -a foo.flm

Older changes are listed in CHANGES.TXT included in distribution.

Usage

Flasm is a command line tool. To use it, you have to open DOS window first (Windows). On Mac OS X, open terminal window: Applications/Utilities/Terminal. Then go to the Flasm folder with cd c:\Flasm (Windows)  or cd /flasm (Mac/Linux), assuming you saved it here. To execute Flasm, simply type in flasm (Windows) or ./flasm (Mac/Linux). Called without arguments, Flasm will show you the list of possible commands described below.

flasm command filename

command
-d   Disassemble SWF file to the console
-a   Assemble Flasm project
-u   Update SWF file, replace Flasm macros
-b   Assemble actions to __bytecode__() instruction or byte sequence
-z   Compress SWF with zlib
-x   Decompress SWF

-d foo.swf
Disassemble foo.swf to the console. To see action offsets in disassembly set showoffset and hexoffset options in flasm.ini.

-d foo.swf > foo.flm
Disassemble foo.swf, redirect the output to foo.flm. Calling Flasm without a command on a .swf file has the same effect.

-a foo.flm
Assemble foo.flm and update the SWF defined inside. Calling Flasm without a command on a .flm file has the same effect.
The backup of original SWF is created with .$wf extension.

-u foo.swf
Disassemble foo.swf to the temporary file.
Execute Flasm macros embedded in SWF.
Make trivial optimizations automatically: remove double nots, replace 0.0 with 0, rebuild constant pools, clear register arguments.
Create .$wf backup, update the original SWF.

It's a good idea to update the final version of SWF with flasm -u. Don't expect the SWF to be noticeably faster, it will just make it a bit smaller.

-b foo.txt
produce __bytecode__ instruction or byte sequence, depending on boutput setting in flasm.ini. Takes as input a simple action list without any movie or frame declarations. Output is sent to console. Redirect it to file if you wish: flasm -b foo.txt > foo.as When boutput is set to 1, Flasm produces binary output — probably of use for inserting raw action chunks into swf files build by other tools on server.

-x foo.swf
Decompress foo.swf, create .$wf backup.

-z foo.swf
Compress foo.swf, create .$wf backup. Source SWF doesn't have to be Flash MX file. However, only Flash MX and later players will be able to play the resulting compressed file.

Flasm settings are read from the configuration file flasm.ini. Available options are commented in flasm.ini and explained at appropriate places in the documentation. flasm.ini is searched for in the working directory and, if not found, in the directory where the Flasm binary resides.

All errors and warnings go to the console. If you want to log them in a file instead, uncomment logto option in flasm.ini and enter the desired log file name there. Set logmode option to 0 (default) to append new messages to the log file. If logmode is set to 1 the log file will be overwritten each time you run Flasm.

If you like Flasm and use it often, you may want to add it to Windows right-click context menu for SWF files. The explanation is for Windows 2000, but it should work with minor changes for any Windows version.
Start Windows Explorer. Select View, Folder Options, click the File Types tab, and choose Flash player movie (or similar) type, which stands for SWF file extension. Click Edit button, then click New button. In the Action field enter Disassemble. Click the Browse button, navigate to the Flasm's folder, and double-click on flasm.exe. No parameters are needed. Click OK, Close, and Close again. Now right click on any SWF and choose Disassemble. The disassembly of somename.swf will be stored in file somename.flm in SWF's folder. Further automating is possible, adding flasm -u for updating SWFs or flasm -a for assembling flm files.

If you don't want to do that, look at WinFlasm — simple windows GUI wrapper for Flasm. Note WinFlasm is old and does not support all Flasm commands.

Flash virtual machine

Stack
Constant pool
Global registers
Local registers

Every ActionScript statement is compiled by Flash into a couple of simple bytecode actions. For example, a=b*b; is transformed into

constants 'a', 'b'
push 'a', 'b'
getVariable
push 'b'
getVariable
multiply
setVariable

The bytecodes are stored in SWF in binary form. They are interpreted by the virtual machine of the Flash Player. The code above is the visual representation of the bytecodes, created by Flasm.

I'll call actions inside of a frame or event action blocks. Flash executes action blocks one after another, so the execution flow inside of a block is never interrupted, neither by event nor by gotoAndPlay() or similar actions. Real parallel execution would be nicer? I'm sure it would dramatically affect player stability, which is great now, considering all things going on in a complex movie.

Stack

Flash virtual machine is stack based, you can not refer to the particular memory location. The stack is a place in memory where data can be stored so that the last entered (pushed) value will be extracted (popped) first from the stack. Every command reads (and pops) operands from stack and pushes the result (if any) onto the stack.

The stack may contain elements of arbitrary type — integers, strings, floats and some others. If needed, type conversion happens during execution — like in ActionScript. Often there's no difference between the string '10', integer 10 or double 10.0.

Further stack explanation by Robert Penner:

If you're familiar with Array.push and Array.pop, those commands are similar to stack manipulations. The stack is like an array of values, except you can only access the value on top, push another value onto the top, or swap the top two values.
For instance, to add two numbers, you have to push both of them onto the stack, then call add. The add command will pop the top two values off the stack, add them together, and push the value onto the stack.

The pop action leads to no errors if the stack is empty. The special UNDEF value is popped then, that corresponds to the ActionScript's undefined.

These two actions give you additional functionality for stack handling: dup and swap. dup duplicates the value on top of the stack, swap swaps the two topmost values. Currently Flash doesn't use dup and swap very often as you'll see in disassembly, but they are of great importance for optimization.

Every ActionScript statement, regardless of its complexity, leaves the stack empty after execution. In Flash IDE you don't see the bytecodes and don't have to worry about it. Making changes to bytecodes with Flasm, however, you should always count what's on stack. Improper stack manipulation often doesn't lead to any errors in Flash player. You will not see the 10.000 dead stack entries your loop produced, but the execution will slow down and the SWF probably runs out of memory at some point.

The stack was global in Flash 5. If the value was pushed in frame 1, frame 5 could trace it successfully. It was accessible in movie clips too. With Flash MX the situation changed: Flash Players 6 and 7 flush stack contents after every action block.

Constant pool

At the beginning of every action block where variables, methods or strings are used more than once, Flash creates so called constant pool. In fact, if at least one variable is used twice, the pool is created for all strings in the block. Here is an example:

constants 'bottom', 'paused', 'aliensleft', 'fire'

Constant pool can hold up to 65535 strings (in theory). These can be addressed later in your actions with 1 byte (first 256 strings in the pool) or 2 byte (the rest of the pool) reference. Commonly no more than 256 strings are stored, so you rarely meet 2 byte references in SWF. Practically the number of strings is limited by overall size of constants action, which can't exceed 65535 bytes like any other action.

Flasm disassembler abstracts constant references away by default. They are showed as strings. To see actual references in disassembly, set literalconstants option in flasm.ini to 0. The difference between strings and constant pool members will be obvious then.

Writing push c:1 after the above constants definition means push second constant from the pool (counting from 0). Writing push 'paused' will in turn have the same effect, because Flasm finds the constant in the pool automatically and substitutes string with reference during assembly.

If no previous constant pool declaration is found in the same action block, however, the string 'paused' will be pushed as is. The difference is in code size only, not in execution speed — naturally, the string 'paused' takes five bytes more than one-byte reference. Don't forget to add your strings to the constant pool.

In update mode (flasm -u foo.swf) Flasm rebuilds all constants, removing empty strings and those referenced only once.

The constant pool defined at the start of the frame is valid for every function in this frame. I've never seen constants defined in functions in disassembly. Every event has its own constant pool though.

Although Flash itself never redefines constant pool in the middle of the action block, theoretically you're allowed to do this. Flasm disassembler versions < 1.52 couldn't really deal with multiple constant pools. Flasm 1.52 will show constant references in c:xx form. To always show strings (resembles Flasm < 1.52 behavior, may be inaccurate) set literalconstants to 2.

Global registers

Flash virtual machine has 4 global registers that are addressed r:0, r:1, r:2, r:3. Accessing variables is much slower than accessing registers, so you should store your most used variables there. Flash versions before MX 2004 only used r:0, so there was enough room for optimization. Flash MX 2004's compiler, however, may substitute local variables with other registers — a very good reason to use local variables in ActionScript.

To store something in a register, you should first put this something onto the stack and then execute setRegister command:

push 'paused'
getVariable
setRegister r:1

Now the value of variable paused is stored in r:1. Instead of asking for paused next time, use push r:1.
Note: Unlike most other commands, setRegister does not pop the top value from stack! If you don't need the value stored in register to be on stack, you should manually pop it.

The value of global register, defined in a particular frame on _root, is available to all functions in this frame. If some function is defined or movie clip happens here, it can access or overwrite the register too. It looks like after the showFrame tag occurs in SWF, registers disappear. Generally you don't know what happens to the global register. Of course, calling function A from the middle of function B should leave registers untouched. Flash MX 2004's compiler takes care of it — at the start of the function registers are saved on stack, at the end original values are restored. You should pay some attention here, too.

Local registers

Inside of function2 (Flash Player 6.0.65 and above), and only there, up to 255 local registers exist — from r:0 to r:254. Why not 256? In the function2 header, the number of local registers used in this function is stored in a byte. At the start of function2 the place for local registers is allocated somewhere in memory. The highest possible value for a byte is 255.

Generally, you don't have to care about the number of allocated registers — Flasm calculates this number automatically, and it's not shown in disassembly. Please take consequent registers numbers — using r:1 and r:254 only forces Flasm and Flash Player to allocate 255 registers, which may have impact on memory.

Since local registers are addressed by the same bytecodes as global registers — setRegister and push r:something, function2 has no access to the global registers. Even more confusing is the scope — imagine you have frame A, function2 B inside of A, and function C inside of B. Now function2 B nicely has its own set of registers, and is totally unaware of global registers. That's OK. But function C will share four global registers with frame A!

Besides of all that, local registers function just like global ones. There's no speed difference, too. To summarize: in SWF7 there are still four global registers everywhere outside function2, but any function2 may allocate a set of 255 local registers.

Assembler syntax

Data types and push
Control flow
Button events
Play head control
setTarget/setTargetExpr
function2
try/catch/finally
protect/enableDebugger
scriptLimits
Unknown actions

For details on SWF file format, read Macromedia's description and Alexis' SWF Reference. Macromedia has updated the docs for Flash 7 file format in November 2003. For historical reasons Flasm has its own names for some actions, slightly different from Macromedia's names. Important differences and abstractions are described here. If in doubt, look into action.h from Flasm's source distribution.

Every Flasm project must start with movie 'moviename.swf'. The moviename.swf is the name of your SWF origin. Don't forget to include the file name in quotes. At assembling time Flasm first looks here and then tries to overwrite the file. The backup of target SWF is created with .$wf extension. If update fails for whatever reason, however, the original file will not be destroyed and no backup will be created.

If compressed attribute is found just after movie name (movie 'moviename.swf' compressed), SWF will be compressed after assembling. Original SWF may be compressed or not, compressed keyword decides about compression of updated SWF.

Flasm is case insensitive (excluding string values that may be case sensitive). If you must use a single quote in your strings, escape it like this: 'it\'s beautiful'. Alternatively you can include string in double quotes: "it's beautiful".

Comments look exactly like in ActionScript:
// calculating distance
or multi-line comment:
/* calculating
distance */

Flasm implements #include macro. #include 'loop.flm' will be substituted with the contents of loop.flm. Nested and multiple includes are allowed too: foo.flm includes routine.flm, which includes loop.flm and calc.flm. Maximum nesting depth is 10.

I introduced some extra constructs in order to match the SWF structure. These serve as containers for Flash actions: frame, defineButton, defineMovieClip, initMovieClip, movie, on, onClipEvent, placeMovieClip.

Other supported tags: enableDebugger, enableDebugger2, exportAssets, fileAttributes. importAssets, importAssets2, metadata, protect, scriptLimits.

Please don't alter the SWF structure! It means don't delete, replace or add action block containers! Well, you may add or delete an extra event without causing any damage. But if you remove a frame or change the movie clip id, Flasm will be no more able to find the pendant to it and any subsequent statements at assembling time.

Data types and push

Well, push is the core action in SWF and we'll go a bit more into detail here. Since you can push all kinds of values onto the stack, the push action has an internal type attribute in SWF. While you don't see and can't access the push type from within Flasm, Flasm decides what type to use based on how your data is formatted.

Push type     Number of bytes     What it meansExample
0string length + 1 stringpush 'Hello'
14floatpush Y_PROPERTY
20nullpush NULL
30undefinedpush UNDEF
41registerpush r:2
51booleanpush TRUE
68doublepush 3.1415926
74integerpush 25
81constant (0-255)push 'Hello'
92constant (256-65534)   push 'Hello'

Strings must be included in single or double quotes and may contain escape characters: \b, \f, \n, \r, \t and \\. No line break is allowed inside of a string. If Flasm founds push 'Hello' statement, it first looks into the constant pool for the current action block. If the string is defined there, 1- or 2-byte reference is pushed (push type 8 or 9); if not, the string itself (type 0).

Integers are recognized in decimal and hexadecimal notation (0xF0F0). Doubles are decimal: -3.1415926. The notation 9.4e-10 is supported too. In addition, constants _NAN, POSITIVE_INFINITY and NEGATIVE_INFINITY are defined as double values.

0.0 is considered double; 0 is an integer. Flash compiler itself always stores 0 as double 0.0. In update mode Flasm will automatically replace all 0.0 occurrences with 0, saving 4 bytes per each replace.

Push type 1 is only used by Flash to store property values. Flash 4 stored all number values as strings (push type 0), Flash 5+ utilizes push type 7 for integers and push type 6 for floats.

However, Flash is not the only program creating SWFs. I know now of at least one third-party program (3D-Flash Animator), which uses type 1 for actually storing numbers. So while Flasm will disassemble type 1 to property constant if possible, all values that couldn't be resolved to any constant will be shown as floats: -3.1415926f or 100.0f. You can use this notation in your Flasm projects too, saving 4 bytes per number. Any floating point value which ends with f will be treated as single-precision float and stored with push type 1 (beware of limited precision). The constants _NANF, POSITIVE_INFINITYF and NEGATIVE_INFINITYF are defined too.

One push statement can handle multiple values of different types: push 'Hello', 3.141, XSCALE_PROPERTY. It's not just a shortcut in Flasm for 3 single push actions, but a shorter and faster way.

Control flow

Jumps inside of the action block are implemented with branch and branchIfTrue actions. Every high level ActionScript construct like if (..) then .. else .. or while (..) do .. is converted to some branch/branchIfTrue pattern. branch simply jumps to the specified label. For example, the translation of if .. then .. else construct always has a branch after its then part, which skips the else part and jumps forward to the end of if. Backward jumps are allowed too, loops always contain them. branchIfTrue takes the condition from stack.

Internally relative numerical branch offsets are stored in SWF after every branch instruction. During disassembling Flasm creates unique label for every offset with the name label1 .. labelN, which hides the branch offset from your eyes and makes the disassembly more readable. The syntax is branch label4 or branchIfTrue label6. Somewhere in the same action block the label (identifier followed by colon) must be present. You are by no means forced to use identifiers like label5:. Choose meaningful names (LoopStart:, SearchComplete: etc.) instead.

Let's take an example: the really fast countdown loop, which can't be made with Flash (and can't be decompiled to any valid ActionScript).

push 0,1,2,3,4,5,6,7,8,9,10
loopstart:
dup
trace
branchIfTrue loopstart

First 10 values are pushed onto the stack. Note the last pushed value (10) will be on top of the stack. We have to duplicate the value in loop with dup, because we need it two times: trace pops the first value, branchIfTrue gets the second as loop condition. Since branchIfTrue converts condition to boolean, loop executes until 0 is found, which evaluates to false and stops the loop.

Button events

Every single button event on contains one or multiple of the following:

idleToOverUpoverUpToIdleoverUpToOverDown
overDownToOverUp    overDownToOutDown    outDownToOverDown
outDownToIdleidleToOverDownoverDownToIdle
keyPress  

There's no one-to-one relation of ActionScript button events and events stored in SWF. Some ActionScript events actually set multiple SWF events. That's why the names are different.

keyPress is used in the form keyPress 'char' or keyPpress const, for example keyPress 'a' or keyPress _SPACE. All constants correspond to those in Flash authoring: _BACKSPACE, _DELETE, _DOWN, _END, _ENTER, _ESCAPE, _HOME, _INSERT, _LEFT, _PAGEDOWN, _PAGEUP, _RIGHT, _SPACE, _TAB, _UP.

You are free to change button event conditions in Flasm code.

Play head control

The SWF file format describes three actions for this task: gotoFrame (frame number as operand), gotoFrame2 (takes the frame number from stack) and gotoLabel (frame label as operand). While Flasm's gotoFrame and gotoLabel actions are named exactly like their SWF format pendants, gotoFrame2 action is not present. For your convenience gotoFrame2 is showed as gotoAndPlay/gotoAndStop. In SWF gotoFrame2 is a sole action with a byte flag for play/stop.

Additionally, if you have multiple scenes, Flash puts yet another argument here — the total number of frames in all scenes before the one you're jumping to. These frames will be skipped by Flash player — in other words, added to the expression on stack. This allows for using gotoAndPlay/gotoAndStop with a frame number inside of current scene instead of absolute frame number which starts from the beginning of SWF. Remember, scenes do not exist in SWF. In this case Flasm will show you something like gotoAndStop skip 10. Note you're in trouble if your expression represents label string instead of integer frame number. Flash player doesn't care and will add frames to skip here too — and play head jumps to the false frame. Try using _root.gotoAndStop(). Here movie clip method will be used instead of single instruction. It does no corrections and will work properly for labels.

Higher Flash versions tend to use gotoAndPlay/gotoAndStop methods of the movie clip object (passing them as strings) to control movie clips.

gotoLabel is rarely seen in disassembly, because Flash replaces it with frame-based actions exporting SWF. Only if Flash can't resolve the frame number (the label is not on the same timeline), gotoLabel will be left as is. Labels, however, are still present in SWF and may be accessed from javascript or whatever hosts the SWF, even if jumps to these labels were eliminated.

setTarget and setTargetExpr

setTarget action corresponds to tellTarget in ActionScript. If target is an expression, setTargetExpr is used, which pops the target string from stack. Flasm shows it like

setTarget '/defender'
  gotoFrame 1
  play
end
    or     setTargetExpr
  gotoFrame 1
  play
end

The end statement does not exist in bytecode; Flash uses setTarget '' to mark the end of “targeted” statements.

setTarget '/defender'
gotoFrame 1
play
setTarget ''
    or     setTargetExpr
gotoFrame 1
play
setTarget ''

Since every setTarget is handled by Flash this way, I decided to make it look more readable. Nesting of setTarget blocks is not allowed.

function2

Flash MX 2004 introduced new function2 bytecode, which works in Flash Player 6.0.65 and above. function2 is an extended version of function. In disassembly, it looks like this:

function2 test (r:3='arg1', 'arg2', r:4='arg3') (r:1='this', r:2='_root')

In first parenthesis function arguments are shown. These arguments may be stored in local registers. Each function2 has its own set of local registers. If register is absent, the corresponding argument isn't stored in register and behaves just like normal function argument. If register is present, the corresponding argument is stored there before function2 executes.

You can't access arguments stored in registers by name (with getVariable), only by r:something (something being numerical or literal, see below). That means their names are effectively useless in SWF. If clearregisterargs is set to 1 in flasm.ini, flasm -u will remove these names from SWF, making it a bit smaller and forcing decompilers to name arguments arg1, arg2, ..., because actual parameter names will be lost. To the best of my knowledge it doesn't affect code execution in any way.

Second parenthesis contains “automatic” parameters. Their values are calculated and stored in local registers before function executes, like function arguments. Currently (in SWF7) there are six possible values: 'this', 'arguments', 'super', '_root', '_parent' and '_global'. Internally in SWF the corresponding bits in an unsigned integer value are set to indicate the presence of such value. For the sake of understandability Flasm shows them in literal form, however you can't add your own particular value here or affect register allocation. Registers are allocated by Flash Player in the above order, i.e the value of 'this' goes to r:1, the value of 'arguments' to r:2 etc. If 'this' is absent, 'arguments' goes to r:1. If you accidentally tell Flasm to store automatic values in wrong registers, Flasm will report an error.

So the use of local registers in function2 is threefold: arguments, “automatic” values and local variables are stored there.

If literalregisters flag is set to 1 in flasm.ini, Flasm will disassemble function2 like this:

function2 isSpace (r:1='char')
  push r:char
  ...
end // of function isSpace

instead of

function2 isSpace (r:1='char')
  push r:1
  ...
end // of function isSpace

I.e. all function2 arguments and automatic values like 'this' will be shown with their literal names after r:. Of course, you're free to write your own code using literal registers. r:char in the example above means exactly the same as r:1, and you may use numerical and literal notations together without any problems. However, you can't name other registers (local variables or like).

You can safely store your own values in local registers, Flasm automatically adjusts the number of registers to allocate for any function2. This number, although stored in SWF, is invisible in disassembly. A small side-effect: for whatever reason Flash compiler often allocates more registers than needed. Flasm will allocate the minimal possible number.

An edge case: this, arguments and super automatic parameters will be suppressed by Flash player if they don't appear in second parenthesis. They will neither be stored in registers nor accessible by name inside of function2. Normally, you don't care: if you need one of these, allocate a register for it. In the very special case where you don't want to do it, but still want to access the parameter by name, you can list it without register given. Listing '_root', '_parent' and '_global' makes no difference. They are always available by name anyway. Nice, what?

The try/catch/finally block

There is a new try-catch-finally construct in Flash MX 2004. In SWF all catch blocks are merged into one, and exception condition checking is done with normal control flow there. In disassembly, the variable that holds the actual exception will be shown after try keyword, not catch. Like this:

try 'e'
  push 'x'
  getVariable
  throw
  branch label1
catch
  push 5
  trace
 label1:
end // of try

Alternatively, condition may be held in a register: try r:2 or try r:something  (literal register). Data type exceptions, for example, are always transferred through register, usually r:0. Other registers are used if error variable is declared local inside of try block (thanks to Alex Bradley for finding this out), or given as parameter to function2 that contains the try/catch/finally block.

The throw action stores condition in a variable or register given after the try keyword automatically, you don't have to do it explicitly. The condition is then available at the start of catch block.

protect, enableDebugger and enableDebugger2 tags

protect was meant by Macromedia as a hint for authoring program, saying that the author of particular SWF doesn't wish it to be opened in Flash IDE. protect is not actually protecting anything, any program that deals with SWF can simply ignore it. In Flasm, protect will be shown, and can be added/deleted. You can place it anywhere in SWF, albeit usual location is somewhere near to the beginning. Note protect is not an action, so it has to be outside of action blocks. Passwords are encoded by Flash compiler into a 28 characters long string, consisting of these parts (Paul Cannon):

The $1$ at the start does signify an encryption scheme; it's the traditional way to indicate a crypt-MD5 password. Everything between the second and third $ is the salt, and everything after the last $ is the hashed password, in a sort of base-64 representation.
Flasm will show the encoded string, but not the password.

enableDebugger is another attempt to secure the content of SWF. Always protected by password (Flasm will show the encoded string), this tag gives you the ability to “remote debug” the SWF. If you don't know the password, debugger will not let you in. If you delete the password, debugger will not let you in. But if you change enableDebugger parameter to '$1$.e$7cXTDev5MooPv3voVnOMX1', empty password will be accepted.

To say it clear one more time: above tags, including encrypted passwords, give you no protection and can be safely deleted or altered.

Flash MX allows debugging on source code level, so there is a new tag enableDebugger2, which is used instead of enableDebugger. It makes no difference at all. However, Flasm will not show another tag (63) or contents of external file used by debugger, don't know anything about their format.

scriptLimits tag

Introduced with Flash Player 7, scriptLimits tag gives you control about the maximum recursion depth and the maximum time before the famous "Script causes the movie to run slowly.." message appears. So far I know, these settings can not be changed in Flash IDE. The following syntax is used:

scriptLimits recursion 2000 timeout 10

While increasing recursion depth (256 by default) is surely useful in some situations, you may actually want to decrease the time-out for ActionScript for testing purposes. Instead of standard 15 or 20 seconds, setting the value to 1 or 2 will immediately show you where the bottlenecks are.

Unknown actions support

Flasm 1.6 knows every Flash action, including Flash 8 actions. Only subset of possible bytecodes, however, is currently used by Flash. Part of bytecodes space is reserved for third-party applications. For example, Apple's QuickTime added tag 0xAA for QuickTime actions. Flasm is able to disassemble/assemble actions it doesn't know. The disassembly line looks like

swfAction 0x02 // Unknown action!

If the action has additional data, hexdata part is present:

swfAction 0xAA hexdata 0x43,0x12,0x18 // Unknown action!

The data is shown as comma separated list of hex bytes. If you define your own actions for some proprietary application, there is no need to include tag length in hexdata field — the length is calculated and added automatically if hexdata keyword is found. Don't forget, only bytecodes > 0x80 may have additional data.

Embedding Flasm code in ActionScript

If invoked with -u command (flasm -u foo.swf), Flasm processes macros embedded in your ActionScript and updates the SWF with Flasm statements. It's not unlike embedding assembler in C or Pascal. The syntax is a bit special to let Flash compile scripts without errors — you have to include Flasm statements in quotes. An example:

"push 'Hello world!'"
"push myTextField"
"setVariable"

The above has the same effect as myTextField = "Hello world!"; ActionScript statement. Semicolons are not required, but will do no harm. Flasm code strings are allowed everywhere in your scripts, so don't worry about the right placement. Any restrictions? Sure. Don't define frames or movie clips in embedded Flasm. If you embed, you are already inside of some frame or event definition. Make sure the stack is empty after your embedded code executes. It's not a restriction, but you probably don't want to cause memory leaks.

All Flasm actions behave as expected, there is only one important difference to consider — if you use constants declaration in embedded scripts, Flasm will add them to the main pool in the action block instead of redefining it. The same goes for constants inside of any included file. Using #include as part of the embedded string, please take care of slashes. Use normal slashes and not backslashes in file path. Latter will be escaped, if not deleted by Flash.

While Flasm works just fine with compression enabled in Flash MX, update process will require two additional steps: decompressing and compressing. If your computer is slow, you may consider disabling compression in Flash publish settings. You can always compress SWF with flasm -z as last step before distribution.

Testing embedded actions from within Flash IDE

Of course you can export the SWF, update it with Flasm, and check for errors then. Testing directly from Flash IDE would be nicer. Since Flash IDE has no post processing interface, it's rather tricky.

Flash 8 and Flash MX 2004

I've written a dll and a JSFL script which manages to preview your SWF in internal player. Here we go.

  • Copy flasmhelper.dll from helper directory of Flasm distribution to External Libraries directory inside of your Flash configuration folder.
    On windows 2000 and XP Flash 8 configuration is located here: C:\Documents and Settings\username\Local Settings\Application Data\Macromedia\Flash 8\en\Configuration. The path will vary for non-English Windows and/or Flash.
  • Copy Update with Flasm and Preview.jsfl to Commands directory inside Flash configuration folder.
  • Edit the first line of JSFL script and adjust path to the Flasm executable.
  • Start Flash 8/MX 2004. "Update with Flasm and Preview" should be now available under "Commands". If you wish, associate a shortcut with it.
  • You are all set. Execute the command to preview embedded Flasm actions. You have to write some first :) If unsure, re-read previous section.
All Flasm messages and errors should go to the output window now. You may also use trace statements in Flasm code. Please note JSFL isn't that stable. Flash 8/MX 2004 may crash or run out of memory occasionally. This has nothing to do with my particular JSFL code or dll.

The dll is reasonably secure in the sense it will execute only a program called flasm.exe, no command.com or such. It should be enough to prevent simple abusing it from malicious JSFL scripts. The dll is tested under Windows 2000 and XP. Please tell me if it works for you on Windows 98/ME — or if it doesn't.

It shouldn't be too hard to port the library to Mac. To a pity, I don't own a Mac and can't do it myself. If you have interest in porting, drop me a line, I'll make the source available on request. To make sure you have all the prerequisites, try compiling the Mac sample from Macromedia.

Flash 5 or MX

There was no JSFL. Sven König has found a way, and I've implemented it in Flasm. We'll make Flash believe Flasm is a browser. While proper installation requires some tweaking, it will work like a charm once you got it. I'll describe the procedure for Windows, but something similar should work on Mac too.

  • Copy flasm.exe and flasm.ini into the Browser subdirectory of Flash.
    For Flash 5, the Browser subdirectory is inside of your Flash install folder.
    Flash MX, stores settings elsewhere. If Browser subdirectory does not exist, create it.
    Windows 2000 or XP: C:\Documents and Settings\username\Application Data\Macromedia\Flash MX\Configuration\Browser
    Windows 98 or ME: C:\Windows\Application Data\Macromedia\Flash MX\Configuration\Browser
    Windows NT: [Windows directory]\profiles\[username]\Application Data\Macromedia\Flash MX\Configuration\Browser
    Mac OS X: Hard Drive/Users/Library/Application Support/Macromedia/FlashMX/Configuration/Browser
    Embedding on Mac is untested, please drop me a line if you get it to work.
  • Rename flasm.exe to iexplore.exe
  • Create shortcut to your new iexplore.exe in the same subdirectory. Don't worry, it doesn't affect the real browser.
  • Open flasm.ini in a text editor. Change flaplayer and flabrowser values to contain your Flash player and internet browser path, respectively. Long file names are not supported in dos, you should first discover what the corresponding short names look like: "C:\PROGRA~1\INTERN~1\IEXPLORE.EXE" or similar. Even if you're on Win 2000, please use short names. Set the value of flatest to “flaplayer” if you want to test your files in player, and to “flabrowser”, if you'll test in browser. You can change flatest value later while testing without restarting machine or Flash. or Flash IDE.
  • Done. Now open your file in Flash, insert Flasm code, make sure HTML and “Use Default Names” boxes are checked in Flash publish settings, and press F12 (Publish Preview).

Flash will compile the SWF, look for browser shortcut, check the name (iexplore.exe) is ok, give the HTML file name to Flasm. After calculating the real SWF name Flasm will update the SWF and invoke browser or player to show it. The DOS box appears for the short time, but will only stay open if there are error messages to report. I guess (based on my experience) the most popular error would be “Could not start: c:\...\...\foo.exe”, because the path in flaplayer or flabrowser is wrong. Correct it and try again.

Doesn't work? Have you tried it with Flash MX 2004 by chance? MX 2004 doesn't seem to support default browser mechanism any more, use the first dll/JSFL method.

Sometimes Flash just doesn't start Flasm. Enter something in actions window. Or uncheck HTML in export settings, publish, check it again, publish. Or delete iexplore shortcut from browser directory, publish, restore shortcut. #include may fail in Flash IDE if you haven't exported the SWF to the right location before. Check publish settings.

In Resources you'll find links to the small debugger by Pavils Jurjans and profiler by Ben Schleimer.

Optimization techniques

Measurement
ActionScript optimizations
Flasm optimizations
Double nots
Thanks

Huge bitmaps, not optimized vectors, false frame rates, animating many movie clips at the same time, loading large XML files, dealing with tons of editable text, streaming high quality sound, or simply viewing SWF on mac — in 95% of all cases, bad SWF performance has nothing to do with ActionScript. Flasm, although being “yet another cool tool”, is no solution for above problems. Optimizing with Flasm makes sense for games, 3D engines, path finding, actually converting large amounts of data — computing things in general. Flash MX 2004 and Flash 8 made things much better here, too — at least for newer Flash Players.

If you're unsure where is the bottle neck: slow drawing or slow calculating, there is a simple trick: make the player or browser window very small and switch aliasing off in the Flash Player. If performance increases significantly, you probably should optimize your graphics or movie clip structure first.

Measurement

Don't try to optimize every single line of your code — you'll just make it unreadable, probably omit some important places, and nobody will ever notice 10.000 hours of your hard work. The key to any optimization is measurement. Bottle necks are very hard to guess. I used to have plenty of tips here, well tested for Flash 5 and Flash 4, but it just can't work this way because of the current diversity of possible environments. Now we have Flash Player 8, 7, 6, 5 and (still) 4 out there, stand-alone applications, mobile devices, not to mention Windows, Mac and Linux. These all are entirely different species. It means only some general strategies still work everywhere, and you have to measure your particular application on your target environment yourself. But how?

Optimize and test for your target environment. Tests in Flash IDE are very rough estimates at best.

You should differentiate between ActionScript code in FLA and compiled code in SWF. For example, if “Omit Trace Actions” is checked during publishing, traces simply do not make it into SWF. They do not exist there. The same goes for commented code. #included files are in effect exactly the same as inlined code, since they are first included, then compiled — nothing to test here, too. There are no classes in SWF — just functions. Flash compiler optimizes library function calls with constant arguments too. Calls like f = Math.sin(0.25) or f = Math.max(3,5) are never saved in SWF, the calculated values go here. Neither will if parts with condition that always evaluates to false be stored. Some local variables are stored in registers by the compiler, which makes a huge difference. And so on. Before you test, take a look at disassembly.

Compiled bytecodes for Flash Player 7 are, err, context-dependant. Code inside of function2 benefits from local registers. The same code in a frame will not.

In a standard procedural programming language, most of the program time is spent in loops and functions or methods called from those loops. In Flash, frame loops and often or parallel called events should be investigated too.

Ben Schleimer's Flasm Profiler (or is it Flash Profiler?) and David Chang's ASProf are attempts to solve the main problem — what to optimize. I don't know how good they work, because I've not used them in a real-world project yet — they are relatively new. The profiler basically tells you execution times and number of calls for every function.

After you've found what's critical, find better algorithm first, or change your approach in general. Although you could improve the code in small, optimization should be the last resort.

Do all tests in a defined computer state — fresh booted, no virus scans or internet connection in background, all other programs closed. Don't move or resize widows during the test, don't do anything. Don't move your mouse, and let your mouse stay over flash movie. It does make a huge difference. That's not voodoo — OS manages your mouse, and Flash isn't the only running process.

Think about graphics. A script is never interrupted, so it's relatively easy to measure. If, however, you start to measure in the first frame, and end in tenth, you measure everything in between and simply don't know what you measure. The result largely depends on player's mood and takes generally much longer, making your ActionScript test irrelevant. Network requests, gotoAndPlay actions and many other things related to screen refresh are executed asynchronously. Get them out of measured code parts.

Beware of loop overhead. Short test times are not reliable, since the loop itself takes most of the time. Calculate time for an empty loop, function, etc. and subtract it from your results. Use big loops, so that remaining times are bigger then, say, 1000 ms. Computers aren't that exact in ms. Generally, try to isolate the code in question from anything else and measure that code only. Of course, try to get other factors out of consideration first — network bandwidth, graphics etc. Otherwise you results can't be compared because of hidden overhead.

Mac Flash Player is slow compared to PC. Test on Macs early.

Don't execute different tests together. If you try to compare optimization 1 with optimization 2, give them the same environment. One run — one test. If you put both tests in the same frame, you start to deal with caching.

ActionScript optimizations

Flash Player 7 and 8 are much faster with ActionScript. Because of player improvements, and because of compiler improvements in Flash MX 2004/Flash 8. The latter are only noticeable if you compile for Flash Player 6 or higher though. If that's your target audience, you mostly can do now without Flasm optimizations, because the compiler will use registers anyway. You will still be able to achieve better performance with Flasm, but your first step should be getting Flash 8 IDE for critical applications.

I used to elaborate on so called “deprecated Flash 4 actions” here, which are much faster in Flash Player 5 or 6. The worst example: myMC.gotoAndStop() was 25 times slower than tellTarget("myMC") gotoAndStop(). In Flash Player 7 they finally don't seem to make a real difference. To insist on recommending them, I would have to re-do the whole testing, including mobile devices, so to hell with them.

Action blocks are always executed from the start to the end, no event or gotoAndPlay() will interrupt execution of other code. That's the reason why any large for loop will hang the player, no screen updates are made.

Define local variables in functions with var keyword. Local variables are faster, generally a good practice, and may be replaced with registers automatically, if compiled with Flash MX 2004/Flash 8.

eval is something special compared to, say, this or any other ActionScript keyword. In fact, eval is kind of macro — it doesn't have a bytecode, but simply writes its argument onto the stack — at compile time. No doubt it's faster than any method call. Starting with Flash MX, you're no longer allowed to use eval on the left side of assignment. Use set instead.

Unfortunately, identifier length still matters, even in Flash 8, so choose short names for variables. This can be extended to built-in functions too. Creating the function t = Math.tan and substituting all Math.tan occurrences with t will serve 2 purposes: no additional lookup is made for object Math, then for method tan; and the name itself is shorter. It works only for Flash 5+ methods and functions; Flash 4 functions will slow down. Of course, names of local variables don't matter if they are stored in registers.

The old trick with replacing b = a*4 to b = a«2 (shift) makes no speed difference in ActionScript.

Flash tries to precalculate constant parts of your expressions. The calculation order results from operator precedence. As Robert Penner noticed, rad = Math.PI/180 will actually store calculated value in SWF, while rad = c*Math.PI/180 will not. Conclusion: explicitly set the precedence to enable precalculation (rad = c*(Math.PI/180) in this case).

for and while loops show no speed difference. It depends on how you write them. The most optimized ActionScript examples of both, looping down to 0, produce the same bytecode: for(var i = 10; i--;) {} and i = 10; while (i--) {} The third part of the for loop, absent in my example, is actually in the body of loop, so you can't compare it with a normal while.

Avoid multiple parallel hitTest() functions in events — often seen in games. If the player is killed after any touch with an enemy, and you have 100 duplicated enemy clips, don't include any code in the enemy clip enterFrame event. Create the new movie clip and insert the enemy clip here. Then duplicate inside of this parent clip. Now you can check with only one hitTest() if the collision takes place. If you need to, use some custom math then to calculate what enemy was hit. Since most of the time no collision occurs, you'll make a really big improvement in fps.

I mostly do not say “3.45 times slower”, because comparisons are very context dependant, exact values will vary. My “slower” just means “noticeably slower, no situation ever makes it faster”.

The list is by no means complete, and will never be. Technology may render some points incorrect, again. Please make your own tests.

Flasm optimizations

After you're done in ActionScript, and the code is still slow, you can start to optimize with Flasm. Basically only two meaningful low-level features are not accessible from ActionScript and therefore subject of Flasm work: stack and registers.

Let's optimize a simple loop using stack. Our ActionScript is

for (var n=0; n<1000; n++) {
  someFunction(n);
}

Flash compiles this loop to the following bytecodes:

constants 'n', 'someFunction'    
push 'n', 0.0
varEquals
  label1:
push 'n'
getVariable
push 1000
lessThan
not
branchIfTrue label2
 
push 'n'
getVariable
push 1, 'someFunction'
callFunction
pop
 
push 'n', 'n'
getVariable
increment
setVariable
branch label1
  label2:
// Store all variables in constant pool
// Push the string 'n' and starting 0 onto the stack
// Initialize loop counter: n = 0
// Start of the loop
 
// Get the value of 'n' again
// Push loop bound
// Evaluate boolean condition: “n < 1000?”
// Invert: now “n >= 1000?”
// If “true” is on stack, go to the end of the loop
 
// Loop body
// Get the value of 'n' again
// Push the number of args (1) and function name
// function call is made with n as argument
// Pop the possible function result away — it's unused
 
// Push 'n' two times
// Evaluate 'n' again
// n+1 on stack now
// n = n+1
// jump to the loop start — unconditional
// end of the loop — addressed with branchIfTrue above

What we immediately see, the n variable is evaluated many times here. getVariable action is slow compared to stack operations, and the n is only used as local counter. Why not discard n, keep the counter on stack and use it over and over, thus eliminating all getVariable calls? We also don't need the constant pool declaration, since n will disappear, and someFunction name will be only used once. The number of jumps can be reduces to one, too. We know we have to call someFunction(0), so there is no need to check for the condition on the top of the loop. Look at optimized version:

push 0
  loopStart:
dup

push 1, 'someFunction'          
callFunction
pop
 
increment
dup
push 1000
lessThan
branchIfTrue loopStart
pop
// No need for double 0.0, integer 0 will do it
// Choosing meaningful name
// dup the counter — our function will eat it up

// Push the number of args (1) and function name
// function call is made with n as argument
// Pop the possible function result away — it's unused
// Now the counter is on top of the stack again
// Increment it
// Dup the counter — condition evaluation will eat it up
// Push loop bound
// Condition evaluation: counter < 1000?
// Jump to the loop start, counter is on top
// Should remove counter from stack after the loop

We can go even further. If our function, say, fills an array with some calculated values, it makes no difference to do it from 0 to 999 or from 999 “down to” 0. We can eliminate lessThan action in this case, because branchIfTrue is kind enough to convert 0 to false, and all other numbers to true for us.

push 1000
  loopStart:
decrement
dup
push 1, 'someFunction'    
callFunction
pop
dup
branchIfTrue loopStart
pop

We moved decrement to the top of the loop, because otherwise branchIfTrue would immediately exit loop if the counter value is 0 and not let us execute someFunction(0).

As you see, we end with a pretty clear loop version, which will be much faster than the original Flash. How much, depends on what someFunction() does. As the next step you would go there and optimize it.

The best way to learn how to use registers is to compile the same code in Flash MX 2004 for Flash 5, Flash 6, Flash 7, and look at the disassembly. Flash 5 version will use r:0 only, Flash 6 will utilize all four global registers, and Flash 7/8 will add local function2 registers.

Now if your target is Flash 5, you'll see from Flash 6' code what can be done. For higher targets, the room for further optimization is smaller. But there are still many places where the code could be improved — basically by eliminating useless pops, pushes and branches.

push statements may push multiple values, not just one. Try to merge single pushes into one. That's way faster. You'll have to slightly re-arrange the code to do that.

Registers are faster than variables, but still slower than stack. Why not keep all the values on stack so they go to the top just in the moment you need them? The problem is, if you're doing this with 2 or more variables, your algorithm may want to access them in a different order than they're stored. If some value is only required, say, at the start and at the end of your routine — no problem, it happily lives somewhere at the bottom, waiting for its time coming, and lets you work with other values on top. But for often needed values it doesn't work. While we have swap action to exchange two top values on stack, we can't directly access the third. Even if you find some illusionistic approach to access many variables, you'll just slow the execution with big amounts of swap commands.

Double nots

In certain cases Flash writes double nots in your code. Consider ActionScript code if (a<=b) { ... } else { ... } Two inversions are created here by Flash compiler:

push 'b'
getVariable
push 'a'
getVariable
lessThan
// a>b?
not // now inverted: a<=b?
not // prepare for branch to the else condition: again a>b?
branchIfTrue elseCondition

As you see, Flash is not very flexible compiling your statements and does not change the order of operands in expression or use another pattern for if statement. It doesn't really make sense. The only purpose here could be an attempt to force type conversion to boolean. The next action you always see in the code, however, is branchIfTrue. And this action does type conversion itself.
So Flasm will automatically remove those nots in update mode.

Thanks

My very special thanks go to the people on flashcoders list, whose ideas helped me to the better understanding of optimization and flowed into above examples:

Rasheed Abdal-Aziz, Ralf Bokelberg, Robin Debreuil, Zeh Fernando, Gary Fixler, Branden Hall, Dave Hayden, Damien Morton, Amos Olson, Robert Penner, Casper Schuirink.

__bytecode__

__bytecode__ function, first mentioned (1. post, 2. post) by Robin Debreuil is a way to inject bytecodes directly into swf without using Flasm (compare embedding). It takes a string filled with hexadecimal numbers as parameter. Since __bytecode__ is evaluated at compile time, it is not possible to give it a variable parameter. While it's convenient to not rely on Flasm updating the swf, __bytecode__ also offers an excellent way to shoot yourself in the foot. No checks are made by compiler here. You better make sure values are correct. Well, using Flasm in -b mode to produce __bytecode__ parameter (or any other tool) at least guarantees proper instruction layout. Some caveats remain though.

__bytecode__ is a function and as such returns a value. So single pop action is added by Flash IDE if the result is unused. You may also assign the return value to variable, then it will be something else than pop. Normally, an extra pop is harmless.

There are possible complications here with constant pools. Defining your own inside of __bytecode__ will disable the pool automatically produced by compiler. Not defining makes the swf bigger. Define your own pool when needed and let __bytecode__ reside in a place where there is no other ActionScript — don't mix.

File size difference

After assembling Flasm source or updating SWF with Flasm you'll often see your SWF having few less bytes even if you haven't changed anything in the bytecode. Besides of trivial optimizations Flasm does in update mode, there is one more reason for it. Flash may save block lengthes in the SWF as 2 or 6 byte records. 6 bytes are only needed if the block is larger than 62 bytes. Flash, however, often uses 6 bytes where 2 bytes will do. Although Flasm does this too in certain cases, most blocks are optimized during assembling. So I get 400 less bytes on the file of 90kB length without optimizing anything. I don't know of any disadvantages, enjoy this unexpected Flasm bonus. Side note: ironically, there are places where long lengthes are required because of Flash player bugs, but Flasm is aware of this and will let them untouched.

Huge scripts

While it's good practice to keep scripts smaller than 64k (compiled) per frame, it's possible to get larger. But the sole action record — constants, push, function and other is limited to 64k because of 2 bytes length field size.

Since Flash attempts to create the constant pool for all variables and methods, and never creates multiple constant pools, what does it do in such cases? Flash 5 compiler would silently write an overflowed value to the length field without errors or warnings. Later Flash versions are smarter: they would put so many strings as possible into constant pool, other strings just remain in place. Flash MX 2004 will even warn you about classes being too big. However, the compiler doesn't check other places where overflow may occur. Function length (ActionScript 1), for example, isn't verified and will be broken for very big functions.

If you try to execute this kind of SWF, Flash player crashes or actions are omitted. Disassembly will be incomplete and/or wrong. In most cases, Flasm will show an error message.

Quirks, bugs and crashes

Flasm may not be able to disassemble protected SWFs. The protection is usually achieved by inserting some kind of junk into SWF. Generally, such SWFs work in Flash Player, but break the SWF file format spec. Flasm, however, aims to support the spec, not to mimic essentially undefined behavior of some particular Flash Player version. What's my point here? Don't ask me to disassemble something you've downloaded somewhere. Neither will I tune Flasm to overcome any protections. They are not even interesting, and easy to fix with a hex editor.

Windows version of Flasm can't open unicode file names. It seems to be a Cygwin limitation.

When saving flm files in Windows Notepad/Editor, choose ANSI as encoding. In UTF-8 mode Notepad inserts so called byte order mark (BOM) at the start of the file, which irritates Flasm (and many other programs). If you work with UTF-8, please choose another editor, most of them don't add the BOM.

You can't compile something like function 0123ä() in Flash because of parser limitations, the SWF format doesn't impose any restrictions on function names. Since Flasm deals with SWF directly, it should support such names, too. When assembling an SWF, you have to manually place quotes around problematic function names. Note: some of these (unicode names, for example, or Flasm keywords) are perfectly ok with Flash IDE, but interfere with Flasm parser.

An edge case: nested tellTargets don't work if one of them is inside of a function.

Don't know of any other bugs at the moment. If you find a nontrivial bug, fell free to send me your file. Please try to produce a minimal sample where the bug still occurs. The relevant part of source code or FLA is also welcome.

To make it clear: provided the SWF is valid, it must run properly after disassembling and assembling back without changes. The update mode must work too. There are absolutely no voodoo behaviors or unsupported features in Flasm. If you encounter problems, there must be a serious bug in my implementation and your report is highly appreciated.

History

Dave Hayden released Flasm in cooperation with Damien Morton in April 2001. The first version was able to disassemble the main timeline of the SWF and assemble to the first frame only. Flasm was quite useful already. I was very excited to discover Flasm back then, and soon started to play with source code. I've expanded Flasm's functionality and fixed some bugs. Dave then started the project on sourceforge.net. From 2002 until now I'm the only person developing and maintaining Flasm. Recently, Wang Zhen from Genable Labs has greatly contributed to Flasm 1.6.

Project state

After five years of development Flasm is stable enough to be used in real-life projects and to my best knowledge fully supports all quirks of SWF format. I'm happy that during these time Flasm's source code has helped to develop some third-party software. My special thanks go to all the people who reported problems and suggested improvements. Now I'm busy with other projects and will not be able to implement exciting new features. Please send me your bug reports though, bug fixes will continue to happen.

It's unlikely I'll ever add support for Flash 9. As you may know, Flash Player 9 contains the new virtual machine, which is nothing like the old one. I see it as a natural end of Flasm's life cycle — supporting that would in fact mean writing another Flasm from scratch.

Resources

This page can always be found at http://www.nowrap.de/flasm.html. The mirror at flasm.sourceforge.net is updated from time to time.

The source is available here or may be downloaded from SourceForge's CVS. Project page is http://sourceforge.net/projects/flasm.

Take a look at my another project: Flare, the free ActionScript decompiler.

On the original Flasm page resides the first version and the useful explanation of Flash 5 bytecodes by Dave.

Compare tree animations made by Amos Olson: standard ActionScript version and the optimized one.

Look at path finding swf made with Flasm by Casper Schuirink. Here is the source.

MTASC, an open source ActionScript II compiler.

ActionScript problems are discussed on the highly frequented flashcoders mailing list, maintained by Branden Hall.

Alexis' SWF Reference

Macromedia's SWF File Format description

At the prototype site you'll find some Flash functions redefined for speed or flexibility, and also many new and useful ones. Often it's better to start Flasm optimizing from one of them.

Extendflash mailing list deals mostly with JSAPI, which allows customizing Flash MX 2004 and Flash 8 IDEs. Some posts about SWF internals.

KineticFusion by Kinesis Software converts your complete SWF to XML and back. Pricey.

Ben Schleimer's Flasm Profiler shows you what to optimize in the first place.

Pavils Jurjans has written the little debugger for Flasm, useful while embedding Flasm code in ActionScript. The debugger shows stack and register contents.

Jon Bott's has written an obfuscator, based on Flasm.

For people who don't like to work with command line, and don't like to register Flasm as SWF handler in Windows Explorer either, there is a WinFlasm by Sharriff Aina, simple Windows interface to Flasm.

Albert Chosky has created Flasm 1.32 syntax files for EditPlus.

The older Flasm syntax file for UltraEdit, submitted by anonymous Russian flasmer.

Terms of use

Copyright © 2001  Opaque Industries,  © 2002-2007  Igor Kogan,  © 2005  Wang Zhen
All rights reserved.

Flasm is completely free. It's provided “as is” and without any warranties. Please read the license included in the distribution for details.

Macromedia and Flash are registered trademarks of Adobe Systems Inc.
Adobe does not sponsor, affiliate, or endorse this product.

Enjoy

Igor Kogan


Last significant update:  15 Juni 2007 Logo
flasm-1.62/action.h0000666000175000017500000002020410632542740012633 0ustar pabspabs/* flasm, command line assembler & disassembler of flash actionscript bytecode Copyright (c) 2001 Opaque Industries, (c) 2002-2007 Igor Kogan, (c) 2005 Wang Zhen All rights reserved. See LICENSE.TXT for terms of use. */ #ifndef ACTION_H_INCLUDED #define ACTION_H_INCLUDED /* math */ #define MATH_LOG2E 1.442695040888963 #define MATH_LOG10E 0.4342944819032518 #define MATH_LN2 0.6931471805599453 #define MATH_LN10 2.302585092994046 #define MATH_PI 3.141592653589793 #define MATH_SQRT1_2 0.7071067811865476 #define MATH_SQRT2 1.414213562373095 #define MATH_DELTA 10000000000.0 #define NUMBER_MAX_VALUE 1.79769313486231e+308 #define NUMBER_MIN_VALUE 5.e-324 typedef enum { SWFACTION_END = 0x00, /* v3 actions */ SWFACTION_NEXTFRAME = 0x04, SWFACTION_PREVFRAME = 0x05, SWFACTION_PLAY = 0x06, SWFACTION_STOP = 0x07, SWFACTION_TOGGLEQUALITY = 0x08, SWFACTION_STOPSOUNDS = 0x09, SWFACTION_GOTOFRAME = 0x81, /* >= 0x80 means record has args */ SWFACTION_GETURL = 0x83, SWFACTION_IFFRAMELOADED = 0x8A, SWFACTION_SETTARGET = 0x8B, SWFACTION_GOTOLABEL = 0x8C, /* v4 actions */ SWFACTION_ADD = 0x0A, SWFACTION_SUBTRACT = 0x0B, SWFACTION_MULTIPLY = 0x0C, SWFACTION_DIVIDE = 0x0D, SWFACTION_EQUALS = 0x0E, SWFACTION_LESSTHAN = 0x0F, SWFACTION_LOGICALAND = 0x10, SWFACTION_LOGICALOR = 0x11, SWFACTION_LOGICALNOT = 0x12, SWFACTION_STRINGEQ = 0x13, SWFACTION_STRINGLENGTH = 0x14, SWFACTION_SUBSTRING = 0x15, SWFACTION_POP = 0x17, SWFACTION_INT = 0x18, SWFACTION_GETVARIABLE = 0x1C, SWFACTION_SETVARIABLE = 0x1D, SWFACTION_SETTARGETEXPRESSION = 0x20, SWFACTION_STRINGCONCAT = 0x21, SWFACTION_GETPROPERTY = 0x22, SWFACTION_SETPROPERTY = 0x23, SWFACTION_DUPLICATECLIP = 0x24, SWFACTION_REMOVECLIP = 0x25, SWFACTION_TRACE = 0x26, SWFACTION_STARTDRAGMOVIE = 0x27, SWFACTION_STOPDRAGMOVIE = 0x28, SWFACTION_STRINGLESSTHAN = 0x29, SWFACTION_RANDOM = 0x30, SWFACTION_MBLENGTH = 0x31, SWFACTION_ORD = 0x32, SWFACTION_CHR = 0x33, SWFACTION_GETTIMER = 0x34, SWFACTION_MBSUBSTRING = 0x35, SWFACTION_MBORD = 0x36, SWFACTION_MBCHR = 0x37, SWFACTION_IFFRAMELOADEDEXPRESSION = 0x8D, SWFACTION_PUSHDATA = 0x96, SWFACTION_BRANCHALWAYS = 0x99, SWFACTION_GETURL2 = 0x9A, SWFACTION_BRANCHIFTRUE = 0x9D, SWFACTION_CALLFRAME = 0x9E, SWFACTION_GOTOEXPRESSION = 0x9F, /* v5 actions */ SWFACTION_DELETE = 0x3A, SWFACTION_DELETE2 = 0x3B, SWFACTION_VAREQUALS = 0x3C, SWFACTION_CALLFUNCTION = 0x3D, SWFACTION_RETURN = 0x3E, SWFACTION_MODULO = 0x3F, SWFACTION_NEW = 0x40, SWFACTION_VAR = 0x41, SWFACTION_INITARRAY = 0x42, SWFACTION_INITOBJECT = 0x43, SWFACTION_TYPEOF = 0x44, SWFACTION_TARGETPATH = 0x45, SWFACTION_ENUMERATE = 0x46, SWFACTION_NEWADD = 0x47, SWFACTION_NEWLESSTHAN = 0x48, SWFACTION_NEWEQUALS = 0x49, SWFACTION_TONUMBER = 0x4A, SWFACTION_TOSTRING = 0x4B, SWFACTION_DUP = 0x4C, SWFACTION_SWAP = 0x4D, SWFACTION_GETMEMBER = 0x4E, SWFACTION_SETMEMBER = 0x4F, SWFACTION_INCREMENT = 0x50, SWFACTION_DECREMENT = 0x51, SWFACTION_CALLMETHOD = 0x52, SWFACTION_NEWMETHOD = 0x53, SWFACTION_BITWISEAND = 0x60, SWFACTION_BITWISEOR = 0x61, SWFACTION_BITWISEXOR = 0x62, SWFACTION_SHIFTLEFT = 0x63, SWFACTION_SHIFTRIGHT = 0x64, SWFACTION_SHIFTRIGHT2 = 0x65, SWFACTION_SETREGISTER = 0x87, SWFACTION_CONSTANTPOOL = 0x88, SWFACTION_WITH = 0x94, SWFACTION_DEFINEFUNCTION = 0x9B, /* v6 actions */ SWFACTION_INSTANCEOF = 0x54, SWFACTION_ENUMERATEVALUE = 0x55, SWFACTION_STRICTEQUALS = 0x66, SWFACTION_GREATERTHAN = 0x67, SWFACTION_STRINGGREATERTHAN = 0x68, SWFACTION_STRICTMODE = 0x89, /* v7 actions */ SWFACTION_CAST = 0x2B, SWFACTION_IMPLEMENTS = 0x2C, SWFACTION_EXTENDS = 0x69, SWFACTION_DEFINEFUNCTION2 = 0x8E, SWFACTION_TRY = 0x8F, SWFACTION_THROW = 0x2A, /* FlashLite */ SWFACTION_FSCOMMAND2 = 0x2D } Action; typedef enum { PF_MOVE = 0x01, /* this place moves an exisiting object */ PF_CHARACTER = 0x02, /* there is a character tag (if no tag, must be a move) */ PF_MATRIX = 0x04, /* there is a matrix (matrix) */ PF_COLORTRANSFORM = 0x08, /* there is a color transform (cxform with alpha) */ PF_RATIO = 0x10, /* there is a blend ratio (word) */ PF_NAME = 0x20, /* there is an object name (string) */ PF_DEFINECLIP = 0x40, /* this shape should open or close a clipping bracket (character != 0 to open, character == 0 to close) */ PF_ONCLIPEVENTS = 0x80, /* there are onClipEvents */ PF_FILTERS = 0x100, /* there are filters */ PF_BLENDMODE = 0x200, /* there is a blend mode */ PF_BITMAPCACHING = 0x400 /* use runtime bitmap caching */ } placeflags; typedef enum { FILTER_DROPSHADOW = 0, FILTER_BLUR = 1, FILTER_GLOW = 2, FILTER_BEVEL = 3, FILTER_GRADIENTGLOW = 4, FILTER_ADJUSTCOLOR = 6, FILTER_GRADIENTBEVEL = 7 } filtertype; typedef enum { ATTR_USENETWORK = 0x01, ATTR_RELATIVEURLS = 0x02, ATTR_SUPPRESSCROSSDOMAINCACHE = 0x04, ATTR_ACTIONSCRIPT3 = 0x08, ATTR_HASMETADATA = 0x10 } fileattributes; typedef enum { TAG_END = 0, /* end tag for movie clip or swf */ TAG_SHOWFRAME = 1, /* frame is completely described now, please show */ TAG_DEFINESHAPE = 2, TAG_FREECHARACTER = 3, TAG_PLACEOBJECT = 4, TAG_REMOVEOBJECT = 5, TAG_DEFINEBITS = 6, TAG_DEFINEBUTTON = 7, TAG_JPEGTABLES = 8, TAG_SETBACKGROUNDCOLOR = 9, TAG_DEFINEFONT = 10, TAG_DEFINETEXT = 11, TAG_DOACTION = 12, /* normal action block */ TAG_DEFINEFONTINFO = 13, TAG_DEFINESOUND = 14, TAG_STARTSOUND = 15, TAG_STOPSOUND = 16, TAG_DEFINEBUTTONSOUND = 17, TAG_SOUNDSTREAMHEAD = 18, TAG_SOUNDSTREAMBLOCK = 19, TAG_DEFINEBITSLOSSLESS = 20, TAG_DEFINEBITSJPEG2 = 21, TAG_DEFINESHAPE2 = 22, TAG_DEFINEBUTTONCXFORM = 23, TAG_PROTECT = 24, /* the author doesn't want the file to be opened */ TAG_PATHSAREPOSTSCRIPT = 25, TAG_PLACEOBJECT2 = 26, /* possibly onClipEvents inside */ TAG_REMOVEOBJECT2 = 28, TAG_SYNCFRAME = 29, TAG_FREEALL = 31, TAG_DEFINESHAPE3 = 32, TAG_DEFINETEXT2 = 33, TAG_DEFINEBUTTON2 = 34, /* possibly button events inside */ TAG_DEFINEBITSJPEG3 = 35, TAG_DEFINEBITSLOSSLESS2 = 36, TAG_DEFINEEDITTEXT = 37, TAG_DEFINEVIDEO = 38, TAG_DEFINEMOVIECLIP = 39, /* movie clip timeline comes */ TAG_NAMECHARACTER = 40, TAG_SERIALNUMBER = 41, TAG_DEFINETEXTFORMAT = 42, TAG_FRAMELABEL = 43, TAG_SOUNDSTREAMHEAD2 = 45, TAG_DEFINEMORPHSHAPE = 46, TAG_GENFRAME = 47, TAG_DEFINEFONT2 = 48, TAG_GENCOMMAND = 49, TAG_DEFINECOMMANDOBJ = 50, TAG_CHARACTERSET = 51, TAG_FONTREF = 52, TAG_EXPORTASSETS = 56, TAG_IMPORTASSETS = 57, TAG_ENABLEDEBUGGER = 58, TAG_INITMOVIECLIP = 59, /* flash 6 mc initialization actions (#initclip .. #endinitclip) */ TAG_DEFINEVIDEOSTREAM = 60, TAG_VIDEOFRAME = 61, TAG_DEFINEFONTINFO2 = 62, TAG_DEBUGID = 63, TAG_ENABLEDEBUGGER2 = 64, TAG_SCRIPTLIMITS = 65, TAG_SETTABINDEX = 66, TAG_DEFINESHAPE4 = 67, TAG_FILEATTRIBUTES = 69, TAG_PLACEOBJECT3 = 70, /* possibly onClipEvents inside */ TAG_IMPORTASSETS2 = 71, TAG_DEFINEFONTINFO3 = 73, TAG_DEFINETEXTINFO = 74, TAG_DEFINEFONT3 = 75, TAG_AVM2DECL = 76, TAG_METADATA = 77, TAG_SLICE9 = 78, TAG_AVM2ACTION = 82, TAG_DEFINESHAPE5 = 83, TAG_DEFINEMORPHSHAPE2 = 84, TAG_DEFINEBITSPTR = 1023 } tagheaderid; /* action block type - all places in SWF that may contain actions */ typedef enum { AB_FRAME, AB_INITMC, AB_MCEVENT, AB_BUTTONEVENT } abtype; typedef enum { MODE_DECOMPRESS, MODE_COMPRESS, MODE_ASSEMBLE, MODE_ASBYTECODE, MODE_DISASSEMBLE, MODE_UPDATE, MODE_IDE, MODE_FLASH_HELP } processingmode; #endif /* ACTION_H_INCLUDED */ flasm-1.62/keywords.gperf0000666000175000017500000002705510632542740014114 0ustar pabspabs%{ /* flasm, command line assembler & disassembler of flash actionscript bytecode Copyright (c) 2001 Opaque Industries, (c) 2002-2007 Igor Kogan All rights reserved. See LICENSE.TXT for terms of use. */ #include #include "assembler.tab.h" #include "util.h" #ifdef MEMWATCH #include "memwatch.h" #endif struct keyword *in_word_set(register const char *str, register unsigned int len); %} struct keyword {} %% compressed, 0, COMPRESSED protect, 0, PROTECT scriptlimits, 0, SCRIPTLIMITS enabledebugger, 0, ENABLEDEBUGGER enabledebugger2, 0, ENABLEDEBUGGER2 metadata, 0, METADATA fileattributes, 0, FILEATTRIBUTES importassets, 0, IMPORTASSETS exportassets, 0, EXPORTASSETS placemovieclip, 0, PLACEMOVIECLIP definemovieclip, 0, DEFINEMOVIECLIP definebutton, 0, DEFINEBUTTON end, 0, END # # action containers # initmovieclip, -1, INITMOVIECLIP frame, -1, FRAME on, -1, ON onclipevent, -1, ONCLIPEVENT # # action blocks # with, 1, WITH try, 1, TRY catch, 0, CATCH finally, 0, FINALLY ifframeloadedexpr, 1, IFFRAMELOADEDEXPR ifframeloaded, 1, IFFRAMELOADED settarget, 2, SETTARGET settargetexpr, 2, SETTARGETEXPR function, 1, FUNCTION function2, 1, FUNCTION2 # # special numbers # _nan, 0, _NAN positive_infinity, 0, POSITIVE_INFINITY negative_infinity, 0, NEGATIVE_INFINITY _nanf, 0, _NANF positive_infinityf, 0, POSITIVE_INFINITYF negative_infinityf, 0, NEGATIVE_INFINITYF # # properties # x_property, 0, X_PROPERTY y_property, 0, Y_PROPERTY xscale_property, 0, XSCALE_PROPERTY yscale_property, 0, YSCALE_PROPERTY width_property, 0, WIDTH_PROPERTY height_property, 0, HEIGHT_PROPERTY alpha_property, 0, ALPHA_PROPERTY visible_property, 0, VISIBLE_PROPERTY rotation_property, 0, ROTATION_PROPERTY highquality_property, 0, HIGHQUALITY_PROPERTY focusrect_property, 0, FOCUSRECT_PROPERTY soundbuftime_property, 0, SOUNDBUFTIME_PROPERTY currentframe_property, 0, CURRENTFRAME_PROPERTY totalframes_property, 0, TOTALFRAMES_PROPERTY target_property, 0, TARGET_PROPERTY framesloaded_property, 0, FRAMESLOADED_PROPERTY name_property, 0, NAME_PROPERTY droptarget_property, 0, DROPTARGET_PROPERTY url_property, 0, URL_PROPERTY quality_property, 0, QUALITY_PROPERTY xmouse_property, 0, XMOUSE_PROPERTY ymouse_property, 0, YMOUSE_PROPERTY # # button events # idletooverup, 0, BIDLETOOVERUP overuptoidle, 0, BOVERUPTOIDLE overuptooverdown, 0, BOVERUPTOOVERDOWN overdowntooverup, 0, BOVERDOWNTOOVERUP overdowntooutdown, 0, BOVERDOWNTOOUTDOWN outdowntooverdown, 0, BOUTDOWNTOOVERDOWN outdowntoidle, 0, BOUTDOWNTOIDLE idletooverdown, 0, BIDLETOOVERDOWN overdowntoidle, 0, BOVERDOWNTOIDLE keypress, 0, KEYPRESS # # key press event # _left, 0, _LEFT _right, 0, _RIGHT _home, 0, _HOME _end, 0, _END _insert, 0, _INSERT _delete, 0, _DELETE _backspace, 0, _BACKSPACE _enter, 0, _ENTER _up, 0, _UP _down, 0, _DOWN _pageup, 0, _PAGEUP _pagedown, 0, _PAGEDOWN _tab, 0, _TAB _escape, 0, _ESCAPE _space, 0, _SPACE # # movie clip events # load, 0, MCLOAD enterframe, 0, MCENTERFRAME unload, 0, MCUNLOAD mousemove, 0, MCMOUSEMOVE mousedown, 0, MCMOUSEDOWN mouseup, 0, MCMOUSEUP keydown, 0, MCKEYDOWN keyup, 0, MCKEYUP data, 0, MCDATA initialize, 0, MCINITIALIZE construct, 0, MCCONSTRUCT press, 0, MCPRESS release, 0, MCRELEASE releaseoutside, 0, MCRELEASEOUTSIDE rollover, 0, MCROLLOVER rollout, 0, MCROLLOUT dragover, 0, MCDRAGOVER dragout, 0, MCDRAGOUT # # push values # true, 0, TRUEVAL false, 0, FALSEVAL null, 0, NULLVAL undef, 0, UNDEFVAL # # actions # nextframe, 1, NEXTFRAME prevframe, 1, PREVFRAME gotoframe, 1, GOTOFRAME gotolabel, 1, GOTOLABEL geturl, 1, GETURL play, 1, PLAY stop, 1, STOP stopsounds, 1, STOPSOUNDS togglequality, 1, TOGGLEQUALITY dup, 1, DUP swap, 1, SWAP pop, 1, POP push, 1, PUSH constants, 1, CONSTANTPOOL setregister, 1, SETREGISTER callfunction, 1, CALLFUNCTION return, 1, RETURN newmethod, 1, NEWMETHOD callmethod, 1, CALLMETHOD bitwiseand, 1, BITWISEAND bitwiseor, 1, BITWISEOR bitwisexor, 1, BITWISEXOR modulo, 1, MODULO oldadd, 1, OLDADD add, 1, NEWADD newadd, 1, NEWADD oldlessthan, 1, OLDLESSTHAN lessthan, 1, NEWLESSTHAN newlessthan, 1, NEWLESSTHAN greaterthan, 1, GREATERTHAN oldequals, 1, OLDEQUALS equals, 1, NEWEQUALS newequals, 1, NEWEQUALS strictequals, 1, STRICTEQUALS tonumber, 1, TONUMBER tostring, 1, TOSTRING increment, 1, INCREMENT decrement, 1, DECREMENT typeof, 1, TYPEOF targetpath, 1, TARGETPATH enumeratevalue, 1, ENUMERATEVALUE enumerate, 1, ENUMERATE instanceof, 1, INSTANCEOF delete, 1, DELETE delete2, 1, DELETE2 new, 1, NEW initobject, 1, INITOBJECT initarray, 1, INITARRAY getmember, 1, GETMEMBER setmember, 1, SETMEMBER shiftleft, 1, SHIFTLEFT shiftright, 1, SHIFTRIGHT shiftright2, 1, SHIFTRIGHT2 var, 1, VAR varequals, 1, VAREQUALS subtract, 1, SUBTRACT multiply, 1, MULTIPLY divide, 1, DIVIDE and, 1, LOGICALAND or, 1, LOGICALOR not, 1, LOGICALNOT stringeq, 1, STRINGEQ stringlength, 1, STRINGLENGTH substring, 1, SUBSTRING int, 1, INT getvariable, 1, GETVARIABLE setvariable, 1, SETVARIABLE concat, 1, STRINGCONCAT getproperty, 1, GETPROPERTY setproperty, 1, SETPROPERTY duplicateclip, 1, DUPLICATECLIP removeclip, 1, REMOVECLIP trace, 1, TRACE startdrag, 1, STARTDRAGMOVIE stopdrag, 1, STOPDRAGMOVIE stringlessthan, 1, STRINGLESSTHAN stringgreaterthan, 1, STRINGGREATERTHAN random, 1, RANDOM mblength, 1, MBLENGTH ord, 1, ORD chr, 1, CHR gettimer, 1, GETTIMER mbsubstring, 1, MBSUBSTRING mbord, 1, MBORD mbchr, 1, MBCHR branch, 1, BRANCHALWAYS branchalways, 1, BRANCHALWAYS branchiftrue, 1, BRANCHIFTRUE geturl2, 1, GETURL2 loadvariables, 1, LOADVARIABLES loadvariablesnum, 1, LOADVARIABLESNUM loadmovie, 1, LOADMOVIE loadmovienum, 1, LOADMOVIENUM callframe, 1, CALLFRAME gotoandplay, 1, GOTOANDPLAY gotoandstop, 1, GOTOANDSTOP strictmode, 1, STRICTMODE implements, 1, IMPLEMENTS extends, 1, EXTENDS throw, 1, THROW cast, 1, CAST fscommand2, 1, FSCOMMAND2 swfaction, 1, SWFACTION # # file attributes (flash 8/9) # attrusenetwork, 0, ATTRUSENETWORK attrrelativeurls, 0, ATTRRELATIVEURLS attrsuppresscrossdomaincache, 0, ATTRSUPPRESSCROSSDOMAINCACHE attractionscript3, 0, ATTRACTIONSCRIPT3 attrhasmetadata, 0, ATTRHASMETADATA # # helper keywords # skip, 0, SKIP as, 0, AS post, 0, POST get, 0, GET hexdata, 0, HEXDATA off, 0, OFF from, 0, FROM recursion, 0, RECURSION timeout, 0, TIMEOUTflasm-1.62/assembler.flex0000666000175000017500000003140710632542740014051 0ustar pabspabs/* flasm, command line assembler & disassembler of flash actionscript bytecode Copyright (c) 2001 Opaque Industries, (c) 2002-2007 Igor Kogan All rights reserved. See LICENSE.TXT for terms of use. */ %option nounput %option warn %{ #include #include #include #include "util.h" #include "flasm.h" #include "assembler.tab.h" #ifdef MEMWATCH #include "memwatch.h" #endif int yyEOF = 0; char *lexBuffer = NULL; int lexBufferLen = 0; #define MAX_INCLUDE_DEPTH 10 #define MAX_LINE_LENGTH 65535 extern char *inputName; YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; static int include_stack_ptr = 0; static char *includenames[MAX_INCLUDE_DEPTH]; static int sLineNumber[MAX_INCLUDE_DEPTH]; static int column[MAX_INCLUDE_DEPTH]; static char szLine[MAX_LINE_LENGTH]; extern int yylex(void); int yywrap(void); void yyerror(char *msg); void warning(char *msg); char *FileName(void); int LineNumber(void); int ColumnNumber(void); inline void count(void); inline void newLine(void); static char sbuf[MAX_LINE_LENGTH]; static char *s; int numActions = 0; %} DIGIT [0-9] LABELD [a-zA-Z_][a-zA-Z0-9_]*: ID [a-zA-Z_][a-zA-Z0-9_]* %x MOVIEDECL %x STRINGSINGLEQ %x STRINGDOUBLEQ %x INCL %x BLOCKCOMMENT %% 0x[0-9a-fA-F]+ { count(); yylval.str = mstrdup(yytext+2); return HEX; } -{DIGIT}+ { count(); yylval.num = atol(yytext); return INTEGER; } {DIGIT}+ { count(); yylval.num = atol(yytext); return INTEGER; } -?{DIGIT}+"."{DIGIT}*(e("+"|"-"){DIGIT}{1,3})?f { count(); yylval.str = mstrdup(yytext); return FLOAT; } -?{DIGIT}+"."{DIGIT}*(e("+"|"-"){DIGIT}{1,3})? { count(); yylval.str = mstrdup(yytext); return DOUBLE; } c\:{DIGIT}+ { count(); yylval.num = atoi(yytext+2); return CONSTANT; } "r:" { count(); return REGISTER; } #INCLUDE[ \"\'\t]* { count(); BEGIN(INCL); } [^\"\'\t\r\n]+ { /* got the include file name */ count(); if (include_stack_ptr >= MAX_INCLUDE_DEPTH) yyerror("Includes nested too deeply"); if (access(yytext, R_OK)!=0) yyerror("Cannot open include file"); includenames[include_stack_ptr+1] = mstrdup(yytext); } [ \"\'\t\r]*\n { /* eat EOL and possible end quote */ newLine(); include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER; sLineNumber[include_stack_ptr] = 0; yyin = fopen(includenames[include_stack_ptr], "r"); if (!yyin) { include_stack_ptr--; sLineNumber[include_stack_ptr]--; yyerror("Problem opening include file"); } yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); BEGIN(INITIAL); } {LABELD} { count(); yylval.str = mstrdup(yytext); yylval.str[yyleng-1]=0; return LABEL; } {ID} { char buf[MAX_KEYWORD_LEN]; struct keyword *k; lowercase(yytext, buf); k = in_word_set(buf, yyleng); count(); if (k != NULL) { /* count numActions for ifFrameLoaded/IfFrameLoadedExpr */ if (k->numActions > 0) numActions += k->numActions; else if (k->numActions < 0) numActions = 0; return k->token; } else { yylval.str = mstrdup(yytext); return STRING; } } \' { count(); BEGIN STRINGSINGLEQ; s = sbuf; } \n { column[include_stack_ptr]++; yyerror("Unterminated string"); } \\b { count(); *s++ = '\b'; } \\t { count(); *s++ = '\t'; } \\n { count(); *s++ = '\n'; } \\f { count(); *s++ = '\f'; } \\r { count(); *s++ = '\r'; } \\\\ { count(); *s++ = '\\'; } \\\' { count(); *s++ = '\''; } \' { count(); *s = 0; yylval.str = mstrdup(sbuf); BEGIN(INITIAL); return STRING; } . { count(); *s++ = *yytext; } \" { count(); BEGIN STRINGDOUBLEQ; s = sbuf; } \n { column[include_stack_ptr]++; yyerror("Unterminated string"); } \\b { count(); *s++ = '\b'; } \\t { count(); *s++ = '\t'; } \\n { count(); *s++ = '\n'; } \\f { count(); *s++ = '\f'; } \\r { count(); *s++ = '\r'; } \\\\ { count(); *s++ = '\\'; } \\\" { count(); *s++ = '\"'; } \" { count(); *s = 0; yylval.str = mstrdup(sbuf); BEGIN(INITIAL); return STRING; } . { count(); *s++ = *yytext; } "movie"[ \t\v\f]+[\'\"] { count(); BEGIN MOVIEDECL; s = sbuf; return MOVIE; } \n { count(); yyerror("Unterminated movie name"); } [\'\"] { count(); *s = 0; yylval.str = mstrdup(sbuf); BEGIN(INITIAL); return MOVIENAME; } . { count(); *s++ = *yytext; } "movie"[ \t\v\f]+[^\n\\'\\"] { count(); yyerror("Movie name must be included in quotes"); } "/*" { count(); BEGIN(BLOCKCOMMENT); } [^*\n]* { count(); } [^*\n]*\n { newLine(); } "*"+[^*/\n]* { count(); } "*"+[^*/\n]*\n { newLine(); } "*"+"/" { count(); BEGIN(INITIAL); } "//" { int c; do c = input(); while (c != '\n' && c != 0 && c != EOF); newLine(); } ":" { count(); return ':'; } "(" { count(); return '('; } ")" { count(); return ')'; } "[" { count(); return '['; } "]" { count(); return ']'; } "," { count(); return ','; } "." { count(); return '.'; } "=" { count(); return '='; } [ \t\v\f] { column[include_stack_ptr]++; } \n { newLine(); } \r { } . { yyerror("Unrecognized character"); } <> { yy_delete_buffer(YY_CURRENT_BUFFER); if (--include_stack_ptr < 0) { yyEOF = 1; yyterminate(); } else { yy_switch_to_buffer(include_stack[include_stack_ptr]); } } %% int yywrap() { return(1); } char *FileName(void) { if (include_stack_ptr==0) return inputName; else return includenames[include_stack_ptr]; } int LineNumber(void) { return sLineNumber[include_stack_ptr]; } int ColumnNumber(void) { return column[include_stack_ptr]; } inline void newLine(void) { column[include_stack_ptr] = 0; sLineNumber[include_stack_ptr]++; } inline void count(void) { /* Count the characters to maintain the current column position */ column[include_stack_ptr] += yyleng; } void warning(char *msg) { strcpy(szLine, yytext + yyleng - ColumnNumber()); tellUser(0, "\n%s", szLine); if (yytext[0] != '\n') tellUser(0, "\n"); tellUser(0, "%*s", ColumnNumber(), "^"); tellUser(0, "\nLine %4.4d of %s:\nWarning: %s \n\n", LineNumber() + 1, FileName(), msg); } void yyerror(char *msg) { if (!yyEOF && strlen(yytext)) { strcpy(szLine, yytext + yyleng - ColumnNumber()); sLineNumber[include_stack_ptr]++; tellUser(0, "\n%s", szLine); if (yytext[0] != '\n') tellUser(0, "\n"); tellUser(0, "%*s", ColumnNumber(), "^"); tellUser(1, "\nLine %4.4d of %s:\n%s \n", LineNumber(), FileName(), msg); } else tellUser(1, "Unexpected EOF found in %s while looking for input.\n",inputName); } flasm-1.62/util.c0000666000175000017500000001634710632573643012351 0ustar pabspabs/* flasm, command line assembler & disassembler of flash actionscript bytecode Copyright (c) 2001 Opaque Industries, (c) 2002-2007 Igor Kogan, (c) 2005 Wang Zhen All rights reserved. See LICENSE.TXT for terms of use. */ #include #include #include #include #include #include #include "util.h" #include "action.h" #ifdef MEMWATCH #include "memwatch.h" #endif int byteorder; void checkByteOrder(void) { unsigned int x; unsigned char *p; x = 0x01020304; p = (unsigned char *)&x; if(*p == 1) byteorder = FLASM_BIG_ENDIAN; else byteorder = FLASM_LITTLE_ENDIAN; } int longintCompare(const void *ap, const void *bp) { unsigned long int a = *(const unsigned long int *) ap; unsigned long int b = *(const unsigned long int *) bp; if (a < b) return -1; else if (a > b) return 1; else return 0; } long int longintBinaryFind(const unsigned long int item, const unsigned long int *table, const long int tablesize) { long int left, right, middle; left = 0; right = tablesize - 1; while (left < right) { middle = (left + right) / 2; if (table[middle] < item) left = middle + 1; else right = middle; } if (table[right] == item) return(right); else return(-1); } char *strIstr(char *String, char *Pattern) { char *pptr, *sptr, *start; size_t slen, plen; if (String == NULL) return (NULL); for (start = String, pptr = Pattern, slen = strlen(String), plen = strlen(Pattern); slen >= plen; start++, slen--) { while (toupper(*start) != toupper(*Pattern)) { start++; slen--; if (slen < plen) return (NULL); } sptr = start; pptr = Pattern; while (toupper(*sptr) == toupper(*pptr)) { sptr++; pptr++; if (*pptr == '\0') return (start); } } return (NULL); } int strIcmp(const char *s, const char *t) { int d = 0; do { d = toupper(*s) - toupper(*t); } while (*s++ && *t++ && !d); return (d); } int strnIcmp(const char *s1, const char *s2, size_t len) { int d1, d2; if (len == 0) return 0; do { d1 = tolower(*s1++); d2 = tolower(*s2++); } while (--len && d1 && d2 && d1 == d2); return d1 - d2; } void lowercase(const char *str, char *buf) { while (*str) *buf++ = tolower(*str++); *buf = '\0'; } unsigned long int xtoi(const char *p) { unsigned long int num = 0; char c; while ((c = tolower(*p)) != '\0') { num <<= 4; if (isdigit(c)) num += c - '0'; else if (c >= 'a' && c <= 'f') num += c - 'a' + 10; else return 0; ++p; } return num; } uint16_t getWord(FILE *f) { unsigned int byte1 = (unsigned int) fgetc(f); unsigned int byte2 = (unsigned int) fgetc(f); return (uint16_t)(byte1 & 0xff) | ((byte2 & 0xff) << 8); } uint32_t getDoubleWord(FILE *f) { uint32_t low = getWord(f); uint32_t hi = getWord(f); return low | (hi << 16); } int goodID(const char *str) { char buf[MAX_KEYWORD_LEN]; char *bufp = buf; size_t len = 0; if (*str == '\0') return 1; if (!isalpha(*str) && *str != '_') return 0; do { ++len; *bufp++ = tolower(*str++); } while (*str && (isalnum(*str) || *str == '_')); if (*str != '\0') return 0; *bufp = '\0'; if (in_word_set(buf, len) != NULL) return 0; else return 1; } void parseTagHeader(FILE *f, unsigned int *typeptr, unsigned long int *lenptr) { unsigned int block = getWord(f);; *typeptr = block >> 6; *lenptr = block & 63; if (*lenptr == 63) *lenptr = getDoubleWord(f); } char *getTagString(unsigned int tag) { switch(tag) { case TAG_END: return"TAG_END"; case TAG_SHOWFRAME: return"TAG_SHOWFRAME"; case TAG_DEFINESHAPE: return"TAG_DEFINESHAPE"; case TAG_FREECHARACTER: return"TAG_FREECHARACTER"; case TAG_PLACEOBJECT: return"TAG_PLACEOBJECT"; case TAG_REMOVEOBJECT: return"TAG_REMOVEOBJECT"; case TAG_DEFINEBITS: return"TAG_DEFINEBITS"; case TAG_DEFINEBUTTON: return"TAG_DEFINEBUTTON"; case TAG_JPEGTABLES: return"TAG_JPEGTABLES"; case TAG_SETBACKGROUNDCOLOR: return"TAG_SETBACKGROUNDCOLOR"; case TAG_DEFINEFONT: return"TAG_DEFINEFONT"; case TAG_DEFINETEXT: return"TAG_DEFINETEXT"; case TAG_DOACTION: return"TAG_DOACTION"; case TAG_DEFINEFONTINFO: return"TAG_DEFINEFONTINFO"; case TAG_DEFINESOUND: return"TAG_DEFINESOUND"; case TAG_STARTSOUND: return"TAG_STARTSOUND"; case TAG_STOPSOUND: return"TAG_STOPSOUND"; case TAG_DEFINEBUTTONSOUND: return"TAG_DEFINEBUTTONSOUND"; case TAG_SOUNDSTREAMHEAD: return"TAG_SOUNDSTREAMHEAD"; case TAG_SOUNDSTREAMBLOCK: return"TAG_SOUNDSTREAMBLOCK"; case TAG_DEFINEBITSLOSSLESS: return"TAG_DEFINEBITSLOSSLESS"; case TAG_DEFINEBITSJPEG2: return"TAG_DEFINEBITSJPEG2"; case TAG_DEFINESHAPE2: return"TAG_DEFINESHAPE2"; case TAG_DEFINEBUTTONCXFORM: return"TAG_DEFINEBUTTONCXFORM"; case TAG_PROTECT: return"TAG_PROTECT"; case TAG_PATHSAREPOSTSCRIPT: return"TAG_PATHSAREPOSTSCRIPT"; case TAG_PLACEOBJECT2: return"TAG_PLACEOBJECT2"; case TAG_REMOVEOBJECT2: return"TAG_REMOVEOBJECT2"; case TAG_SYNCFRAME: return"TAG_SYNCFRAME"; case TAG_FREEALL: return"TAG_FREEALL"; case TAG_DEFINESHAPE3: return"TAG_DEFINESHAPE3"; case TAG_DEFINETEXT2: return"TAG_DEFINETEXT2"; case TAG_DEFINEBUTTON2: return"TAG_DEFINEBUTTON2"; case TAG_DEFINEBITSJPEG3: return"TAG_DEFINEBITSJPEG3"; case TAG_DEFINEBITSLOSSLESS2: return"TAG_DEFINEBITSLOSSLESS2"; case TAG_DEFINEEDITTEXT: return"TAG_DEFINEEDITTEXT"; case TAG_DEFINEVIDEO: return"TAG_DEFINEVIDEO"; case TAG_DEFINEMOVIECLIP: return"TAG_DEFINEMOVIECLIP"; case TAG_NAMECHARACTER: return"TAG_NAMECHARACTER"; case TAG_SERIALNUMBER: return"TAG_SERIALNUMBER"; case TAG_DEFINETEXTFORMAT: return"TAG_DEFINETEXTFORMAT"; case TAG_FRAMELABEL: return"TAG_FRAMELABEL"; case TAG_SOUNDSTREAMHEAD2: return"TAG_SOUNDSTREAMHEAD2"; case TAG_DEFINEMORPHSHAPE: return"TAG_DEFINEMORPHSHAPE"; case TAG_GENFRAME: return"TAG_GENFRAME"; case TAG_DEFINEFONT2: return"TAG_DEFINEFONT2"; case TAG_GENCOMMAND: return"TAG_GENCOMMAND"; case TAG_DEFINECOMMANDOBJ: return"TAG_DEFINECOMMANDOBJ"; case TAG_CHARACTERSET: return"TAG_CHARACTERSET"; case TAG_FONTREF: return"TAG_FONTREF"; case TAG_EXPORTASSETS: return"TAG_EXPORTASSETS"; case TAG_IMPORTASSETS: return"TAG_IMPORTASSETS"; case TAG_ENABLEDEBUGGER: return"TAG_ENABLEDEBUGGER"; case TAG_INITMOVIECLIP: return"TAG_INITMOVIECLIP"; case TAG_DEFINEVIDEOSTREAM: return"TAG_DEFINEVIDEOSTREAM"; case TAG_VIDEOFRAME: return"TAG_VIDEOFRAME"; case TAG_DEFINEFONTINFO2: return"TAG_DEFINEFONTINFO2"; case TAG_ENABLEDEBUGGER2: return"TAG_ENABLEDEBUGGER2"; case TAG_SCRIPTLIMITS: return"TAG_SCRIPTLIMITS"; case TAG_SETTABINDEX: return"TAG_SETTABINDEX"; case TAG_DEFINESHAPE4: return"TAG_DEFINESHAPE4"; case TAG_FILEATTRIBUTES: return"TAG_FILEATTRIBUTES"; case TAG_PLACEOBJECT3: return"TAG_PLACEOBJECT3"; case TAG_IMPORTASSETS2: return"TAG_IMPORTASSETS2"; case TAG_DEFINEFONTINFO3: return"TAG_DEFINEFONTINFO3"; case TAG_DEFINETEXTINFO: return"TAG_DEFINETEXTINFO"; case TAG_DEFINEFONT3: return"TAG_DEFINEFONT3"; case TAG_METADATA: return"TAG_METADATA"; case TAG_SLICE9: return"TAG_SLICE9"; case TAG_DEFINESHAPE5: return"TAG_DEFINESHAPE5"; case TAG_DEFINEMORPHSHAPE2: return"TAG_DEFINEMORPHSHAPE2"; case TAG_DEFINEBITSPTR: return"TAG_DEFINEBITSPTR"; default: return NULL; } } flasm-1.62/util.h0000666000175000017500000000364410632573563012353 0ustar pabspabs/* flasm, command line assembler & disassembler of flash actionscript bytecode Copyright (c) 2001 Opaque Industries, (c) 2002-2007 Igor Kogan, (c) 2005 Wang Zhen All rights reserved. See LICENSE.TXT for terms of use. */ #ifndef UTIL_H_INCLUDED #define UTIL_H_INCLUDED #include #include #define FLASM_VERSION "1.62" #define MALLOC_INC 16 #define OUTPUT_INC 2048 #define MAX_BUFFER 1000000 #define MAX_CONSTANTS 10000 #define MAX_FUNCDEPTH 10 #define MAX_REGISTERS 256 #define MAX_FUNCARGS 256 #define MAX_AUTO_REGS 6 #define MAX_INCLUDE_POOL 4000 #define MAX_KEYWORD_LEN 256 #define MAX_PATH_LEN 1000 #define DUPCLIP_NUMBER 0x4000 #define ANY_VALUE 10000000 #define INDENT_LEVEL 2 #define S16(p) ((p)[0] + (((p)[1])<<8)) #define S16signed(p) ((p)[0] + ((signed char)((p)[1])<<8)) #define SUREALLOC(num,storage,size) if ((num) % MALLOC_INC == 0) storage = realloc(storage, ((num) + MALLOC_INC + 1) * (size)) typedef unsigned char byte; enum { FLASM_BIG_ENDIAN, FLASM_LITTLE_ENDIAN }; extern int byteorder; struct keyword { const char *name; int numActions; int token; }; extern struct keyword *in_word_set(register const char *str, register unsigned int len); void checkByteOrder(void); int longintCompare(const void *ap, const void *bp); long int longintBinaryFind(const unsigned long int item, const unsigned long int *table, const long int tablesize); char *strIstr(char *String, char *Pattern); void lowercase(const char *str, char *buf); int strIcmp(const char *s, const char *t); int strnIcmp(const char *s1, const char *s2, size_t len); uint16_t getWord(FILE *f); uint32_t getDoubleWord(FILE *f); unsigned long int xtoi(const char *p); void parseTagHeader(FILE *f, unsigned int *typeptr, unsigned long int *lenptr); char *getTagString(unsigned int tag); int goodID(const char *str); #endif /* UTIL_H_INCLUDED */ flasm-1.62/CHANGES.TXT0000666000175000017500000002556610634442507012700 0ustar pabspabsFlasm 1.62 Bug fixes, thanks to Petr Ovtchenkov et al. Flasm 1.61 importAssets2 tag fix placeObject2 fix (Flash 4) Calculate path to the flasm.ini in a more reliable way Flasm 1.6 Flash 8: support for metadata, fileAttributes tags Flash 8: support for placeObject3, importAssets2 tags (Wang Zhen) "Update with Flasm and Preview" JSFL action now works in Flash 8 IDE Windows binary includes zlib 1.2.3 Fixed: names of register parameters of function2 may contain non-english characters Calling Flasm without a command: flasm foo.flm has the same effect as flasm -a foo.flm Flasm 1.52 Live testing embedded Flasm actions in Flash MX 2004 with JSFL command New -b mode: generate __bytecode__ instruction or byte sequence Support for FSCommand2 (introduced in FlashLite) Optionally show constant references instead of strings in disassembly Allow empty constant pools Support for multiple constant pools: Flasm will show constant references instead of strings Log file support keyPress events renamed for compatibility with Flash: _PGDN to _PAGEDOWN, _DOWN to _PAGEDOWN Fixed: keyPress event reported "unknown key" for some working keys Allow hex key codes: -a will work even if the key in keyPress is unsupported Fixed: #include wouldn't work in embedded mode function2 will now handle all kinds of automatic parameters including strange species that are neither preloaded nor suppressed Max line length for flm files set to 65535 (was 4096) If function name is a keyword or contains non-english characters, it will be quoted automatically in disassembly Exception names after try keyword are now quoted in disassembly Search for flasm.ini in the current directory first, then look at executable's path Flasm 1.51 scriptLimits tag support Corrected erroneous placing of exportAssets/importAssets tags in some situations Always write long length for soundStreamBlock because of Flash Player bug Flasm 1.5 Full Flash MX 2004 support New actions: implements, extends, try/catch/finally, throw, cast, function2 New event: onClipEvent construct Flasm now uses gperf together with flex for parsing keywords. It means Flasm got smaller and faster. I've dropped support for $flasm, $end and $include macros. Inserting Flasm code into ActionScript is now even easier. Windows version: zlib inside, no external zlib.dll needed; update to zlib 1.1.4 Large number of bug/crash fixes Any number of labels possible Show numerical jumps to the middle of action with warnings in disassembly. Better memory handling Try to recover broken constant pool declaration (uncomplete). Report if file to update is in use. New flasm.ini parameters: showoffset, hexoffset, literalregisters (suggested by Casper Schuirink), clearregisterargs You (and me) may now add comments to flasm.ini. Every line starting with # character is considered a comment. Button keyPress events renamed for compatibility with Flash: _DEL to _DELETE, _DN to _DOWN, _INS to _INSERT. Added missing _ESCAPE key. Support for exportAssets and importAssets tags Show warnings about everything unusual (events/actions/key codes etc.) to the console. Flasm 1.42 strictMode ON / strictMode OFF (canceled in later Flash versions) New bison fix stringGreaterThan action, code 0x68 Numerical jumps support: positive/negative values, offset starts from the branch end, i.e. branch 0 goes to the next instruction as if no branch were present. Number of labels per block increased to 2048. Flasm 1.41 Flasm now handles compressed sfws transparently - no need to decompress before disassembling or updating. New attribute "compressed" added to the movie declaration (first line of your disassembly). There is a really strange bug in Flash Player (MX too): it always expects long length for gif/png images. SWF File Format spec allows storing small lengthes in a more compact manner than big ones. In a special case of very small images (smaller than 64 byte) Flasm would use short length, and Flash Player would be unable to display these images. Flasm 1.41 takes care of this bug now. Thanks to Marcelo Vomaro for pointing me to the issue. Liam Morley has found a problem with gotoAndStop/gotoAndPlay actions. I've tracked it down to the following: undocumented argument for SWF action gotoFrame2 exists, containing the number of frames to add to the expression on stack. The behavior is described here. Updated Testing embedded actions paragraph for Flash MX, Resources, ActionScript protection, and Project state sections. Flasm 1.4 Flasm 1.4 fully supports new features introduced by Flash MX. Movie clips can handle all button events now, so onClipEvent format has changed inside of SWF. Note buttons do not support movie clip events! New initialize event - component parameters are no longer set with load event unlike used to be for smartclips in Flash 5, but with new initialize event. initMovieClip tag - new tag deals with component initialization: look for #initclip .. #endinitclip in Flash MX help. New actions: greaterThan, instanceOf, strictEquals, enumerateValue. Debugger now utilizes enableDebugger2 tag. Flash MX implements standard zlib compression. Flasm fully supports it. Flasm 1.36 Support for passwords in protect tag added New enableDebugger tag. Now it's easy to include disassemble command in Windows context menu for swf extension. Thanks to Pavils Jurjans for suggesting the feature. Flasm 1.35 After Cygwin people have updated their product, and after I've fixed some minor bugs in Flasm, Flasm executable compiled with Cygwin finally supports embedding mode and interacts with Flash properly. So I moved from djgpp to Cygwin/ming32. What it means to end users: smaller executable (uses Windows libraries), no limitation for path length in embedding mode. What it means to developers: djgpp installation on win 2000 was almost impossible. Now you can compile with a standard Cygwin environment. I've put the Flasm source into CVS on sourceforge. Flasm now uses ini file instead of environment variables in testing mode. Function parameters are included in quotes in disassembly to avoid name collisions with your ActionScript. Overflowed constant pools no more crash Flasm disassembler. You will be also not allowed to assemble pools larger than 64k. Unclosed setTarget/setTargetExpr blocks created by older Flash versions will be completed automatically. Bug in swfAction fixed. The protect tag is shown in disassembly. Former strange looking waitForFrameExpr/waitForFrame .. else skip .. actions are renamed to ifFrameLoaded/ifFrameLoadedExpr and support conventional syntax: ifFrameLoaded frameNum .. end, ifFrameLoadedExpr .. end As usual, documentation updates. I know it's hard to find what's new because of the structure I've choosen for this page - so sorry. Compare old and new ones with diff :) Flasm 1.32 Better handling of floating-point values updated, infinity and not-a-number constants are now supported, Number.MAX_VALUE and Number.MIN_VALUE are correctly resolved. I've finally understood that push type 1 (property) actually pushes a single-precision float value. It caused confusion before, because property constants had strange meanings if treated as integers. Look at updated push and getProperty/setProperty topics for details. "Label defined twice" and other error messages added. New FLABROWSER environment variable to enable showing Flash help while using Flasm in embedded mode. Increased portability across systems: Makefile shows no warnings; some problematic math functions redefined. Escape characters are recognized in getUrl too. Embedding mode: multiple user constant declarations allowed now in one frame. They all will be added to the main pool for this frame. Useful when, say, many functions with independent declarations are inserted with $include. Some bugs with $include fixed. On Windows 2000 dos box was closing immediately after Flasm reported errors in embedding mode. Flasm 1.32 now waits for user key press (Windows version only). Don't know if this misbehavior occurs on mac too, please report. Empty movie clip events compile without errors. action keyword used in Flasm for unknown actions support renamed to swfAction to prevent collision with Branden Hall's ACK eventEngine. The maximum number of labels per action block increased from 256 to 1024. The former caused crashes with very large scripts. Numbers as movie clip instance names are supported now. Flasm compiled Flash 4-style oldAdd action improperly as Flash 5-style add - fixed. Documentation updates: real-life optimization example etc. Flasm 1.3 Nested includes supported with maximal depth 10. Support for Flasm macros in Flash IDE is here. Finally got line numbers for error messages working! Don't laugh, it's a challenge with flex. Line numbers and file names are reported for included files too. Error messages are sent to the console even if the output of disassembler is redirected to the file. Some specific messages added instead of standard "parse error". Please tell me where you would like to see warnings. push type 9 discovered, which does 2-byte constant pool lookup. Corrected the wrong assembly of gotoAndPlay/gotoAndStop actions thanks to Sven Knig who noticed the bug. Support for unknown actions added. Working mac executable (compiled on SourceForge's compiler farm). No more need to compile Flasm yourself. For those who must compile: Makefile adjusted to work for all systems. Problems with large ActionScripts fixed. My test file contains 93 kb compiled ActionScript in one frame only. It works fine now. Flasm 1.22 Correct handling of property constants: more properties defined - the list is full now, NAME_PROPERTY fixed. Escape characters \b, \f, \n, \r, \t and \\ defined. An ActionScript string containing those characters will now be disassembled without producing weird line breaks or other anomalities. Backup of original swf is created before update. Flasm 1.21 The famous double nots written by Flash will be now automatically removed at assembling time. So forget them and concentrate on optimization! Label handling improved. Flasm 1.2 sometimes forgot to write labels at the end of action block. Fixed: on Windows 2000 Flasm 1.2 could not update the original swf and you had to manually rename the temp file created foo.$$$ into foo.swf. Flasm 1.21 crashes no more if called without arguments or if input file is missing. disassembler no more writes nonsense 53.39999999999964 values instead of 53.4. Added toString action corresponding to a = String(b); in ActionScript. Flasm 1.2 It was my first "official" version, the main difference to Dave's original was the ability to deal with the whole swf. Event disassembly added, some missing bytecodes, few errors fixed. Don't remember all the changes exactly, this page did not exist yet. flasm-1.62/logo.gif0000600000175000017500000000043210634444407012624 0ustar pabspabsGIF89a#ճʤwiZK|A8vrǺ/RS7/{ *V*I&I"|BU  U2s> z2 ^cEEa28^= '"'k5'Yts 1m%XC>  >+ p+ wB (z ;Z/;flasm-1.62/classic.css0000600000175000017500000000155410634444407013336 0ustar pabspabsbody {background-color:#001144;} a {color:#FD4D30;text-decoration:none;} a:hover {text-decoration:underline;} code {color:#666666;font-size:90%} h3, h4, h5 {color:#001455;} ul {margin-left:1.2em;padding-left:0} #menu {color:#FFFFFF;font-family:Verdana;line-height:22px;} #menu a:hover {color:#FFF7E0;text-decoration:none;} #subhead {color:#FFF7E0;font-family:Georgia;line-height:20px;} #menutd {background-color:#921212;} #contenttd {background-color:#FFF7E0;color:#001144;font-family:Georgia;line-height:22px;} #whitehr {color:#FFFFFF;background:#FFFFFF;border:0;height:1px;} #bluehr {color:#001144;background:#001144;border:0;height:1px;} @media print { body {background:white;font-size:12pt;margin:0px;padding:0px;} a {text-decoration:underline;} #subhead,#contenttd {margin:0px;padding:0px} code {font-size:9pt} #menutd, #whitehr {display:none} }flasm-1.62/unflasm.c0000666000175000017500000015075710632572057013043 0ustar pabspabs/* flasm, command line assembler & disassembler of flash actionscript bytecode Copyright (c) 2001 Opaque Industries, (c) 2002-2007 Igor Kogan, (c) 2005 Wang Zhen All rights reserved. See LICENSE.TXT for terms of use. */ #include #include #include #include #include #include #include #ifdef MEMWATCH #include "memwatch.h" #endif #include "util.h" #include "action.h" void disassembleSWF(FILE *f, char *fname); void skipProtected(FILE *f, unsigned long int length); extern void tellUser(int isError, char *s, ...); static int indent = 1; static int targetIndent = 0; static long int swfabspos = -1; static long int swfrelpos = -1; int swfVersion; static int showLiterals; static int nDict = 0; struct _dict { char *value; int count; }; static struct _dict dictionary[MAX_CONSTANTS]; static char *flasmDict = NULL; static long int numLabels = 0; static long int curLabel = 0; static unsigned long int *labels = NULL; static unsigned long int conststart = 0, constend = 0; /* from flasm.c */ extern int mode; extern char wasCompressed; /* from flasm.c <- flasm.ini */ extern int showoffset, hexoffset, literalregisters, literalconstants; enum { eventLoad = 0x01, eventEnterFrame = 0x02, eventUnload = 0x04, eventMouseMove = 0x08, eventMouseDown = 0x10, eventMouseUp = 0x20, eventKeyDown = 0x40, eventKeyUp = 0x80, eventData = 0x100, eventInitialize = 0x200, /* flash 6/7 only; flash 5 stores smartClip parameters in eventLoad */ eventPress = 0x400, eventRelease = 0x800, eventReleaseOutside = 0x1000, eventRollOver = 0x2000, eventRollOut = 0x4000, eventDragOver = 0x8000, eventDragOut = 0x10000, eventKeyPress = 0x20000, eventConstruct = 0x40000 /* flash 7 only */ }; enum { IdleToOverUp = 0x01, OverUpToIdle = 0x02, OverUpToOverDown = 0x04, OverDownToOverUp = 0x08, OverDownToOutDown = 0x10, OutDownToOverDown = 0x20, OutDownToIdle = 0x40, IdleToOverDown = 0x80, OverDownToIdle = 0x100 }; enum { keyLeft = 1, keyRight = 2, keyHome = 3, keyEnd = 4, keyInsert = 5, keyDelete = 6, keyBackspace = 8, keyEnter = 13, keyUp = 14, keyDown = 15, keyPageUp = 16, keyPageDown = 17, keyTab = 18, keyEscape = 19, keySpace = 32 }; /* names of automatic values for function2 */ char *arNames[] = {"this", "arguments", "super", "_root", "_parent", "_global"}; /* preload flags set for particular automatic value */ unsigned int arPreFlags[] = {0x0001, 0x0004, 0x0010, 0x0040, 0x0080, 0x0100}; /* suppress flags set for particular automatic value */ unsigned int arSupFlags[] = {0x0002, 0x0008, 0x0020, 0x0000, 0x0000, 0x0000}; void skipProtected(FILE *f, unsigned long int length) { if (fseek(f, length, SEEK_CUR) != 0) tellUser(1, "Unexpected end of file"); } static void printIndent(int i) { size_t buflen = INDENT_LEVEL*i; static long int lastpos = 0; long int swfpos; if (showoffset > 0) { if (showoffset == 1) swfpos = swfrelpos; else swfpos = swfabspos+swfrelpos; /* don't print adress if unchanged */ if (swfpos >= 0 && (swfpos > lastpos || swfpos == 0)) { lastpos = swfpos; if (hexoffset == 1) /* big endian issues? */ printf("%04X%04X", (unsigned int) (0xFFFF & (swfpos>>16)), (unsigned int) (0xFFFF & swfpos)); else printf("%08li", swfpos); } else buflen += 8; } if (i>0) { char buf[buflen]; memset(buf, ' ', buflen); fwrite(buf, sizeof(char), buflen, stdout); } } static void print(char *s, ...) { va_list ap; printIndent(indent); va_start(ap, s); vprintf(s, ap); va_end(ap); } static void printstr(char *str) { char buf[strlen(str)*2+3]; char *bufp = buf, *bufstr = str; *bufp++ = '\''; while (*bufstr++ != '\0') { switch (*(bufstr-1)) { case '\b': *bufp++ = '\\'; *bufp++ = 'b'; break; case '\t': *bufp++ = '\\'; *bufp++ = 't'; break; case '\n': *bufp++ = '\\'; *bufp++ = 'n'; break; case '\f': *bufp++ = '\\'; *bufp++ = 'f'; break; case '\r': *bufp++ = '\\'; *bufp++ = 'r'; break; case '\'': *bufp++ = '\\'; *bufp++ = '\''; break; case '\\': *bufp++ = '\\'; *bufp++ = '\\'; break; default: *bufp++ = *(bufstr-1); } } *bufp++ = '\''; fwrite(buf, sizeof(char), bufp-buf, stdout); } static void printFloat(float f, int intCast) { char s[60]; char *sp, *xpn; if (f != f) printf("_NANF"); else if (f == (1.0f / 0.0f)) printf("POSITIVE_INFINITYF"); else if (f == (-1.0f / 0.0f)) printf("NEGATIVE_INFINITYF"); else { sprintf(s, "%#g", (double) f); if ((xpn = strchr(s, 'e')) == NULL) sp = s + strlen(s) - 1; else sp = xpn - 2; while (*sp == '0') --sp; if (intCast == 1) { if (*sp == '.') --sp; *(sp + 1) = '\0'; printf("%s", s); } else { if (*sp == '.') *++sp = '0'; *++sp = '\0'; printf("%s", s); if (xpn != NULL) printf("%s", xpn); putchar('f'); } } } static unsigned int bitPos; static unsigned int bitBuf; static void InitBits(FILE *f) { bitPos = 8; bitBuf = ((unsigned int) fgetc(f)) & 0xff; } static unsigned int getBits(FILE *f, unsigned int n) { unsigned long int v = 0; while (n > bitPos) { n -= bitPos; v |= bitBuf << n; bitBuf = ((unsigned int) fgetc(f)) & 0xff; bitPos = 8; } bitPos -= n; v |= bitBuf >> bitPos; bitBuf &= 0xff >> (8 - bitPos); /* never need more than 16 bits */ return (unsigned int) v & 0xffff; } static void printFrameNum(unsigned int frameNum) { printf("\n"); printIndent(indent); printf("frame %u\n", frameNum); } static void addLabel(unsigned long int offset) { long int i; for (i = 0; i < numLabels; ++i) { if (labels[i] == offset) return; } SUREALLOC(numLabels, labels, sizeof (unsigned long int)); if (labels == NULL) tellUser(1, "Not enough memory to store all labels"); labels[numLabels++] = offset; } static void processASMLine(char *line); static void includeFile(const char *ifilename) { FILE *ifile; char buf[256]; while (*ifilename == ' ' || *ifilename == '\t') ifilename++; if ((ifile = fopen(ifilename, "rt")) == NULL) tellUser(1, "Couldn't include file: %s", ifilename); printf("\n// start of %s\n", ifilename); while (fgets(buf, 256, ifile)) processASMLine(buf); printf("\n// end of %s\n", ifilename); fclose(ifile); } static void processASMLine(char *line) { char *ci = strIstr(line, "constants "); char *fi = strIstr(line, "#include "); if ((ci == NULL) || (constend == 0) || !showLiterals) { /* line contains no 'constants' or prev constant declaration absent */ /* or multiple/broken constant pools found in action block */ if (fi != NULL) includeFile(fi + 9); else printf("%s", line); } else if (flasmDict == NULL) { /* start dict */ flasmDict = strdup(ci + 10); } else { /* add user constants */ flasmDict = realloc(flasmDict, strlen(flasmDict) + strlen(ci + 10) + 3); strcat(flasmDict, ", "); strcat(flasmDict, ci + 10); } } static void checkLabel(unsigned long int addr) { if (curLabel < numLabels) { while (addr > labels[curLabel]) { printIndent(indent-1); printf(" %s%li: // Wild label in the middle of an action, now placed before next action\n", mode < MODE_UPDATE ? "label" : "lbl", ++curLabel); tellUser(0, "Branch into the middle of an action, %s%li (off by %i bytes) is placed before next action", mode < MODE_UPDATE ? "label" : "lbl", curLabel, (int)(addr - labels[curLabel-1])); } if (addr == labels[curLabel]) { printIndent(indent-1); /* make sure generated labels don't match user labels */ printf(" %s%li:\n", mode < MODE_UPDATE ? "label" : "lbl", ++curLabel); } } } static byte *buffer; static void printActionRecord(byte *p, Action type, unsigned int *lenptr, char **regtable); static unsigned long int printActions(byte *p, unsigned long int length, unsigned long int maxActions, char **regtable) { /* processes also nested blocks like 'function', 'with', and 'ifFrameLoaded' stops at given length (in bytes) or after given numActions give ANY_VALUE as parameter, if one of them is unused */ unsigned long int i = 0; unsigned long int curAction = 0; unsigned int blocklen; while ((i < length) && (curAction < maxActions)) { Action type = (Action) p[i]; checkLabel(p + i - buffer); swfrelpos = p + i - buffer; ++i; if ((type & 0x80) != 0) { blocklen = S16(p + i); i += 2; } else blocklen = 0; if (i+blocklen <= length) printActionRecord(p + i, type, &blocklen, regtable); else { tellUser(0, "Disassembly may be incomplete: wrong action length encountered"); return length; } i += blocklen; curAction++; } checkLabel(p + i - buffer); return i; } static void printActionRecord(byte *p, Action type, unsigned int *lenptr, char **regtable) { unsigned int len = *lenptr; switch (type) { case SWFACTION_ADD: print("oldAdd\n"); break; case SWFACTION_SUBTRACT: print("subtract\n"); break; case SWFACTION_MULTIPLY: print("multiply\n"); break; case SWFACTION_DIVIDE: print("divide\n"); break; case SWFACTION_EQUALS: print("oldEquals\n"); break; case SWFACTION_LESSTHAN: print("oldLessThan\n"); break; case SWFACTION_LOGICALAND: print("and\n"); break; case SWFACTION_LOGICALOR: print("or\n"); break; case SWFACTION_LOGICALNOT: print("not\n"); break; case SWFACTION_STRINGEQ: print("stringEq\n"); break; case SWFACTION_STRINGLENGTH: print("stringLength\n"); break; case SWFACTION_SUBSTRING: print("substring\n"); break; case SWFACTION_INT: print("int\n"); break; case SWFACTION_POP: print("pop\n"); break; case SWFACTION_SWAP: print("swap\n"); break; case SWFACTION_INITOBJECT: print("initObject\n"); break; case SWFACTION_INITARRAY: print("initArray\n"); break; case SWFACTION_GETVARIABLE: print("getVariable\n"); break; case SWFACTION_SETVARIABLE: print("setVariable\n"); break; case SWFACTION_STRINGCONCAT: print("concat\n"); break; case SWFACTION_GETPROPERTY: print("getProperty\n"); break; case SWFACTION_SETPROPERTY: print("setProperty\n"); break; case SWFACTION_DUPLICATECLIP: print("duplicateClip\n"); break; case SWFACTION_REMOVECLIP: print("removeClip\n"); break; case SWFACTION_TRACE: print("trace\n"); break; case SWFACTION_STARTDRAGMOVIE: print("startDrag\n"); break; case SWFACTION_STOPDRAGMOVIE: print("stopDrag\n"); break; case SWFACTION_STRINGLESSTHAN: print("stringLessThan\n"); break; case SWFACTION_STRINGGREATERTHAN: print("stringGreaterThan\n"); break; case SWFACTION_RANDOM: print("random\n"); break; case SWFACTION_MBLENGTH: print("mbLength\n"); break; case SWFACTION_ORD: print("ord\n"); break; case SWFACTION_CHR: print("chr\n"); break; case SWFACTION_GETTIMER: print("getTimer\n"); break; case SWFACTION_MBSUBSTRING: print("mbSubstring\n"); break; case SWFACTION_MBORD: print("mbOrd\n"); break; case SWFACTION_MBCHR: print("mbChr\n"); break; case SWFACTION_NEXTFRAME: print("nextFrame\n"); break; case SWFACTION_PREVFRAME: print("prevFrame\n"); break; case SWFACTION_PLAY: print("play\n"); break; case SWFACTION_STOP: print("stop\n"); break; case SWFACTION_TOGGLEQUALITY: print("toggleQuality\n"); break; case SWFACTION_STOPSOUNDS: print("stopSounds\n"); break; /* ops with args */ case SWFACTION_PUSHDATA: { byte pushtype; byte *start = p; long int pushstart; int n = 0; /* may need to go back and erase push while processing flasm macros */ pushstart = ftell(stdout); print("push "); while (p < start + len) { switch (pushtype = *p++) { case 0: /* string */ { char *d = (char *) p; if (mode >= MODE_UPDATE && (Action) start[len] == SWFACTION_POP) { fseek(stdout, pushstart, SEEK_SET); /* go back to overwrite 'push' */ processASMLine(d); p = start + len; /* skip to the end of push statement */ *lenptr += 1; } else { printf("%s", n++ > 0 ? ", " : ""); printstr(d); p += strlen(d) + 1; } break; } case 1: /* float, used by flash for properties only */ { float f; double prop; if (byteorder == FLASM_BIG_ENDIAN) { byte *fp = (byte *) (&f); fp[0] = p[3]; fp[1] = p[2]; fp[2] = p[1]; fp[3] = p[0]; } else f = *(float *) p; printf("%s", (n++ > 0) ? ", " : ""); if (modf((double) f, &prop) == 0) { /* integer, most likely property */ switch ((int) prop) { case 0: printf("X_PROPERTY"); break; case 1: printf("Y_PROPERTY"); break; case 2: printf("XSCALE_PROPERTY"); break; case 3: printf("YSCALE_PROPERTY"); break; case 4: printf("CURRENTFRAME_PROPERTY"); break; case 5: printf("TOTALFRAMES_PROPERTY"); break; case 6: printf("ALPHA_PROPERTY"); break; case 7: printf("VISIBLE_PROPERTY"); break; case 8: printf("WIDTH_PROPERTY"); break; case 9: printf("HEIGHT_PROPERTY"); break; case 10: printf("ROTATION_PROPERTY"); break; case 11: printf("TARGET_PROPERTY"); break; case 12: printf("FRAMESLOADED_PROPERTY"); break; case 13: printf("NAME_PROPERTY"); break; case 14: printf("DROPTARGET_PROPERTY"); break; case 15: printf("URL_PROPERTY"); break; case 16: printf("HIGHQUALITY_PROPERTY"); break; case 17: printf("FOCUSRECT_PROPERTY"); break; case 18: printf("SOUNDBUFTIME_PROPERTY"); break; case 19: printf("QUALITY_PROPERTY"); break; case 20: printf("XMOUSE_PROPERTY"); break; case 21: printf("YMOUSE_PROPERTY"); break; default: printFloat(f, 0); } } else printFloat(f, 0); p += 4; break; } case 2: /* null */ printf("%sNULL", (n++ > 0) ? ", " : ""); break; case 3: /* undefined */ printf("%sUNDEF", (n++ > 0) ? ", " : ""); break; case 4: /* register */ if (n++ > 0) printf(", "); if (literalregisters && regtable != NULL && regtable[*p] != NULL && *regtable[*p] != '\0') { if (goodID(regtable[*p])) printf("r:%s", regtable[*p]); else printf("r:'%s'", regtable[*p]); } else { printf("r:%i", *p); } p++; break; case 5: /* boolean */ if (*p++) printf("%sTRUE", (n++ > 0) ? ", " : ""); else printf("%sFALSE", (n++ > 0) ? ", " : ""); break; case 6: /* double */ { double d; byte *dp = (byte *) (&d); char s[100]; char *sp, *xpn; if (byteorder == FLASM_BIG_ENDIAN) { dp[0] = p[3]; dp[1] = p[2]; dp[2] = p[1]; dp[3] = p[0]; dp[4] = p[7]; dp[5] = p[6]; dp[6] = p[5]; dp[7] = p[4]; } else { dp[0] = p[4]; dp[1] = p[5]; dp[2] = p[6]; dp[3] = p[7]; dp[4] = p[0]; dp[5] = p[1]; dp[6] = p[2]; dp[7] = p[3]; } /* the old way without '1.5e-24'-like notation fprec = 15-floor(log10(fabs(d))); sprintf(s,"%.*f",fprec,d); */ printf("%s", (n++ > 0) ? ", " : ""); if (d == 0) { if (mode < MODE_UPDATE) printf("0.0"); else printf("0"); /* save some bytes in update mode - integer instead of double */ } else if (d != d) printf("_NAN"); else if (d == (1.0 / 0.0)) printf("POSITIVE_INFINITY"); else if (d == (-1.0 / 0.0)) printf("NEGATIVE_INFINITY"); else { sprintf(s, "%#.*g", 16, d); if ((xpn = strchr(s, 'e')) == NULL) sp = s + strlen(s) - 1; else sp = xpn - 2; /* one digit less precision for exp form values preventing MAX_NUMBER rounding to INFINITY */ while (*sp == '0') /* delete 0's at the end of the number or mantissa */ --sp; if (*sp == '.') /* expand values like "1." to "1.0" */ *++sp = '0'; *++sp = '\0'; /* terminate buffer (exponent is cutted off) */ printf("%s", s); if (xpn != NULL) /* if exponent here, print it */ printf("%s", xpn); } p += 8; break; } case 7: /* integer */ { int i; if (byteorder == FLASM_BIG_ENDIAN) { byte *ip = (byte *) (&i); ip[0] = p[3]; ip[1] = p[2]; ip[2] = p[1]; ip[3] = p[0]; } else i = *(int *) p; printf("%s%i", (n++ > 0) ? ", " : "", i); p += 4; break; } case 8: /* dictionary, 1-byte reference */ { char *d; if (showLiterals && (*p < nDict)) { d = dictionary[*p].value; if (mode >= MODE_UPDATE && (Action) start[len] == SWFACTION_POP) { fseek(stdout, pushstart, SEEK_SET); /* go back to overwrite 'push' */ processASMLine(d); p = start + len; /* skip to the end of push statement */ *lenptr += 1; } else { printf("%s", n++ > 0 ? ", " : ""); printstr(d); dictionary[*p].count++; /* constant used one more time */ p++; } } else { printf("%sc:%u", n++ > 0 ? ", " : "", *p); p++; } break; } case 9: /* dictionary, 2-byte reference */ { char *d; if (showLiterals && (S16(p) < nDict)) { d = dictionary[S16(p)].value; if (mode >= MODE_UPDATE && (Action) start[len] == SWFACTION_POP) { fseek(stdout, pushstart, SEEK_SET); /* go back to overwrite 'push' */ processASMLine(d); p = start + len; /* skip to the end of push statement */ *lenptr += 1; } else { printf("%s", n++ > 0 ? ", " : ""); printstr(d); dictionary[S16(p)].count++; /* constant used one more time */ p += 2; } } else { printf("%sc:%u", n++ > 0 ? ", " : "", S16(p)); p += 2; } break; } default: printf("%s%s // unknown push type %i: rest of push skipped", (n++ > 0) ? ", " : "", "???", pushtype); tellUser(0, "Unknown push type %i: rest of push skipped", pushtype); p = start + len; } } putchar('\n'); break; } case SWFACTION_GOTOFRAME: print("gotoFrame %u\n", S16(p)); p += 2; break; case SWFACTION_GETURL: { char *url = (char *) p; p += strlen(url) + 1; print("getURL "); printstr(url); putchar(' '); printstr((char *) p); putchar('\n'); break; } case SWFACTION_BRANCHALWAYS: { long int l = longintBinaryFind((unsigned long int)(p + 2 + S16signed(p) - buffer), labels, numLabels); if (l >= 0) { print("branch %s%i", mode < MODE_UPDATE ? "label" : "lbl", l + 1, labels); if (showoffset == 0) putchar('\n'); else printf(" // offset %i\n", S16signed(p)); } else { print("branch %i // branch target not found\n", S16signed(p)); tellUser(0, "branch target not found: %li", S16signed(p)); } break; } case SWFACTION_BRANCHIFTRUE: { long int l = longintBinaryFind((unsigned long int)(p + 2 + S16signed(p) - buffer), labels, numLabels); if (l >= 0) { print("branchIfTrue %s%i", mode < MODE_UPDATE ? "label" : "lbl", l + 1, labels); if (showoffset == 0) putchar('\n'); else printf(" // offset %i\n", S16signed(p)); } else { print("branchIfTrue %i // branch target not found\n", S16signed(p)); tellUser(0, "branchIfTrue target not found: %li", S16signed(p)); } break; } case SWFACTION_GETURL2: { byte flags = *p; switch (flags) { case 0: print("getURL2\n"); break; case 1: print("getURL2 GET\n"); break; case 2: print("getURL2 POST\n"); break; case 0x40: print("loadMovie\n"); break; case 0x41: print("loadMovie GET\n"); break; case 0x42: print("loadMovie POST\n"); break; case 0x80: print("loadVariablesNum\n"); break; case 0x81: print("loadVariablesNum GET\n"); break; case 0x82: print("loadVariablesNum POST\n"); break; case 0xC0: print("loadVariables\n"); break; case 0xC1: print("loadVariables GET\n"); break; case 0xC2: print("loadVariables POST\n"); break; default: print("getURL2 0x%x // unknown flag\n", flags); tellUser(0, "Unknown getURL2 flag: 0x%x"); } break; } case SWFACTION_CALLFRAME: print("callFrame\n"); break; case SWFACTION_GOTOEXPRESSION: print("goto"); if (*p == 0) puts("AndStop"); else if (*p == 1) puts("AndPlay"); else if ((*p == 2) && (len == 3)) { /* undocumented additional argument - the number of frames in all previous scenes */ p++; printf("AndStop skip %u\n", S16(p)); } else if ((*p == 3) && (len == 3)) { /* undocumented additional argument - the number of frames in all previous scenes */ p++; printf("AndPlay skip %u\n", S16(p)); } else { /* what the hell is it? assume andPlay, since flag>1 */ printf("AndPlay // unknown goto flag %i\n", *p); tellUser(0, "Unknown goto flag %i", *p); } break; case SWFACTION_IFFRAMELOADED: { unsigned int frame = S16(p); byte frameLoadedActions; p += 2; print("ifFrameLoaded %u\n", frame); ++indent; frameLoadedActions = *p++; *lenptr += printActions(p, ANY_VALUE, (unsigned long int) frameLoadedActions, regtable); --indent; print("end // of ifFrameLoaded %u\n\n", frame); break; } case SWFACTION_IFFRAMELOADEDEXPRESSION: { byte frameLoadedActions; print("ifFrameLoadedExpr\n"); ++indent; frameLoadedActions = *p++; *lenptr += printActions(p, ANY_VALUE, (unsigned long int) frameLoadedActions, regtable); --indent; print("end // of ifFrameLoadedExpr\n\n"); break; } case SWFACTION_SETTARGET: { if (targetIndent == 1) { --indent; print("end\n"); targetIndent = 0; } if (strlen((char *) p) > 0) { print("setTarget '%s'\n", p); ++indent; targetIndent = 1; } break; } case SWFACTION_SETTARGETEXPRESSION: if (targetIndent == 1) { --indent; print("end\n"); targetIndent = 0; } print("setTargetExpr\n"); ++indent; targetIndent = 1; break; case SWFACTION_GOTOLABEL: print("gotoLabel '%s'\n", p); break; case SWFACTION_END: break; /* f5 ops */ case SWFACTION_DELETE: print("delete\n"); break; case SWFACTION_DELETE2: print("delete2\n"); break; case SWFACTION_VAR: print("var\n"); break; case SWFACTION_VAREQUALS: print("varEquals\n"); break; case SWFACTION_CALLFUNCTION: print("callFunction\n"); break; case SWFACTION_RETURN: print("return\n"); break; case SWFACTION_MODULO: print("modulo\n"); break; case SWFACTION_NEW: print("new\n"); break; case SWFACTION_TYPEOF: print("typeof\n"); break; case SWFACTION_TARGETPATH: print("targetPath\n"); break; case SWFACTION_NEWADD: print("add\n"); break; case SWFACTION_NEWLESSTHAN: print("lessThan\n"); break; case SWFACTION_NEWEQUALS: print("equals\n"); break; case SWFACTION_TONUMBER: print("toNumber\n"); break; case SWFACTION_TOSTRING: print("toString\n"); break; case SWFACTION_DUP: print("dup\n"); break; case SWFACTION_GETMEMBER: print("getMember\n"); break; case SWFACTION_SETMEMBER: print("setMember\n"); break; case SWFACTION_INCREMENT: print("increment\n"); break; case SWFACTION_DECREMENT: print("decrement\n"); break; case SWFACTION_NEWMETHOD: print("newMethod\n"); break; case SWFACTION_CALLMETHOD: print("callMethod\n"); break; case SWFACTION_BITWISEAND: print("bitwiseAnd\n"); break; case SWFACTION_BITWISEOR: print("bitwiseOr\n"); break; case SWFACTION_BITWISEXOR: print("bitwiseXor\n"); break; case SWFACTION_SHIFTLEFT: print("shiftLeft\n"); break; case SWFACTION_SHIFTRIGHT: print("shiftRight\n"); break; case SWFACTION_SHIFTRIGHT2: print("shiftRight2\n"); break; case SWFACTION_CONSTANTPOOL: { unsigned int i, n = S16(p); int willInclude = 0; unsigned int constlen = 2; if (n > MAX_CONSTANTS) tellUser(0, "Too many constants"); p += 2; conststart = ftell(stdout); print("constants "); nDict = 0; for (i = 0; i < n; ++i) { if (strnIcmp((char *) p, "#include", 8) == 0) willInclude = 1; dictionary[i].value = (char *) p; dictionary[i].count = 0; nDict++; printstr((char *) p); if (i < n - 1) printf("%s", ", "); else printf("%s", " "); constlen += strlen((char *) p) + 1; p += strlen((char *) p) + 1; } if (constlen != len) { tellUser(0, "Declared constant pool length %u differs from calculated length %u", len, constlen); /* try to restore the real constant pool length */ *lenptr = 0xff & constlen; *(lenptr+1) = 0xff & (constlen >> 8); } printf("%s", "\n"); /* put some free space after constant pool to add user constants from included file later */ if (mode >= MODE_UPDATE && willInclude == 1) for (i = 0; i < MAX_INCLUDE_POOL; ++i) putchar(' '); constend = ftell(stdout); break; } case SWFACTION_WITH: { unsigned int withlen = S16(p); print("with\n"); ++indent; printActions(p + 2, (unsigned long int) withlen, ANY_VALUE, regtable); --indent; *lenptr += withlen; print("end\n"); break; } case SWFACTION_DEFINEFUNCTION: { int nargs; unsigned int funclen; char *name = (char *) p; char *argname; p += strlen(name) + 1; nargs = S16(p); p += 2; if (*name != '\0') { if (mode < MODE_UPDATE && goodID(name)) print("function %s (", name); else print("function '%s' (", name); } else print("function ("); if (nargs > 0) { argname = (char *) p; printf("'%s'", argname); p += strlen(argname) + 1; --nargs; } for (; nargs > 0; --nargs) { argname = (char *) p; printf(", '%s'", argname); p += strlen(argname) + 1; } putchar(')'); putchar('\n'); funclen = S16(p); p += 2; ++indent; printActions(p, (unsigned long int) funclen, ANY_VALUE, NULL); --indent; print("end // of function %s\n\n", name); *lenptr += funclen; break; } case SWFACTION_ENUMERATE: print("enumerate\n"); break; case SWFACTION_SETREGISTER: if (literalregisters && regtable != NULL && regtable[*p] != NULL && *regtable[*p] != '\0') { if (goodID(regtable[*p])) print("setRegister r:%s\n", regtable[*p]); else print("setRegister r:'%s'\n", regtable[*p]); } else print("setRegister r:%i\n", *p); break; case SWFACTION_STRICTEQUALS: print("strictEquals\n"); break; case SWFACTION_GREATERTHAN: print("greaterThan\n"); break; case SWFACTION_ENUMERATEVALUE: print("enumerateValue\n"); break; case SWFACTION_INSTANCEOF: print("instanceOf\n"); break; case SWFACTION_STRICTMODE: print("strictMode"); if (*p > 0) puts(" ON"); else puts(" OFF"); break; case SWFACTION_DEFINEFUNCTION2: { unsigned int i, funclen, nargs, autoregFlags; int firstprint = 1; byte nregisters, curautoreg; char *argnames[MAX_REGISTERS]; char *name = (char *) p; memset(argnames, 0, MAX_REGISTERS * sizeof (char *)); p += strlen(name) + 1; nargs = S16(p); p += 2; nregisters = *p++; autoregFlags = S16(p); p += 2; if (*name != '\0') { if (mode < MODE_UPDATE && goodID(name)) print("function2 %s (", name); else print("function2 '%s' (", name); } else print("function2 ("); /* print function arguments, store links to their names in register allocation table */ for (; nargs > 0; nargs--) { byte reg = *p; char *arg = (char *)(p+1); if (reg != 0) { if (argnames[reg] != NULL) { tellUser (0, "Duplicate register allocation in function2 %s: %s and %s both go to r:%u", name, argnames[reg], arg, reg); } argnames[reg] = arg; printf("r:%u='%s'%s", reg, arg, nargs == 1 ? "" : ", "); } else printf("'%s'%s", arg, nargs == 1 ? "" : ", "); p += strlen(arg) + 2; } printf("%s", ") ("); /* allocate registers for "automatic" names based on flags, skip suppressed parameters */ curautoreg = 1; for (i = 0; i < MAX_AUTO_REGS; i++) { if ((autoregFlags & arPreFlags[i]) == arPreFlags[i]) { /* preload flag */ if (argnames[curautoreg] != NULL && strcmp(argnames[curautoreg], arNames[i])) tellUser (0, "Duplicate register allocation in function2 %s: %s and %s both go to r:%u", name, argnames[curautoreg], arNames[i], curautoreg); if (autoregFlags & arSupFlags[i]) tellUser (0, "Preload and suppress flags are both set for %s parameter of function2 %s", arNames[i], name); argnames[curautoreg] = arNames[i]; autoregFlags -= arPreFlags[i]; printf("%sr:%u='%s'", firstprint == 0 ? ", " : "", curautoreg, arNames[i]); firstprint = 0; curautoreg++; } else if ((autoregFlags & arSupFlags[i]) == arSupFlags[i]) { /* suppress flag */ autoregFlags -= arSupFlags[i]; } else { /* the parameter is neither preloaded nor suppressed */ printf("%s'%s'", firstprint == 0 ? ", " : "", arNames[i]); firstprint = 0; } } printf("%s", ")\n"); if (autoregFlags != 0) tellUser(0,"Unknown register flag for function2 %s: %u", name, autoregFlags); funclen = S16(p); p += 2; ++indent; printActions(p, (unsigned long int) funclen, ANY_VALUE, argnames); --indent; print("end // of function %s\n\n", name); *lenptr += funclen; break; } case SWFACTION_THROW: print("throw\n"); break; case SWFACTION_EXTENDS: print("extends\n"); break; case SWFACTION_IMPLEMENTS: print("implements\n"); break; case SWFACTION_CAST: print("cast\n"); break; case SWFACTION_FSCOMMAND2: print("FSCommand2\n"); break; case SWFACTION_TRY: { unsigned int trylen, catchlen, finallylen; /* try type */ byte catchtype = *p++; trylen = S16(p); p += 2; catchlen = S16(p); p += 2; finallylen = S16(p); p += 2; if (catchtype & 4) { /* error in register */ if (literalregisters && regtable != NULL && regtable[*p] != NULL && *regtable[*p] != '\0') print("try r:%s\n", regtable[*p++]); else print("try r:%u\n", *p++); } else { /* error in variable */ print("try"); if (*p != '\0') printf(" '%s'", (char *) p); putchar('\n'); p += len-7; } ++indent; printActions(p, (unsigned long int) trylen, ANY_VALUE, regtable); --indent; if (catchlen > 0) { print("catch\n"); ++indent; printActions(p+trylen, (unsigned long int) catchlen, ANY_VALUE, regtable); --indent; } if (finallylen > 0) { print("finally\n"); ++indent; printActions(p+trylen+catchlen, (unsigned long int) finallylen, ANY_VALUE, regtable); --indent; } *lenptr += trylen+catchlen+finallylen; print("end // of try\n"); break; } default: print("swfAction 0x%02x", type); if (len > 0) { unsigned int i; printf("%s", " hexdata "); for (i = 0; i < len; ++i) { printf("0x%02X", *p); if (i < len - 1) printf("%s", ","); p++; } } printf("%s", " // unknown action\n"); tellUser(0, "Unknown action 0x%02x", type); } return; } static void rebuildConstantPool(void) { long int curpos = ftell(stdout); unsigned long int k; int i; int constOK = 0; fseek(stdout, conststart, SEEK_SET); /* go back to the last constants declaration */ for (i = 0; i < nDict; ++i) { /* remove constants used less than 2 times, and empty strings */ if ((dictionary[i].count > 1) && (strlen(dictionary[i].value) > 0)) { if (constOK == 0) { print("constants "); constOK = 1; } printstr(dictionary[i].value); printf("%s", ", "); } } if (flasmDict != NULL) { if (constOK == 0) { print("constants "); constOK = 1; } if (ftell(stdout) + strlen(flasmDict) >= constend) tellUser(0, "Too many user constants: %s", flasmDict); else printf("%s", flasmDict); /* add user constants */ } else if (constOK == 1) fseek(stdout, -2, SEEK_CUR); /* remove last ", " */ putchar('\n'); for (k = ftell(stdout); k < constend - 1; ++k) /* fill the rest of former constants with spaces */ putchar(' '); putchar('\n'); if (flasmDict != NULL) { free(flasmDict); flasmDict = NULL; } fseek(stdout, curpos, SEEK_SET); } static void readActionBuffer(FILE *f, unsigned long int length) { if (length == 0) return; buffer = malloc(length); if (buffer == NULL) tellUser(1, "Not enough memory to process action block"); if (fread(buffer, 1, length, f) != length) tellUser(1, "Attempt to read beyond EOF"); } static void createLabels(unsigned long int length) { unsigned long int i; unsigned int blocklen; unsigned int numpools = 0; numLabels = 0; curLabel = 0; for (i = 0; i < length; ++i) { if (buffer[i] & 0x80) { blocklen = S16(buffer + i + 1); if ((Action) buffer[i] == SWFACTION_BRANCHALWAYS || (Action) buffer[i] == SWFACTION_BRANCHIFTRUE) { if ((signed long int) (i + 3 + blocklen + S16signed(buffer + i + 3)) >= 0) { addLabel(i + 3 + blocklen + S16signed(buffer + i + 3)); } } else if ((Action) buffer[i] == SWFACTION_CONSTANTPOOL) { byte *p = buffer + i + 3; unsigned int n, nStrings = S16(p); numpools++; p += 2; for (n = 0; n < nStrings; ++n) p += strlen((char *) p) + 1; if (blocklen != p - buffer - i - 3) { /* fix broken length */ blocklen = p - buffer - i - 3; } } i += blocklen + 2; } } if ((numpools > 1) && (literalconstants < 2)) showLiterals = 0; } static void printActionBlock(FILE *f, unsigned long int length, abtype abtype, unsigned int flags, byte key) { swfabspos = ftell(f); swfrelpos = 0; conststart = constend = 0; nDict = 0; showLiterals = literalconstants; ++indent; readActionBuffer(f, length); createLabels(length); qsort(labels, (size_t) numLabels, sizeof (unsigned long int), longintCompare); printActions(buffer, length, ANY_VALUE, NULL); if ((mode >= MODE_UPDATE) && (constend > 0) && showLiterals) rebuildConstantPool(); if (targetIndent == 1) { --indent; print("end\n"); targetIndent = 0; } --indent; if (buffer != NULL) { free(buffer); buffer = NULL; } if (labels != NULL) { free(labels); labels = NULL; } swfabspos = -1; swfrelpos = -1; } static void skipMatrix(FILE *f) { InitBits(f); if (getBits(f, 1)) getBits(f, getBits(f, 5) * 2); if (getBits(f, 1)) getBits(f, getBits(f, 5) * 2); getBits(f, getBits(f, 5) * 2); } static void skipColorTransform(FILE *f) { unsigned int needAdd, needMul, nBits; InitBits(f); needAdd = getBits(f, 1); needMul = getBits(f, 1); nBits = getBits(f, 4); if (needMul) getBits(f, nBits * 4); if (needAdd) getBits(f, nBits * 4); } static void skipFilters(FILE *f, byte numfilters) { while(numfilters--) { int filter = fgetc(f); switch(filter){ case FILTER_DROPSHADOW: skipProtected(f, 23); break; case FILTER_BLUR: skipProtected(f, 9); break; case FILTER_GLOW: skipProtected(f, 15); break; case FILTER_BEVEL: skipProtected(f, 27); break; case FILTER_GRADIENTGLOW: skipProtected(f, fgetc(f)*5 + 19); break; case FILTER_ADJUSTCOLOR: skipProtected(f, 80); break; case FILTER_GRADIENTBEVEL: skipProtected(f, fgetc(f)*5 + 19); break; default: tellUser(1, "Unknown filter %i", filter); } } } static void parseKeyPressEvent(byte onKey) { printf("keyPress "); if (onKey == keySpace) printf("_SPACE"); else if (onKey == '\'') printf("'%s'", "\\'"); else if (onKey == '\\') printf("'%s'", "\\\\"); else if (onKey > 32) printf("'%c'", onKey); else switch (onKey) { case keyLeft: printf("_LEFT"); break; case keyRight: printf("_RIGHT"); break; case keyHome: printf("_HOME"); break; case keyEnd: printf("_END"); break; case keyInsert: printf("_INSERT"); break; case keyDelete: printf("_DELETE"); break; case keyBackspace: printf("_BACKSPACE"); break; case keyEnter: printf("_ENTER"); break; case keyUp: printf("_UP"); break; case keyDown: printf("_DOWN"); break; case keyPageUp: printf("_PAGEUP"); break; case keyPageDown: printf("_PAGEDOWN"); break; case keyTab: printf("_TAB"); break; case keyEscape: printf("_ESCAPE"); break; default: printf("0x%02x // unknown key", onKey); tellUser(0, "Unknown key 0x%02x in keyPress event", onKey); } } static void parseButtonEvent(FILE *f, unsigned int event, unsigned long int length) { char delim = ' '; byte key = 0; putchar('\n'); ++indent; print("on"); if (event & IdleToOverUp) { putchar(delim); printf("idleToOverUp"); delim = ','; } if (event & OverUpToIdle) { putchar(delim); printf("overUpToIdle"); delim = ','; } if (event & OverUpToOverDown) { putchar(delim); printf("overUpToOverDown"); delim = ','; } if (event & OverDownToOverUp) { putchar(delim); printf("overDownToOverUp"); delim = ','; } if (event & OverDownToOutDown) { putchar(delim); printf("overDownToOutDown"); delim = ','; } if (event & OutDownToOverDown) { putchar(delim); printf("outDownToOverDown"); delim = ','; } if (event & OutDownToIdle) { putchar(delim); printf("outDownToIdle"); delim = ','; } if (event & IdleToOverDown) { putchar(delim); printf("idleToOverDown"); delim = ','; } if (event & OverDownToIdle) { putchar(delim); printf("overDownToIdle"); delim = ','; } /* keyPress */ if (event > 0x1FF) { key = (byte) (event >> 9); putchar(delim); parseKeyPressEvent(key); } printf("\n"); printActionBlock(f, length, AB_BUTTONEVENT, event, key); print("end\n"); --indent; } static void parseButton2(FILE *f, unsigned long int length) { unsigned int condition, buttonID, actionOffset; unsigned long int lastABLength; buttonID = getWord(f); /* trackAsMenu */ fgetc(f); actionOffset = getWord(f); lastABLength = length - 8 - actionOffset; if (actionOffset > 0) { printf("\n"); print("defineButton %u\n", buttonID); /* skip button data */ while (actionOffset > 2) { fgetc(f); --actionOffset; } while ((actionOffset = getWord(f)) > 0) { lastABLength -= actionOffset; condition = getWord(f); parseButtonEvent(f, condition, actionOffset - 4); } /* last event */ condition = getWord(f); parseButtonEvent(f, condition, lastABLength); print("end // of defineButton %u\n", buttonID); } else { /* no button events */ while (lastABLength + 2 > 0) { fgetc(f); --lastABLength; } } /* button end */ fgetc(f); } static int printBitMasked(unsigned long int *eventptr, unsigned long int e, char *delimptr, char *str) { if (*eventptr & e) { *eventptr -= e; printf("%c%s", *delimptr, str); *delimptr = ','; return 1; } else return 0; } static void parseEvent(FILE *f, unsigned long int event, unsigned long int length) { byte key = 0; char delim = ' '; unsigned long int event2 = event; putchar('\n'); ++indent; print("onClipEvent"); printBitMasked(&event2, eventLoad, &delim, "load"); printBitMasked(&event2, eventEnterFrame, &delim, "enterFrame"); printBitMasked(&event2, eventUnload, &delim, "unload"); printBitMasked(&event2, eventMouseMove, &delim, "mouseMove"); printBitMasked(&event2, eventMouseDown, &delim, "mouseDown"); printBitMasked(&event2, eventMouseUp, &delim, "mouseUp"); printBitMasked(&event2, eventKeyDown, &delim, "keyDown"); printBitMasked(&event2, eventKeyUp, &delim, "keyUp"); printBitMasked(&event2, eventData, &delim, "data"); printBitMasked(&event2, eventInitialize, &delim, "initialize"); printBitMasked(&event2, eventConstruct, &delim, "construct"); printBitMasked(&event2, eventPress, &delim, "press"); printBitMasked(&event2, eventRelease, &delim, "release"); printBitMasked(&event2, eventReleaseOutside, &delim, "releaseOutside"); printBitMasked(&event2, eventRollOver, &delim, "rollOver"); printBitMasked(&event2, eventRollOut, &delim, "rollOut"); printBitMasked(&event2, eventDragOver, &delim, "dragOver"); printBitMasked(&event2, eventDragOut, &delim, "dragOut"); if (printBitMasked(&event2, eventKeyPress, &delim, "")) { key = (byte) fgetc(f); parseKeyPressEvent(key); length--; delim = ','; } if (event2 != 0) { printf("%c%lu // unknown event", delim, event2); tellUser(0, "Unknown event: %lu", event2); } printf("\n"); printActionBlock(f, length, AB_MCEVENT, event, key); print("end\n"); --indent; } static void parsePlaceObject(FILE *f, unsigned long int length, unsigned int type) { int i; unsigned int flags; unsigned int clipID = 0; unsigned int depth; unsigned long int curEvent; if (type == TAG_PLACEOBJECT2) flags = fgetc(f); else flags = getWord(f); if (flags & PF_ONCLIPEVENTS) { printf("\n"); /* character depth */ depth = getWord(f); /* clipID should always be present */ if (flags & PF_CHARACTER) { print("placeMovieClip %u ", clipID = getWord(f)); } else { print("placeMovieClip ??? "); tellUser(0, "placeMovieClip: clip ID not found"); } if (flags & PF_MATRIX) skipMatrix(f); if (flags & PF_COLORTRANSFORM) skipColorTransform(f); if (flags & PF_RATIO) getWord(f); if (flags & PF_NAME) { printf("as "); putchar('\''); while ((i = fgetc(f)) != 0) putchar((char) i); putchar('\''); } if (flags & PF_DEFINECLIP) getWord(f); if (type == TAG_PLACEOBJECT3) { if (flags & PF_FILTERS) skipFilters(f, (byte) fgetc(f)); if (flags & PF_BLENDMODE) fgetc(f); if (flags & PF_BITMAPCACHING) fgetc(f); } printf("\n"); /* reserved: always 0 */ getWord(f); if (swfVersion >= 6 || type == TAG_PLACEOBJECT3) { /* flash 6 supports button events for mcs, therefore going long here */ getDoubleWord(f); while ((curEvent = getDoubleWord(f)) != 0) parseEvent(f, curEvent, getDoubleWord(f)); } else { /* all events */ getWord(f); while ((curEvent = (unsigned long int) getWord(f)) != 0) parseEvent(f, curEvent, getDoubleWord(f)); } if (flags & PF_CHARACTER) print("end // of placeMovieClip %u\n", clipID); else print("end // of placeMovieClip ???\n"); } else { /* no events found, skip the rest of placeObject2/3 */ if (type == TAG_PLACEOBJECT3) skipProtected(f, length - 2); else skipProtected(f, length - 1); } } static void parseMovieClip(FILE *f) { unsigned int clipID, frameNum = 0, framesTotal = 0; clipID = getWord(f); framesTotal = getWord(f); printf("\n"); print("defineMovieClip %u // total frames: %u\n", clipID, framesTotal); indent++; while (!feof(f)) { unsigned int type; unsigned long int length; parseTagHeader(f, &type, &length); if (type == 0) break; switch (type) { case TAG_DOACTION: printFrameNum(frameNum); printActionBlock(f, length, AB_FRAME, frameNum, 0); print("end // of frame %u\n", frameNum); break; case TAG_PLACEOBJECT2: /* possibly onClipEvents inside */ parsePlaceObject(f, length, TAG_PLACEOBJECT2); break; case TAG_PLACEOBJECT3: /* possibly onClipEvents inside */ parsePlaceObject(f, length, TAG_PLACEOBJECT3); break; case TAG_SHOWFRAME: ++frameNum; break; default: if (getTagString(type) == NULL) { print("\n"); print("// unknown tag %lu length %li\n\n", type, length); } skipProtected(f, length); } } --indent; print("end // of defineMovieClip %u\n", clipID); } void disassembleSWF(FILE *f, char *fname) { unsigned int componentID, frameNum = 0, framesTotal = 0, bits; unsigned long int i, size; float frameRate, movieWidth, movieHeight; swfVersion = fgetc(f); size = getDoubleWord(f); /* movie bounds */ InitBits(f); bits = getBits(f, 5); /* xMin - always 0 */ getBits(f, bits); /* xMax */ movieWidth = ((float) getBits(f, bits)) / 20; /* yMin - always 0 */ getBits(f, bits); /* yMax */ movieHeight = ((float) getBits(f, bits)) / 20; frameRate = ((float) fgetc(f)) / 256; frameRate += (float) fgetc(f); framesTotal = getWord(f); printf("movie '%s'", fname); if (wasCompressed) printf(" compressed"); printf(" // flash %i, total frames: %u, frame rate: ", swfVersion, framesTotal); printFloat(frameRate, 1); printf(" fps, "); printFloat(movieWidth, 1); putchar('x'); printFloat(movieHeight, 1); printf(" px\n"); while (!feof(f)) { unsigned int type; unsigned long int length; parseTagHeader(f, &type, &length); if (type == 0) break; switch (type) { case TAG_DOACTION: printFrameNum(frameNum); printActionBlock(f, length, AB_FRAME, frameNum, 0); print("end // of frame %u\n", frameNum); break; case TAG_INITMOVIECLIP: print("\n"); componentID = getWord(f); print("initMovieClip %i\n", componentID); printActionBlock(f, length - 2, AB_INITMC, componentID, 0); print("end // of initMovieClip %i\n", componentID); break; case TAG_PLACEOBJECT2: /* possibly onClipEvents inside */ parsePlaceObject(f, length, TAG_PLACEOBJECT2); break; case TAG_PLACEOBJECT3: /* possibly onClipEvents inside */ parsePlaceObject(f, length, TAG_PLACEOBJECT3); break; case TAG_DEFINEBUTTON2: /* possibly button events inside */ parseButton2(f, length); break; case TAG_SHOWFRAME: ++frameNum; break; case TAG_SCRIPTLIMITS: { unsigned int recursion = getWord(f); unsigned int timeout = getWord(f); print("\n"); print("scriptLimits recursion %u timeout %u\n", recursion, timeout); break; } case TAG_PROTECT: print("\n"); print("protect"); if (length > 0) { /* password found */ printf(" '"); /* always 0 */ getWord(f); for (i = 2; i < length - 1; ++i) { putchar((char) fgetc(f)); } /* always 0 - string end */ fgetc(f); putchar('\''); } putchar('\n'); break; case TAG_ENABLEDEBUGGER: print("\n"); print("enableDebugger"); if (length > 0) { /* password found */ /* debugger always uses password, even for empty one */ printf(" '"); /* always 0 */ getWord(f); for (i = 2; i < length - 1; ++i) { putchar((char) fgetc(f)); } /* always 0 - string end */ fgetc(f); putchar('\''); } putchar('\n'); break; case TAG_ENABLEDEBUGGER2: /* flash MX debugger */ print("\n"); print("enableDebugger2"); if (length > 0) { /* password found */ /* debugger always uses password, even for empty one */ printf(" '"); /* reserved, always 0 */ getWord(f); for (i = 2; i < length - 1; ++i) { putchar((char) fgetc(f)); } /* always 0 - string end */ fgetc(f); putchar('\''); } putchar('\n'); break; case TAG_DEFINEMOVIECLIP: parseMovieClip(f); break; case TAG_EXPORTASSETS: { unsigned int assetID, numAssets = getWord(f); int n; print("\n"); print("exportAssets\n"); ++indent; while (numAssets--) { print("%u as '", assetID = getWord(f)); while ((n = fgetc(f)) != 0) { putchar((char) n); } printf("'\n"); } --indent; print("end // of exportAssets\n"); break; } case TAG_IMPORTASSETS: case TAG_IMPORTASSETS2: { unsigned int assetID, numAssets, attr; int n; print("\n"); print("importAssets from '"); while ((n = fgetc(f)) != 0) { putchar((char) n); } printf("'\n"); if (type == TAG_IMPORTASSETS2) { /* Reserved: always 1 */ attr = getWord(f); if (attr != 1) tellUser(0, "Unknown importAssets2 attribute: %u (should be 1)", attr); } numAssets = getWord(f); ++indent; while (numAssets--) { assetID = getWord(f); print("'"); while ((n = fgetc(f)) != 0) { putchar((char) n); } printf("' as %u\n", assetID); } --indent; print("end // of importAssets\n"); break; } case TAG_METADATA: { static char buf[MAX_BUFFER]; print("\n"); print("metadata "); fread(buf, 1, length, f); printstr(buf); putchar('\n'); break; } case TAG_FILEATTRIBUTES: { char delim = ' '; unsigned long int attrs = getDoubleWord(f); if (attrs) { print("\n"); print("fileAttributes"); } printBitMasked(&attrs, ATTR_USENETWORK, &delim, "attrUseNetwork"); printBitMasked(&attrs, ATTR_RELATIVEURLS, &delim, "attrRelativeURLs"); printBitMasked(&attrs, ATTR_SUPPRESSCROSSDOMAINCACHE, &delim, "attrSuppressCrossDomainCache"); printBitMasked(&attrs, ATTR_ACTIONSCRIPT3, &delim, "attrActionScript3"); printBitMasked(&attrs, ATTR_HASMETADATA, &delim, "attrHasMetadata"); if (attrs != 0) { printf("%c%lu // unknown attribute", delim, attrs); tellUser(0, "Unknown file attribute: %lu", attrs); } printf("\n"); break; } default: if (getTagString(type) == NULL) { print("\n"); print("// unknown tag %lu length %li\n", type, length); } skipProtected(f, length); } } fclose(f); printf("end\n"); }