text: /*
  ** buildtexture.cpp
  ** Handling Build textures (now as a usable editing feature!)
  **
  **---------------------------------------------------------------------------
  ** Copyright 2004-2006 Randy Heit
  ** Copyright 2018 Christoph Oelckers
  ** All rights reserved.
  **
  ** Redistribution and use in source and binary forms, with or without
  ** modification, are permitted provided that the following conditions
  ** are met:
  **
  ** 1. Redistributions of source code must retain the above copyright
  **    notice, this list of conditions and the following disclaimer.
  ** 2. 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.
  ** 3. The name of the author may not be used to endorse or promote products
  **    derived from this software without specific prior written permission.
  **
  ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
  **---------------------------------------------------------------------------
  **
  **
  */
  
  #include "files.h"
  #include "filesystem.h"
  #include "templates.h"
  #include "cmdlib.h"
  #include "colormatcher.h"
  #include "bitmap.h"
  #include "textures.h"
  #include "resourcefile.h"
  #include "image.h"
  #include "animations.h"
  #include "texturemanager.h"
  #include "r_translate.h"
  #include "r_data/sprites.h"
  
  //===========================================================================
  //
  // CountTiles
  //
  // Returns the number of tiles provided by an artfile
  //
  //===========================================================================
  
  static int CountTiles (const void *tiles)
  {
  	int version = LittleLong(*(uint32_t *)tiles);
  	if (version != 1)
  	{
  		return 0;
  	}
  
  	int tilestart = LittleLong(((uint32_t *)tiles)[2]);
  	int tileend = LittleLong(((uint32_t *)tiles)[3]);
  
  	return tileend >= tilestart ? tileend - tilestart + 1 : 0;
  }
  
  //===========================================================================
  //
  // Create palette data and remap table for the tile set's palette
  //
  //===========================================================================
  
  static int BuildPaletteTranslation(int lump)
  {
  	if (fileSystem.FileLength(lump) < 768)
  	{
  		return false;
  	}
  
  	FileData data = fileSystem.ReadFile(lump);
  	const uint8_t *ipal = (const uint8_t *)data.GetMem();
  	FRemapTable opal;
  
  	bool blood = false;
  	for (int c = 0; c < 765; c++)	// Build used VGA palettes (color values 0..63), Blood used full palettes (0..255) Let's hope this doesn't screw up...
  	{
  		if (ipal[c] >= 64)
  		{
  			blood = true;
  			break;
  		}
  	}
  
  	for (int c = 0; c < 255; c++)
  	{
  		int r, g, b;
  		if (!blood)
  		{
  			r = (ipal[3*c    ] << 2) | (ipal[3 * c    ] >> 4);
  			g = (ipal[3*c + 1] << 2) | (ipal[3 * c + 1] >> 4);
  			b = (ipal[3*c + 2] << 2) | (ipal[3 * c + 2] >> 4);
  		}
  		else
  		{
  			r = ipal[3 * c] << 2;
  			g = ipal[3 * c + 1] << 2;
  			b = ipal[3 * c + 2] << 2;
  		}
  		opal.Palette[c] = PalEntry(255, r, g, b);
  		opal.Remap[c] = ColorMatcher.Pick(r, g, b);
  	}
  	// The last entry is transparent.
  	opal.Palette[255] = 0;
  	opal.Remap[255] = 0;
  	// Store the remap table in the translation manager so that we do not need to keep track of it ourselves. 
  	// Slot 0 for internal translations is a convenient location because normally it only contains a small number of translations.
  	return GetTranslationIndex(GPalette.StoreTranslation(TRANSLATION_Standard, &opal));
  }
  
  
  //===========================================================================
  //
  // AddTiles
  //
  // Adds all the tiles in an artfile to the texture manager.
  //
  //===========================================================================
  
  void AddTiles(const FString& pathprefix, const void* tiles, FRemapTable *remap)
  {
  
  	//	int numtiles = LittleLong(((uint32_t *)tiles)[1]);	// This value is not reliable
  	int tilestart = LittleLong(((uint32_t*)tiles)[2]);
  	int tileend = LittleLong(((uint32_t*)tiles)[3]);
  	const uint16_t* tilesizx = &((const uint16_t*)tiles)[8];
  	const uint16_t* tilesizy = &tilesizx[tileend - tilestart + 1];
  	const uint32_t* picanm = (const uint32_t*)&tilesizy[tileend - tilestart + 1];
  	const uint8_t* tiledata = (const uint8_t*)&picanm[tileend - tilestart + 1];
  
  	for (int i = tilestart; i <= tileend; ++i)
  	{
  		int pic = i - tilestart;
  		int width = LittleShort(tilesizx[pic]);
  		int height = LittleShort(tilesizy[pic]);
  		uint32_t anm = LittleLong(picanm[pic]);
  		int xoffs = (int8_t)((anm >> 8) & 255) + width / 2;
  		int yoffs = (int8_t)((anm >> 16) & 255) + height / 2;
  		int size = width * height;
  		FTextureID texnum;
  
  		if (width <= 0 || height <= 0) continue;
  
  		FStringf name("%sBTIL%04d", pathprefix.GetChars(), i);
  		auto tex = MakeGameTexture(new FImageTexture(new FBuildTexture(pathprefix, i, tiledata, remap, width, height, xoffs, yoffs)), name, ETextureType::Override);
  		texnum = TexMan.AddGameTexture(tex);
  		tiledata += size;
  
  
  		// reactivate only if the texture counter works here.
  		//StartScreen->Progress();
  
  		if ((picanm[pic] & 63) && (picanm[pic] & 192))
  		{
  			int type, speed;
  
  			switch (picanm[pic] & 192)
  			{
  			case 64:	type = 2;	break;
  			case 128:	type = 0;	break;
  			case 192:	type = 1;	break;
  			default:    type = 0;   break;  // Won't happen, but GCC bugs me if I don't put this here.
  			}
  
  			speed = (anm >> 24) & 15;
  			speed = MAX(1, (1 << speed) * 1000 / 120);	// Convert from 120 Hz to 1000 Hz.
  
  			TexAnim.AddSimpleAnim(texnum, picanm[pic] & 63, type, speed);
  		}
  
  		// Blood's rotation types:
  		// 0 - Single
  		// 1 - 5 Full
  		// 2 - 8 Full
  		// 3 - Bounce (looks no different from Single; seems to signal bouncy sprites)
  		// 4 - 5 Half (not used in game)
  		// 5 - 3 Flat (not used in game)
  		// 6 - Voxel
  		// 7 - Spin Voxel
  
  		int rotType = (anm >> 28) & 7;
  		if (rotType == 1)
  		{
  			spriteframe_t rot;
  			rot.Texture[0] =
  				rot.Texture[1] = texnum;
  			for (int j = 1; j < 4; ++j)
  			{
  				rot.Texture[j * 2].SetIndex(texnum.GetIndex() + j);
  				rot.Texture[j * 2 + 1].SetIndex(texnum.GetIndex() + j);
  				rot.Texture[16 - j * 2].SetIndex(texnum.GetIndex() + j);
  				rot.Texture[17 - j * 2].SetIndex(texnum.GetIndex() + j);
  			}
  			rot.Texture[8].SetIndex(texnum.GetIndex());
  			rot.Texture[9].SetIndex(texnum.GetIndex());
  			rot.Flip = 0x00FC;
  			rot.Voxel = NULL;
  			tex->SetRotations(SpriteFrames.Push(rot));
  		}
  		else if (rotType == 2)
  		{
  			spriteframe_t rot;
  			rot.Texture[0] =
  				rot.Texture[1] = texnum;
  			for (int j = 1; j < 8; ++j)
  			{
  				rot.Texture[16 - j * 2].SetIndex(texnum.GetIndex() + j);
  				rot.Texture[17 - j * 2].SetIndex(texnum.GetIndex() + j);
  			}
  			rot.Flip = 0;
  			rot.Voxel = NULL;
  			tex->SetRotations(SpriteFrames.Push(rot));
  		}
  	}
  }
  
  //===========================================================================
  //
  // R_CountBuildTiles
  //
  // Returns the number of tiles found. Also loads all the data for
  // R_InitBuildTiles() to process later.
  //
  //===========================================================================
  
  void InitBuildTiles()
  {
  	int lumpnum;
  	int numtiles;
  	int totaltiles = 0;
  
  	// The search rules are as follows: 
  	// - scan the entire lump directory for palette.dat files.
  	// - if one is found, process the directory for .ART files and add textures for them.
  	// - once all have been found, process all directories that may contain Build data.
  	// - the root is not excluded which allows to read this from .GRP files as well.
  	// - Blood support has been removed because it is not useful for modding to have loose .ART files.
  	//
  	// Unfortunately neither the palettes nor the .ART files contain any usable identifying marker
  	// so this can only go by the file names.
  
  	int numlumps = fileSystem.GetNumEntries();
  	for (int i = 0; i < numlumps; i++)
  	{
  		const char* name = fileSystem.GetFileFullName(i);
  		if (fileSystem.CheckNumForFullName(name) != i) continue;	// This palette is hidden by a later one. Do not process
  		FString base = ExtractFileBase(name, true);
  		base.ToLower();
  		if (base.Compare("palette.dat") == 0 && fileSystem.FileLength(i) >= 768)	// must be a valid palette, i.e. at least 256 colors.
  		{
  			FString path = ExtractFilePath(name);
  			if (path.IsNotEmpty() && path.Back() != '/') path += '/';
  
  			int translation = BuildPaletteTranslation(i);
  			auto remap = GPalette.GetTranslation(TRANSLATION_Standard, translation);
  
  			for (int numartfiles = 0; numartfiles < 1000; numartfiles++)
  			{
  				FStringf artpath("%stiles%03d.art", path.GetChars(), numartfiles);
  				// only read from the same source as the palette.
  				// The entire format here is just too volatile to allow liberal mixing.
  				// An .ART set must be treated as one unit.
  				lumpnum = fileSystem.CheckNumForFullName(artpath, fileSystem.GetFileContainer(i));
  				if (lumpnum < 0)
  				{
  					break;
  				}
  
  				auto& artdata = TexMan.GetNewBuildTileData();
  				artdata.Resize(fileSystem.FileLength(lumpnum));
  				fileSystem.ReadFile(lumpnum, &artdata[0]);
  
  				if ((numtiles = CountTiles(&artdata[0])) > 0)
  				{
  					AddTiles(path, &artdata[0], remap);
  					totaltiles += numtiles;
  				}
  			}
  		}
  	}
  }
  
  