the cutting room mirror

1997-1998 Era Files

This article has a talk page!
view on tcrf.net

This is a sub-page of Development:Duke Nukem Forever/1999 Asset CD.

To do:
Some quick notes

  • Any photos from 1998 should be on the Reference Photos sub-page and not here.
  • Some files "may" have ended up the 2001 prototypes and might not be worth documenting here but don't consider this gospel as I haven't check yet.

Models#

One of the main contents of interest from the CD was 5 early models from the Quake II iteration of the game. The five models are stored .ucx models in the models folder, however .mdx versions of the Bombshell and Alien Ship models are present in their own folders. Converted versions of the Duke, Gus and Alien ship models were included separately from the disc by the leaker.

Bombshell#

Duke Nukem#

Gus#

DNF-AssetCD-Highgus.png

The model is missing his hat and pickaxe seen in prerelease media. The model also only has a running animation, even though various other animations were shown in prerelease media.

Swat Cop#

DNF-AssetCD-Swatcop.png

A previously unknown model by the name of "swatcop". No prerelease media featuring this model was ever released. The Swatcop depicts a guy wearing a balaclava, a green camo suit, a protective vest with "POLICE" on the front and back, a satchel strapped to his left leg and some boots.

The model features a simple animation of it walking.

While unconfirmed, its likely that this a very early version of what become the EDF soldiers in later builds, considering that the EDF possess a SWAT-like design in the 2001 prototypes and final game.

Alien Ship#

DNF-AssetCD-Alienship1.png

The model processes no animations.

The model can be seen many times in the 1998 E3 trailer.

The .mdx version refers to the model as Dalienship3.

Textures/Graphics#

The following three sections are found in the bright/outgoing/DP folder.

Console Backgrounds#

Various background graphics for the console, from idTech 2 iterations of the game. Both conback and conback2 appear to feature a very early mock-up of a logo. The more interesting of these is conold.pcx, which appears to have been based on a mock-up for box-art.

Unreal Texture (?)#

To do:
Can any Epic MegaFans of Unreal confirm if this is from that game? Thanks! - Mantis.

An image simply called unreal.pcx.

Dawn#

An image called dawn.pcx of the eponymous comic book character, what purpose the image was meant to serve is unclear.

GONG6#

An image called GONG6.LBM depicting a dragon presumably taken from a gong dated to June 1997. Purpose unknown.

Miscellaneous#

Early Map#

Download kslab3.unr (Restored)

File: DNF-AssetCD-kslab3Restored.zip (149 KB) (info)

To do:

  • Add a gameplay video showing off the map, alongside some screenshots.
  • Also add some images of this map in Unreal, since it can probably load there.

An early map can be found in the bright folder called kslab3.unr. The map is dated to October 21st, 1998, making it one of the earliest maps made after the switch to Unreal Engine in June 1998. The map can't be loaded into the 2001 prototypes due to them using a custom map extension (.dnf) rather than the default Unreal one (.unr).

A fixed and converted version of the map by Mighty Foot Productions can be downloaded above.

UnrealEd Source Code#

Found in bright folder is UnEdFact.cpp, source code for the Unreal Engine's editor. The file is dated June 12th, 1998, which was only three days before 3D Realms announced the switch to Unreal Engine. <pre style='white-space: pre-wrap; overflow: auto; width: 90%; height: 300px> /*=============================================================================

   UnEdFact.cpp: Editor class factories.
   Copyright 1997 Epic MegaGames, Inc. This software is a trade secret.

=============================================================================*/

  1. include "EditorPrivate.h"

/*------------------------------------------------------------------------------

   UClassFactory implementation.

------------------------------------------------------------------------------*/

UClassFactory::UClassFactory() {

   guard(UClassFactory::UClassFactory);

   // Init UFactory properties.
   SupportedClass = UClass::StaticClass;
   new(Formats)FString("uc;Unreal class definitions");
   bCreateNew = 0;

   unguard;

} UObject* UClassFactory::Create (

   UClass*             Class,
   UObject*            InParent,
   FName               Name,
   UObject*            Context,
   const char*         Type,
   const char*&        Buffer,
   const char*         BufferEnd,
   FFeedbackContext*   Warn

) {

   guard(UClassFactory::Create);
   const char* InBuffer=Buffer;
   char StrLine[256], ClassName[32]="", BaseClassName[32]="";

   // Validate format.
   if( Class != UClass::StaticClass )
   {
       Warn->Warnf( "Can only import classes", Type );
       return NULL;
   }
   if( appStricmp(Type,"UC")!=0 )
   {
       Warn->Warnf( "Can't import classes from files of type '%s'", Type );
       return NULL;
   }

   // Import the script text.
   UTextBuffer* ScriptText = new UTextBuffer;
   UTextBuffer* DefaultPropText = new UTextBuffer;
   while( ParseLine(&Buffer,StrLine,ARRAY_COUNT(StrLine),1) )
   {
       const char* Str = &StrLine[0], *Temp;
       if( ParseCommand(&Str,"defaultproperties") )
       {
           // Get default properties text.
           while( ParseLine(&Buffer,StrLine,256,1) )
           {
               Str = &StrLine[0];
               ParseNext( &Str );
               if( *Str=='}' )
                   break;
               DefaultPropText->Logf( "%s\r\n", StrLine );
           }
       }
       else
       {
           // Get script text.
           ScriptText->Logf( "%s\r\n", StrLine );

           // Stub out the comments.
           if( appStrstr(StrLine, "//") )
               *appStrstr(StrLine, "//") = 0;

           // Get class name.
           if( !ClassName[0] && (Temp=appStrfind(Str, "class"))!=0 )
           {
               Temp+=6;
               ParseToken( Temp, ClassName, ARRAY_COUNT(ClassName), 0 );
           }
           if( !BaseClassName[0] && (Temp=appStrfind(Str, "expands"))!=0 )
           {
               Temp+=7;
               ParseToken( Temp, BaseClassName, ARRAY_COUNT(BaseClassName), 0 );
               int c = appStrlen(BaseClassName);
               while( c>0 && BaseClassName[c-1]==';' )
                   c--;
               BaseClassName[c] = 0;
           }
       }
   }

   // Handle failure.
   if( !ClassName[0] || (!BaseClassName[0] && !appStricmp(ClassName,"Object")) )
   {
       Warn->Warnf( "Bad class definition '%s'/'%s'/%i/%i", ClassName, BaseClassName, BufferEnd-InBuffer, appStrlen(InBuffer) );
       if( ScriptText )
           delete ScriptText;
       return NULL;
   }
   else if( appStricmp(ClassName,*Name)!=0 )
   {
       Warn->Warnf( "Script vs. class name mismatch (%s/%s)", *Name, ClassName );      
   }

   UClass* ResultClass = FindObject( InParent, ClassName );
   if( ResultClass && (ResultClass->GetFlags() & RF_Intrinsic) )
   {
       // Gracefully update an existing hardcoded class.
       debugf( NAME_Log, "Updated intrinsic class '%s'", ResultClass->GetFullName() );
       ResultClass->ClassFlags &= ~(CLASS_Parsed | CLASS_Compiled);
   }
   else
   {
       // Create new class.
       ResultClass = new( InParent, ClassName, RF_Public | RF_Standalone )UClass( NULL );

       // Find or forward-declare base class.
       ResultClass->SuperField = FindObject( InParent, BaseClassName );
       if( !ResultClass->SuperField )
           ResultClass->SuperField = FindObject( ANY_PACKAGE, BaseClassName );
       if( !ResultClass->SuperField )
           ResultClass->SuperField = new( InParent, BaseClassName )UClass( ResultClass );
       debugf( NAME_Log, "Imported: %s", ResultClass->GetFullName() );
   }

   // Set this class's properties.
   ResultClass->DefaultPropText  = DefaultPropText;
   ResultClass->ScriptText       = new( ResultClass->GetParent(), NAME_None, RF_NotForClient | RF_NotForServer )UTextBuffer;
   ResultClass->ScriptText->Text = ScriptText->Text;
   delete ScriptText;

   return ResultClass;
   unguard;

} IMPLEMENT_CLASS(UClassFactory);

/*------------------------------------------------------------------------------

   ULevelFactory.

------------------------------------------------------------------------------*/

static void ForceValid( ULevel* Level, UStruct* Struct, BYTE* Data ) {

   guard(ForceValid);
   for( TFieldIterator It(Struct); It; ++It )
   {
       for( INT i=0; iArrayDim; i++ )
       {
           BYTE* Value = Data + It->Offset + i*It->GetElementSize();
           if( It->IsA(UObjectProperty::StaticClass) )
           {
               UObject*& Obj = *(UObject**)Value;
               if( Obj && Obj->IsA(AActor::StaticClass) )
               {
                   for( INT j=0; jNum(); j++ )
                       if( Level->Actors(j)==Obj )
                           break;
                   if( j==Level->Num() )
                   {
                       debugf( NAME_Log, "Usurped %s", Obj->GetClassName() );
                       Obj = NULL;
                   }
               }
           }
           else if( It->IsA(UStructProperty::StaticClass) )
           {
               ForceValid( Level, ((UStructProperty*)*It)->Struct, Value );
           }
       }
   }
   unguard;

}

ULevelFactory::ULevelFactory() {

   guard(ULevelFactory::ULevelFactory);

   // Init UFactory properties.
   SupportedClass = ULevel::StaticClass;
   new(Formats)FString("t3d;Unreal level text");
   bCreateNew = 0;

   unguard;

} UObject* ULevelFactory::Create (

   UClass*             Class,
   UObject*            InParent,
   FName               Name,
   UObject*            Context,
   const char*         Type,
   const char*&        Buffer,
   const char*         BufferEnd,
   FFeedbackContext*   Warn

) {

   guard(ULevelFactory::Create);
   TMap<AActor*,FString> Map;

   // Create (or replace) the level object.
   ULevel* Level = GEditor->Level;
   Level->CompactActors();
   check(Level->Num()>1);
   check(Cast(Level->Actors(0)));
   check(Cast(Level->Actors(1)));

   // Init actors.
   for( INT i=0; iNum(); i++ )
   {
       if( Level->Actors(i) )
       {
           Level->Actors(i)->bTempEditor = 1;
           Level->Actors(i)->bSelected   = 0;
       }
   }

   // Assumes data is being imported over top of a new, valid map.
   ParseNext( &Buffer );
   if( !GetBEGIN( &Buffer, "MAP") )
       return Level;

   // Import everything.
   int ImportedActive=appStricmp(Type,"paste")==0;
   char StrLine[256];
   while( ParseLine( &Buffer, StrLine, 256 ) )
   {
       const char* Str = StrLine;
       if( GetEND(&Str,"MAP") )
       {
           // End of brush polys.
           break;
       }
       else if( GetBEGIN(&Str,"BRUSH") )
       {
           GSystem->StatusUpdatef( 0, 0, "%s", "Importing Brushes" );
           char BrushName[NAME_SIZE];
           if( Parse(Str,"NAME=",BrushName,NAME_SIZE) )
           {
               ABrush* Actor;
               if( !ImportedActive )
               {
                   // Parse the active brush, which has already been allocated.
                   Actor          = Level->Brush();
                   ImportedActive = 1;
               }
               else
               {
                   // Parse a new brush which has not yet been allocated.
                   Actor             = Level->SpawnBrush();
                   Actor->bSelected  = 1;
                   Actor->Brush      = new( InParent, NAME_None, RF_NotForClient|RF_NotForServer )UModel( NULL );          
               }

               // Import.
               Actor->SetFlags       ( RF_NotForClient | RF_NotForServer );
               Actor->Brush->SetFlags( RF_NotForClient | RF_NotForServer );
               TObjectIterator It;
               check(*It);
               Actor->Brush = (UModel*)It->Create(UModel::StaticClass,InParent,Actor->Brush->GetFName(),Actor,Type,Buffer,BufferEnd);
               check(Actor->Brush);
               if( (Actor->PolyFlags&PF_Portal) && !(Actor->PolyFlags&PF_Translucent) )
                   Actor->PolyFlags |= PF_Invisible;
           }
       }
       else if( GetBEGIN(&Str,"ACTOR") )
       {
           UClass* TempClass;
           if( ParseObject( Str, "CLASS=", TempClass, ANY_PACKAGE ) )
           {
               // Get actor name.
               FName ActorName(NAME_None);
               Parse( Str, "NAME=", ActorName );

               // Make sure this name is unique.
               AActor* Found=NULL;
               if( ActorName!=NAME_None )
                   Found = FindObject( InParent, *ActorName );
               if( Found )
                   Found->Rename();

               // Import it.
               AActor* Actor = Level->SpawnActor( TempClass, ActorName, NULL, NULL, FVector(0,0,0), FRotator(0,0,0), NULL, 0, 1 );

               // Get property text.
               FString PropText;
               char StrLine[256];
               while
               (   GetEND( &Buffer, "ACTOR" )==0
               &&  ParseLine( &Buffer,  StrLine, 256 ) )
                   PropText.Appendf( "%s\r\n", StrLine );
               Map.Add( Actor, PropText );

               // Handle class.
               if( Actor->IsA(ALevelInfo::StaticClass) )
               {
                   // Copy the one LevelInfo the position #0.
                   check(Level->Num()>0);
                   INT iActor=0; Level->FindItem( Actor, iActor );
                   Level->Actors(0)       = Actor;
                   Level->Actors(iActor)  = NULL;
               }
               else if( Actor->GetClass()==ABrush::StaticClass && !ImportedActive )
               {
                   // Copy the active brush the position #0.
                   INT iActor=0; Level->FindItem( Actor, iActor );
                   Level->Actors(1)       = Actor;
                   Level->Actors(iActor)  = NULL;
                   ImportedActive = 1;
               }
           }
       }
   }

   // Import actor properties.
   // We do this after creating all actors so that actor references can be matched up.
   guard(Finishing);
   check(Cast(Level->Actors(0)));
   for( int i=0; iNum(); i++ )
   {
       AActor* Actor = Level->Actors(i);
       if( Actor )
       {
           Actor->bSelected = !Actor->bTempEditor;
           FString PropText;
           if( Map.Find(Actor,PropText) )
           {
               guard(ImportActorProperties);
               ImportProperties( Actor->GetClass(), (BYTE*)Actor, Level, *PropText, InParent );
               unguard;
           }
           guard(FixupActor);
           Actor->Level  = (ALevelInfo*)Level->Actors(0);
           Actor->Region = FPointRegion( (ALevelInfo*)Level->Actors(0) );
           ForceValid( Level, Actor->GetClass(), (BYTE*)Actor );
           unguard;
       }
   }
   unguard;

   return Level;
   unguard;

} IMPLEMENT_CLASS(ULevelFactory);

/*-----------------------------------------------------------------------------

   UPolysFactory.

-----------------------------------------------------------------------------*/

UPolysFactory::UPolysFactory() {

   guard(UPolysFactory::UPolysFactory);

   // Init UFactory properties.
   SupportedClass = UPolys::StaticClass;
   new(Formats)FString("t3d;Unreal brush text");
   bCreateNew = 0;

   unguard;

} UObject* UPolysFactory::Create (

   UClass*             Class,
   UObject*            InParent,
   FName               Name,
   UObject*            Context,
   const char*         Type,
   const char*&        Buffer,
   const char*         BufferEnd,
   FFeedbackContext*   Warn

) {

   guard(UPolysFactory::Create);

   // Create polys.
   UPolys* Polys = Context ? CastChecked(Context) : new(InParent,Name)UPolys;

   // Eat up if present.
   GetBEGIN( &Buffer, "POLYLIST" );

   // Parse all stuff.
   int First=1, GotBase=0;
   char StrLine[256], ExtraLine[256];
   FPoly Poly;
   while( ParseLine( &Buffer, StrLine, 256 ) )
   {
       const char* Str = StrLine;
       if( GetEND(&Str,"POLYLIST") )
       {
           // End of brush polys.
           break;
       }
       else if( appStrstr(Str,"ENTITIES") && First )
       {
           // Autocad .DXF file.
           debugf(NAME_Log,"Reading Autocad DXF file");
           INT Started=0, NumPts=0, IsFace=0;
           FVector PointPool[4096];
           FPoly NewPoly; NewPoly.Init();

           while
           (   ParseLine( &Buffer, StrLine, 256, 1 )
           &&  ParseLine( &Buffer, ExtraLine, 256, 1 ) )
           {
               // Handle the line.
               Str = ExtraLine;
               INT Code = appAtoi(StrLine);
               //debugf("DXF: %i: %s",Code,ExtraLine);
               if( Code==0 )
               {
                   // Finish up current poly.
                   if( Started )
                   {
                       if( NewPoly.NumVertices == 0 )
                       {
                           // Got a vertex definition.
                           NumPts++;
                           //debugf("DXF: Added vertex %i",NewPoly.NumVertices);
                       }
                       else if( NewPoly.NumVertices>=3 && NewPoly.NumVertices<FPoly::MAX_VERTICES )
                       {
                           // Got a poly definition.
                           if( IsFace ) NewPoly.Reverse();
                           NewPoly.Base = NewPoly.Vertex[0];
                           NewPoly.Finalize(0);
                           Polys->AddItem(NewPoly);
                           //debugf("DXF: Added poly %i",Num);
                       }
                       else
                       {
                           // Bad.
                           Warn->Warnf( "DXF: Bad vertex count %i", NewPoly.NumVertices );
                       }

                       // Prepare for next.
                       NewPoly.Init();
                   }
                   Started=0;

                   if( ParseCommand(&Str,"VERTEX") )
                   {
                       // Start of new vertex.
                       //debugf("DXF: Vertex");
                       PointPool[NumPts] = FVector(0,0,0);
                       Started = 1;
                       IsFace  = 0;
                   }
                   else if( ParseCommand(&Str,"3DFACE") )
                   {
                       // Start of 3d face definition.
                       //debugf("DXF: 3DFace");
                       Started = 1;
                       IsFace  = 1;
                   }
                   else if( ParseCommand(&Str,"SEQEND") )
                   {
                       // End of sequence.
                       //debugf("DXF: SEQEND");
                       NumPts=0;
                   }
                   else if( ParseCommand(&Str,"EOF") )
                   {
                       // End of file.
                       //debugf("DXF: End");
                       break;
                   }
               }
               else if( Started )
               {
                   // Replace commas with periods to handle european dxf's.
                   for( char* Stupid = appStrchr(ExtraLine,','); Stupid; Stupid=appStrchr(Stupid,',') )
                       *Stupid = '.';

                   // Handle codes.
                   if( Code>=10 && Code<=19 )
                   {
                       // X coordinate.
                       if( IsFace && Code-10==NewPoly.NumVertices )
                       {
                           //debugf("DXF: NewVertex %i",NewPoly.NumVertices);
                           NewPoly.Vertex[NewPoly.NumVertices++] = FVector(0,0,0);
                       }
                       NewPoly.Vertex[Code-10].X = PointPool[NumPts].X = appAtof(ExtraLine);
                   }
                   else if( Code>=20 && Code<=29 )
                   {
                       // Y coordinate.
                       NewPoly.Vertex[Code-20].Y = PointPool[NumPts].Y = appAtof(ExtraLine);
                   }
                   else if( Code>=30 && Code<=39 )
                   {
                       // Z coordinate.
                       NewPoly.Vertex[Code-30].Z = PointPool[NumPts].Z = appAtof(ExtraLine);
                   }
                   else if( Code>=71 && Code<=79 && (Code-71)==NewPoly.NumVertices )
                   {
                       INT iPoint = Abs(appAtoi(ExtraLine));
                       if( iPoint>0 && iPoint<=NumPts )
                           NewPoly.Vertex[NewPoly.NumVertices++] = PointPool[iPoint-1];
                       else debugf( NAME_Warning, "DXF: Invalid point index %i/%i", iPoint, NumPts );
                   }
               }
           }
       }
       else if( appStrstr(Str,"Tri-mesh,") && First )
       {
           // 3DS .ASC file.
           debugf( NAME_Log, "Reading 3D Studio ASC file" );
           FVector PointPool[4096];

           AscReloop:
           int NumVerts = 0, TempNumPolys=0, TempVerts=0;
           while( ParseLine( &Buffer, StrLine, 256 ) )
           {
               Str=StrLine;

               char VertText[256],FaceText[256];
               appSprintf(VertText,"Vertex %i:",NumVerts);
               appSprintf(FaceText,"Face %i:",TempNumPolys);

               if( appStrstr(Str,VertText) )
               {
                   PointPool[NumVerts].X = appAtof(appStrstr(Str,"X:")+2);
                   PointPool[NumVerts].Y = appAtof(appStrstr(Str,"Y:")+2);
                   PointPool[NumVerts].Z = appAtof(appStrstr(Str,"Z:")+2);
                   NumVerts++;
                   TempVerts++;
               }
               else if( appStrstr(Str,FaceText) )
               {
                   Poly.Init();
                   Poly.NumVertices=3;
                   Poly.Vertex[0] = PointPool[appAtoi(appStrstr(Str,"A:")+2)];
                   Poly.Vertex[1] = PointPool[appAtoi(appStrstr(Str,"B:")+2)];
                   Poly.Vertex[2] = PointPool[appAtoi(appStrstr(Str,"C:")+2)];
                   Poly.Base = Poly.Vertex[0];
                   Poly.Finalize(0);
                   Polys->AddItem(Poly);
                   TempNumPolys++;
               }
               else if( appStrstr(Str,"Tri-mesh,") )
                   goto AscReloop;
           }
           debugf( NAME_Log, "Imported %i vertices, %i faces", TempVerts, Polys->Num() );
       }
       else if( GetBEGIN(&Str,"POLYGON") ) // Unreal .t3d file.
       {
           // Init to defaults and get group/item and texture.
           Poly.Init();
           Parse( Str, "LINK=", Poly.iLink );
           Parse( Str, "ITEM=", Poly.ItemName );
           ParseObject( Str, "TEXTURE=", Poly.Texture, ANY_PACKAGE );
           Parse( Str, "FLAGS=", Poly.PolyFlags );
           Poly.PolyFlags &= ~PF_NoImport;
       }
       else if( ParseCommand(&Str,"PAN") )
       {
           Parse( Str, "U=", Poly.PanU );
           Parse( Str, "V=", Poly.PanV );
       }
       else if( ParseCommand(&Str,"ORIGIN") )
       {
           GotBase=1;
           GetFVECTOR( Str, Poly.Base );
       }
       else if( ParseCommand(&Str,"VERTEX") )
       {
           if( Poly.NumVertices < FPoly::MAX_VERTICES )
           {
               GetFVECTOR( Str, Poly.Vertex[Poly.NumVertices] );
               Poly.NumVertices++;
           }
       }
       else if( ParseCommand(&Str,"TEXTUREU") )
       {
           GetFVECTOR( Str, Poly.TextureU );
       }
       else if( ParseCommand(&Str,"TEXTUREV") )
       {
           GetFVECTOR( Str, Poly.TextureV );
       }
       else if( GetEND(&Str,"POLYGON") )
       {
           if( !GotBase )
               Poly.Base = Poly.Vertex[0];
           if( Poly.Finalize(1)==0 )
               Polys->AddItem(Poly);
           GotBase=0;
       }
   }

   // Success.
   return Polys;
   unguard;

} IMPLEMENT_CLASS(UPolysFactory);

/*-----------------------------------------------------------------------------

   UModelFactory.

-----------------------------------------------------------------------------*/

UModelFactory::UModelFactory() {

   guard(UModelFactory::UModelFactory);

   // Init UFactory properties.
   SupportedClass = UModel::StaticClass;
   new(Formats)FString("t3d;Unreal model text");
   bCreateNew = 0;

   unguard;

} UObject* UModelFactory::Create (

   UClass*             Class,
   UObject*            InParent,
   FName               Name,
   UObject*            Context,
   const char*         Type,
   const char*&        Buffer,
   const char*         BufferEnd,
   FFeedbackContext*   Warn

) {

   guard(UModelFactory::Create);
   ABrush* TempOwner = (ABrush*)Context;
   UModel* Model = new( InParent, Name )UModel( TempOwner, 1 );

   const char* StrPtr;
   char StrLine[256];
   if( TempOwner )
   {
       TempOwner->InitPosRotScale();
       TempOwner->bSelected   = 0;
       TempOwner->bTempEditor = 0;
   }
   while( ParseLine( &Buffer, StrLine, 256 ) )
   {
       StrPtr = StrLine;
       if( GetEND(&StrPtr,"BRUSH") )
       {
           break;
       }
       else if( GetBEGIN (&StrPtr,"POLYLIST") )
       {
           UFactory* PolysFactory=NULL;
           for( TObjectIterator It; It; ++It )
               if( appStricmp(It->GetClassName(),"PolysFactory")==0 )
                   PolysFactory = *It;
           check(PolysFactory);
           Model->Polys = (UPolys*)PolysFactory->Create(UPolys::StaticClass,InParent,NAME_None,NULL,Type,Buffer,BufferEnd);
           check(Model->Polys);
       }
       if( TempOwner )
       {
           if      (ParseCommand(&StrPtr,"PREPIVOT"    )) GetFVECTOR   (StrPtr,TempOwner->PrePivot);
           else if (ParseCommand(&StrPtr,"SCALE"       )) GetFSCALE    (StrPtr,TempOwner->MainScale);
           else if (ParseCommand(&StrPtr,"POSTSCALE"   )) GetFSCALE    (StrPtr,TempOwner->PostScale);
           else if (ParseCommand(&StrPtr,"LOCATION"    )) GetFVECTOR   (StrPtr,TempOwner->Location);
           else if (ParseCommand(&StrPtr,"ROTATION"    )) GetFROTATOR  (StrPtr,TempOwner->Rotation,1);
           if( ParseCommand(&StrPtr,"SETTINGS") )
           {
               Parse( StrPtr, "CSG=", TempOwner->CsgOper );
               Parse( StrPtr, "POLYFLAGS=", TempOwner->PolyFlags );
           }
       }
   }
   if( GEditor )
       GEditor->bspValidateBrush( Model, 1, 0 );

   return Model;
   unguard;

} IMPLEMENT_CLASS(UModelFactory);

/*-----------------------------------------------------------------------------

   USoundFactory.

-----------------------------------------------------------------------------*/

USoundFactory::USoundFactory() {

   guard(USoundFactory::USoundFactory);

   // Init UFactory properties.
   SupportedClass = USound::StaticClass;
   new(Formats)FString("wav;Wave audio files");
   bCreateNew = 0;

   unguard;

} UObject* USoundFactory::Create (

   UClass*             Class,
   UObject*            InParent,
   FName               Name,
   UObject*            Context,
   const char*         FileType,
   const char*&        Buffer,
   const char*         BufferEnd,
   FFeedbackContext*   Warn

) {

   guard(USoundFactory::Create);
   USound* Sound=NULL;
   if( appStricmp(FileType, "WAV")==0 )
   {
       // Wave file.
       USound* Sound = new(InParent,Name)USound;
       Sound->FileType = FName("WAV");
       Sound->Data.Add( BufferEnd-Buffer );
       appMemcpy( &Sound->Data(0), Buffer, Sound->Data.Num() );
       return Sound;
   }
   else if( appStricmp(FileType, "UFX")==0 )
   {
       // Ancient ufx file containing (hopefully one) Wave file.
       Warn->Warnf( "Invalid old-format sound %s", *Name );
       return NULL;
   }
   else
   {
       // Unrecognized.
       Warn->Warnf( "Unrecognized sound format '%s' in %s", FileType, *Name );
       return NULL;
   }
   unguard;

} IMPLEMENT_CLASS(USoundFactory);

/*-----------------------------------------------------------------------------

   UMusicFactory.

-----------------------------------------------------------------------------*/

UMusicFactory::UMusicFactory() {

   guard(UMusicFactory::UMusicFactory);

   // Init UFactory properties.
   SupportedClass = UMusic::StaticClass;
   new(Formats)FString("mod;Amiga modules;s3m;Scream Tracker 3");
   bCreateNew = 0;

   unguard;

} UObject* UMusicFactory::Create (

   UClass*             Class,
   UObject*            InParent,
   FName               Name,
   UObject*            Context,
   const char*         FileType,
   const char*&        Buffer,
   const char*         BufferEnd,
   FFeedbackContext*   Warn

) {

   guard(UMusicFactory::Create);

   UMusic* Music = new( InParent, Name )UMusic;
   Music->FileType = FName(FileType);
   Music->Data.Add( BufferEnd - Buffer );
   appMemcpy( &Music->Data(0), Buffer, Music->Data.Num() );
   return Music;

   unguard;

} IMPLEMENT_CLASS(UMusicFactory);

/*-----------------------------------------------------------------------------

   UTextureFactory.

-----------------------------------------------------------------------------*/

// // 128-byte header found at the beginning of a ".PCX" file. // class FPCXFileHeader { public:

   BYTE    Manufacturer;       // Always 10.
   BYTE    Version;            // PCX file version.
   BYTE    Encoding;           // 1=run-length, 0=none.
   BYTE    BitsPerPixel;       // 1,2,4, or 8.
   _WORD   XMin;               // Dimensions of the image.
   _WORD   YMin;               // Dimensions of the image.
   _WORD   XMax;               // Dimensions of the image.
   _WORD   YMax;               // Dimensions of the image.
   _WORD   hdpi;               // Horizontal printer resolution.
   _WORD   vdpi;               // Vertical printer resolution.
   BYTE    OldColorMap[48];    // Old colormap info data.
   BYTE    Reserved1;          // Must be 0.
   BYTE    NumPlanes;          // Number of color planes (1, 3, 4, etc).
   _WORD   BytesPerLine;       // Number of bytes per scanline.
   _WORD   PaletteType;        // How to interpret palette: 1=color, 2=gray.
   _WORD   HScreenSize;        // Horizontal monitor size.
   _WORD   VScreenSize;        // Vertical monitor size.
   BYTE    Reserved2[54];      // Must be 0.

};

// Headers found at the beginning of a ".BMP" file.

  1. pragma pack(push,1)

struct FBitmapFileHeader {

   _WORD bfType;
   DWORD bfSize;
   _WORD bfReserved1;
   _WORD bfReserved2;
   DWORD bfOffBits;

};

  1. pragma pack(pop)
  1. pragma pack(push,1)

struct FBitmapInfoHeader {

   DWORD biSize;
   DWORD biWidth;
   DWORD biHeight;
   _WORD biPlanes;
   _WORD biBitCount;
   DWORD biCompression;
   DWORD biSizeImage;
   DWORD biXPelsPerMeter;
   DWORD biYPelsPerMeter;
   DWORD biClrUsed;
   DWORD biClrImportant;

};

  1. pragma pack(pop)

UTextureFactory::UTextureFactory() {

   guard(UTextureFactory::UTextureFactory);

   // Init UFactory properties.
   SupportedClass = UTexture::StaticClass;
   new(Formats)FString("bmp;Bitmap files;pcx;PC Painbrush files");
   bCreateNew = 0;

   unguard;

} char* GFile=NULL; UObject* UTextureFactory::Create (

   UClass*             Class,
   UObject*            InParent,
   FName               Name,
   UObject*            Context,
   const char*         Type,
   const char*&        Buffer,
   const char*         BufferEnd,
   FFeedbackContext*   Warn

) {

   guard(UTextureFactory::Create);
   UTexture* Texture = NULL;

   const FPCXFileHeader*    PCX   = (FPCXFileHeader *)Buffer;
   const FBitmapFileHeader* bmf   = (FBitmapFileHeader *)(Buffer + 0);
   const FBitmapInfoHeader* bmhdr = (FBitmapInfoHeader *)(Buffer + sizeof(FBitmapFileHeader));

   // Validate it.
   int Length = BufferEnd - Buffer;
   if( Length < sizeof(FPCXFileHeader) )
   {
       // Doesn't contain valid header.
       Warn->Warnf( "Texture: Invalid header" );
       return NULL;
   }
   else if( Buffer[0]=='B' && Buffer[1]=='M' )
   {
       // This is a .bmp type data stream.
       if( bmhdr->biPlanes!=1 || bmhdr->biBitCount!=8 )
       {
           // Not a 256 color .bmp image.
           Warn->Warnf( "BMP is not 8-bit color! planes=%i bits=%i", bmhdr->biPlanes, bmhdr->biBitCount );
           return NULL;
       }
       if( (bmhdr->biWidth&(bmhdr->biWidth-1)) || (bmhdr->biHeight&(bmhdr->biHeight-1)) )
       {
           Warn->Warnf( "Texture dimensions are not powers of two" );
           return NULL;
       }

       // Set texture properties.
       Texture = CastChecked(GObj.ConstructObject( Class, InParent, Name ) );
       Texture->Init( bmhdr->biWidth, bmhdr->biHeight );
       Texture->PostLoad();

       // Import it.
       //old: Buffer += sizeof(FBitmapFileHeader); // Skip 1st bmp header.
       //old: Buffer += sizeof(FBitmapInfoHeader); // Skip 2nd bmp header.
       //old: Buffer += 4 * 256;                   // Skip palette data.
       //old: Buffer += 4 * (bmhdr->biClrUsed ? bmhdr->biClrUsed : 256);
       const BYTE* Ptr = (BYTE*)Buffer + bmf->bfOffBits;

       // Copy upside-down scanlines.
       for( INT y=0; y<(INT)bmhdr->biHeight; y++ )
           appMemcpy
           (
               &Texture->Mips(0).DataArray((bmhdr->biHeight - 1 - y) * bmhdr->biWidth),
               &Ptr[y * Align(bmhdr->biWidth,4)],
               bmhdr->biWidth
           );

       // Do palette.
       UPalette* Palette = new( InParent )UPalette;
       Palette->SetFlags( RF_Public );
       const BYTE* bmpal = (BYTE*)Buffer + sizeof(FBitmapFileHeader) + sizeof(FBitmapInfoHeader);
       Palette->Colors.Empty();
       Palette->Colors.Add( NUM_PAL_COLORS );
       for( INT i=0; i<NUM_PAL_COLORS; i++ )
       {
           Palette->Colors(i).B = *bmpal++;
           Palette->Colors(i).G = *bmpal++;
           Palette->Colors(i).R = *bmpal++;
           *bmpal++;
       }
       Texture->Palette = Palette->ReplaceWithExisting();
   }
   else if( PCX->Manufacturer == 10 )
   {
       // This is a .PCX.
       if( PCX->BitsPerPixel!=8 || PCX->NumPlanes!=1 )
       {
           // Bad format, must have 8 bits per pixel, 1 plane.
           Warn->Warnf("PCX: Bad header (%i/%i)", PCX->BitsPerPixel, PCX->NumPlanes );
           return NULL;
       }

       int NewU = PCX->XMax + 1 - PCX->XMin;
       int NewV = PCX->YMax + 1 - PCX->YMin;
       if( (NewU&(NewU-1)) || (NewV&(NewV-1)) )
       {
           Warn->Warnf( "Texture dimensions are not powers of two" );
           return NULL;
       }

       // Set texture properties.
       Texture = CastChecked(GObj.ConstructObject( Class, InParent, Name ) );
       Texture->Init( NewU, NewV );
       Texture->PostLoad();

       // Import it.
       BYTE* DestPtr   = &Texture->Mips(0).DataArray(0);
       BYTE* DestEnd   = DestPtr + Texture->Mips(0).DataArray.Num();
       Buffer += 128;
       while( DestPtr < DestEnd )
       {
           BYTE Color = *Buffer++;
           if( (Color & 0xc0) == 0xc0 )
           {
               INT RunLength = Color & 0x3f;
               Color     = *Buffer++;
               appMemset( DestPtr, Color, Min(RunLength,(int)(DestEnd - DestPtr)) );
               DestPtr  += RunLength;
           }
           else *DestPtr++ = Color;
       }

       // Do the palette.
       UPalette* Palette = new( InParent )UPalette;
       Palette->SetFlags( RF_Public );
       BYTE* PCXPalette = (BYTE *)(BufferEnd - NUM_PAL_COLORS * 3);
       Palette->Colors.Empty();
       Palette->Colors.Add( NUM_PAL_COLORS );
       for( int i=0; i<NUM_PAL_COLORS; i++ )
       {
           Palette->Colors(i).R = *PCXPalette++;
           Palette->Colors(i).G = *PCXPalette++;
           Palette->Colors(i).B = *PCXPalette++;
       }
       Texture->Palette = Palette->ReplaceWithExisting();
   }
   else
   {
       // Unknown format.
       Warn->Warnf( "Bad image format for texture import" );
       return NULL;
   }

   // See if part of an animation sequence.
   check(Texture);
   if( appStrlen(Texture->GetName())>=4 )
   {
       char Temp[256];
       appStrcpy( Temp, Texture->GetName() );
       char* End = Temp + appStrlen(Temp) - 4;
       if( End[0]=='_' && appToUpper(End[1])=='A' && appIsDigit(End[2]) && appIsDigit(End[3]) )
       {
           int i = appAtoi( End+2 );
           debugf( NAME_Log, "Texture animation frame %i: %s", i, Texture->GetName() );
           if( i>0 )
           {
               appSprintf( End+2, "%02i", i-1 );
               UTexture* Other = FindObject( Texture->GetParent(), Temp );
               if( Other )
               {
                   Other->AnimNext = Texture;
                   debugf( NAME_Log, "   Linked to previous: %s", Other->GetName() );
               }
           }
           if( i<99 )
           {
               appSprintf( End+2, "%02i", i+1 );
               UTexture* Other = FindObject( Texture->GetParent(), Temp );
               if( Other )
               {
                   Texture->AnimNext = Other;
                   debugf( NAME_Log, "   Linked to next: %s", Other->GetName() );
               }
           }
       }
   }

  1. if 0

   // Handle bumps.
   if( 1 )
   {
       FMemMark Mark(GMem);
       INT      U      = Texture->USize;
       INT      V      = Texture->VSize;
       FColor*  Colors = &Texture->Palette->Colors(0);
       BYTE*    Data   = &Texture->Mips(0).DataArray(0);
       FVector* Grad   = new(GMem)FVector[U*V];
       for( INT i=0,ii=V-1; i<V; ii=i++ )
       {
           for( INT j=0,jj=U-1; j<U; jj=j++ )
           {
               FVector A(0,0,Colors[Data[i  * U + j  ]].FBrightness());
               FVector B(1,0,Colors[Data[i  * U + jj ]].FBrightness());
               FVector C(0,1,Colors[Data[ii * U + j  ]].FBrightness());
               Grad[i*U+j] = (((A-B) ^ (C-A)) * FVector(32,32,1)).UnsafeNormal();
           }
       }

       for( i=0,ii=V-1; i<V; ii=i++ )
           for( INT j=0,jj=U-1; j<U; jj=j++ )
               Grad[i*U+j] = (Grad[i*U+j]+Grad[ii*U+j]+Grad[i*U+jj]+Grad[ii*U+jj]).SafeNormal();

       GFile[appStrlen(GFile)-3]='b';
       GFile[appStrlen(GFile)-2]='m';
       GFile[appStrlen(GFile)-1]='p';
       FILE* F = appFopen( GFile, "w+b" );
       if( F )
       {
           FBitmapFileHeader FH;
           FBitmapInfoHeader IH;

           FH.bfType       = 'B' + 256*'M';
           FH.bfSize       = sizeof(FH) + sizeof(IH) + 3 * U * V;
           FH.bfReserved1  = 0;
           FH.bfReserved2  = 0;
           FH.bfOffBits    = sizeof(FH) + sizeof(IH);
           appFwrite( &FH, sizeof(FH), 1, F );

           IH.biSize           = sizeof(IH);
           IH.biWidth          = U;
           IH.biHeight         = V;
           IH.biPlanes         = 1;
           IH.biBitCount       = 24;
           IH.biCompression    = 0; //BI_RGB
           IH.biSizeImage      = U * V * 3;
           IH.biXPelsPerMeter  = 0;
           IH.biYPelsPerMeter  = 0;
           IH.biClrUsed        = 0;
           IH.biClrImportant   = 0;
           appFwrite( &IH, sizeof(IH), 1, F );

           for( INT i=V-1; i>=0; i-- )
           {
               for( INT j=0; j<U; j++ )
               {
                   struct {BYTE B,G,R,A;} C;
                   C.R      = Clamp( appFloor(128+Grad[i*U+j].Y*127), 0, 255 );
                   C.B      = Clamp( appFloor(128+Grad[i*U+j].X*127), 0, 255 );
                   C.G      = Clamp( appFloor(128+Grad[i*U+j].Z*127), 0, 255 );
                   appFwrite( &C, 3, 1, F );
               }
           }
           appFclose( F );
       }
       Mark.Pop();
   }

  1. endif

   return Texture;
   unguard;

} IMPLEMENT_CLASS(UTextureFactory);

/*------------------------------------------------------------------------------

   UFontFactory.

------------------------------------------------------------------------------*/

// // Fast pixel-lookup. // static inline BYTE AT( BYTE* Screen, int SXL, int X, int Y ) {

   return Screen[X+Y*SXL];

}

// // Find the border around a font character that starts at x,y (it's upper // left hand corner). If it finds a character box, it returns 0 and the // character's length (xl,yl). Otherwise returns -1. // static int UFont_ScanFontBox( UTexture* Texture, INT X, INT Y, INT& XL, INT& YL ) {

   guard(UFont_ScanFontBox);
   BYTE* Data = &Texture->GetMip(0)->DataArray(0);
   INT FontXL = Texture->USize;

   // Find x-length.
   INT NewXL = 1;
   while ( AT(Data,FontXL,X+NewXL,Y)==255 && AT(Data,FontXL,X+NewXL,Y+1)!=255 )
       NewXL++;

   if( AT(Data,FontXL,X+NewXL,Y)!=255 )
       return -1;

   // Find y-length.
   INT NewYL = 1;
   while( AT(Data,FontXL,X,Y+NewYL)==255 && AT(Data,FontXL,X+1,Y+NewYL)!=255 )
       NewYL++;

   if( AT(Data,FontXL,X,Y+NewYL)!=255 )
       return -1;

   XL = NewXL - 1;
   YL = NewYL - 1;

   return 0;
   unguard;

}

UFontFactory::UFontFactory() {

   guard(UFontFactory::UFontFactory);

   // Use UTextureFactory properties.
   SupportedClass = UFont::StaticClass;

   unguard;

} UObject* UFontFactory::Create (

   UClass*             Class,
   UObject*            InParent,
   FName               Name,
   UObject*            Context,
   const char*         Type,
   const char*&        Buffer,
   const char*         BufferEnd,
   FFeedbackContext*   Warn

) {

   guard(UFontFactory::Create);
   UFont* Font = (UFont*)UTextureFactory::Create( Class, InParent, Name, Context, Type, Buffer, BufferEnd, Warn );
   if( Font )
   {
       // Init.
       BYTE* TextureData = &Font->GetMip(0)->DataArray(0);
       Font->Characters.Add( UFont::NUM_FONT_CHARS );
       for( int i=0; iCharacters.Num(); i++ )
       {
           Font->Characters(i).StartU = 0; Font->Characters(i).USize = 0;
           Font->Characters(i).StartV = 0; Font->Characters(i).VSize = 0;
       }

       // Scan in all fonts, starting at character 32.
       i = 32;
       int Y = 0;
       do
       {
           int X = 0;
           while( AT(TextureData,Font->USize,X,Y)!=255 && YVSize )
           {
               X++;
               if( X >= Font->USize )
               {
                   X = 0;
                   if( ++Y >= Font->VSize )
                       break;
               }
           }

           // Scan all characters in this row.
           if( Y < Font->VSize )
           {
               int XL=0, YL=0, MaxYL=0;
               while( iCharacters.Num() && UFont_ScanFontBox(Font,X,Y,XL,YL)==0 )
               {
                   Font->Characters(i).StartU = X+1;
                   Font->Characters(i).StartV = Y+1;
                   Font->Characters(i).USize  = XL;
                   Font->Characters(i).VSize  = YL;
                   X += XL + 1;
                   i++;
                   if( YL > MaxYL )
                       MaxYL = YL;
               }
               Y += MaxYL + 1;
           }
       } while( iCharacters.Num() && YVSize );

       // Cleanup font data.
       for( i=0; iMips.Num(); i++ )
           for( INT j=0; jMips(i).DataArray.Num(); j++ )
               if( Font->Mips(i).DataArray(j)==255 )
                   Font->Mips(i).DataArray(j) = 0;
   }
   return Font;
   unguard;

} IMPLEMENT_CLASS(UFontFactory);

/*------------------------------------------------------------------------------

   The end.

------------------------------------------------------------------------------*/