Ejemplo n.º 1
0
// Returns false if the hash fails and the user hits "Yes"
static bool VerifyRoms()
{
  struct DspRomHashes
  {
    u32 hash_irom;  // dsp_rom.bin
    u32 hash_drom;  // dsp_coef.bin
  };

  static const std::array<DspRomHashes, 4> known_roms = {
      {// Official Nintendo ROM
       {0x66f334fe, 0xf3b93527},

       // LM1234 replacement ROM (Zelda UCode only)
       {0x9c8f593c, 0x10000001},

       // delroth's improvement on LM1234 replacement ROM (Zelda and AX only,
       // IPL/Card/GBA still broken)
       {0xd9907f71, 0xb019c2fb},

       // above with improved resampling coefficients
       {0xd9907f71, 0xdb6880c1}}};

  u32 hash_irom = HashAdler32((u8*)g_dsp.irom, DSP_IROM_BYTE_SIZE);
  u32 hash_drom = HashAdler32((u8*)g_dsp.coef, DSP_COEF_BYTE_SIZE);
  int rom_idx = -1;

  for (size_t i = 0; i < known_roms.size(); ++i)
  {
    const DspRomHashes& rom = known_roms[i];
    if (hash_irom == rom.hash_irom && hash_drom == rom.hash_drom)
      rom_idx = static_cast<int>(i);
  }

  if (rom_idx < 0)
  {
    if (AskYesNoT("Your DSP ROMs have incorrect hashes.\n"
                  "Would you like to stop now to fix the problem?\n"
                  "If you select \"No\", audio might be garbled."))
      return false;
  }

  if (rom_idx == 1)
  {
    DSPHost::OSD_AddMessage("You are using an old free DSP ROM made by the Dolphin Team.", 6000);
    DSPHost::OSD_AddMessage("Only games using the Zelda UCode will work correctly.", 6000);
  }
  else if (rom_idx == 2 || rom_idx == 3)
  {
    DSPHost::OSD_AddMessage("You are using a free DSP ROM made by the Dolphin Team.", 8000);
    DSPHost::OSD_AddMessage("All Wii games will work correctly, and most GC games should ", 8000);
    DSPHost::OSD_AddMessage("also work fine, but the GBA/IPL/CARD UCodes will not work.\n", 8000);
  }

  return true;
}
Ejemplo n.º 2
0
// Returns false iff the hash fails and the user hits "Yes"
static bool VerifyRoms(const char *irom_filename, const char *coef_filename)
{
	struct DspRomHashes
	{
		u32 hash_irom; // dsp_rom.bin
		u32 hash_drom; // dsp_coef.bin
	} KNOWN_ROMS[] = {
		// Official Nintendo ROM
		{ 0x66f334fe, 0xf3b93527 },

		// LM1234 replacement ROM (Zelda UCode only)
		{ 0x9c8f593c, 0x10000001 },

		// delroth's improvement on LM1234 replacement ROM (Zelda and AX only,
		// IPL/Card/GBA still broken)
		{ 0xd9907f71, 0xb019c2fb }
	};

	u32 hash_irom = HashAdler32((u8*)g_dsp.irom, DSP_IROM_BYTE_SIZE);
	u32 hash_drom = HashAdler32((u8*)g_dsp.coef, DSP_COEF_BYTE_SIZE);
	int rom_idx = -1;

	for (u32 i = 0; i < sizeof (KNOWN_ROMS) / sizeof (KNOWN_ROMS[0]); ++i)
	{
		DspRomHashes& rom = KNOWN_ROMS[i];
		if (hash_irom == rom.hash_irom && hash_drom == rom.hash_drom)
			rom_idx = i;
	}

	if (rom_idx < 0)
	{
		if (AskYesNoT("Your DSP ROMs have incorrect hashes.\n"
			"Would you like to stop now to fix the problem?\n"
			"If you select \"No\", audio might be garbled."))
			return false;
	}

	if (rom_idx == 1)
	{
		DSPHost_OSD_AddMessage("You are using an old free DSP ROM made by the Dolphin Team.", 6000);
		DSPHost_OSD_AddMessage("Only games using the Zelda UCode will work correctly.", 6000);
	}

	if (rom_idx == 2)
	{
		DSPHost_OSD_AddMessage("You are using a free DSP ROM made by the Dolphin Team.", 8000);
		DSPHost_OSD_AddMessage("All Wii games will work correctly, and most GC games should ", 8000);
		DSPHost_OSD_AddMessage("also work fine, but the GBA/IPL/CARD UCodes will not work.\n", 8000);
	}

	return true;
}
Ejemplo n.º 3
0
void CompressedBlobReader::GetBlock(u64 block_num, u8 *out_ptr)
{
	bool uncompressed = false;
	u32 comp_block_size = (u32)GetBlockCompressedSize(block_num);
	u64 offset = m_block_pointers[block_num] + m_data_offset;

	if (offset & (1ULL << 63))
	{
		if (comp_block_size != m_header.block_size)
			PanicAlert("Uncompressed block with wrong size");
		uncompressed = true;
		offset &= ~(1ULL << 63);
	}

	// clear unused part of zlib buffer. maybe this can be deleted when it works fully.
	memset(&m_zlib_buffer[comp_block_size], 0, m_zlib_buffer.size() - comp_block_size);

	m_file.Seek(offset, SEEK_SET);
	m_file.ReadBytes(m_zlib_buffer.data(), comp_block_size);

	// First, check hash.
	u32 block_hash = HashAdler32(m_zlib_buffer.data(), comp_block_size);
	if (block_hash != m_hashes[block_num])
		PanicAlertT("The disc image \"%s\" is corrupt.\n"
		            "Hash of block %" PRIu64 " is %08x instead of %08x.",
		            m_file_name.c_str(),
		            block_num, block_hash, m_hashes[block_num]);

	if (uncompressed)
	{
		std::copy(m_zlib_buffer.begin(), m_zlib_buffer.begin() + comp_block_size, out_ptr);
	}
	else
	{
		z_stream z = {};
		z.next_in  = m_zlib_buffer.data();
		z.avail_in = comp_block_size;
		if (z.avail_in > m_header.block_size)
		{
			PanicAlert("We have a problem");
		}
		z.next_out  = out_ptr;
		z.avail_out = m_header.block_size;
		inflateInit(&z);
		int status = inflate(&z, Z_FULL_FLUSH);
		u32 uncomp_size = m_header.block_size - z.avail_out;
		if (status != Z_STREAM_END)
		{
			// this seem to fire wrongly from time to time
			// to be sure, don't use compressed isos :P
			PanicAlert("Failure reading block %" PRIu64 " - out of data and not at end.", block_num);
		}
		inflateEnd(&z);
		if (uncomp_size != m_header.block_size)
			PanicAlert("Wrong block size");
	}
}
Ejemplo n.º 4
0
bool CompressFileToBlob(const std::string& infile_path, const std::string& outfile_path,
                        u32 sub_type, int block_size, CompressCB callback, void* arg)
{
  bool scrubbing = false;

  File::IOFile infile(infile_path, "rb");
  if (IsGCZBlob(infile))
  {
    PanicAlertT("\"%s\" is already compressed! Cannot compress it further.", infile_path.c_str());
    return false;
  }

  if (!infile)
  {
    PanicAlertT("Failed to open the input file \"%s\".", infile_path.c_str());
    return false;
  }

  File::IOFile outfile(outfile_path, "wb");
  if (!outfile)
  {
    PanicAlertT("Failed to open the output file \"%s\".\n"
                "Check that you have permissions to write the target folder and that the media can "
                "be written.",
                outfile_path.c_str());
    return false;
  }

  DiscScrubber disc_scrubber;
  if (sub_type == 1)
  {
    if (!disc_scrubber.SetupScrub(infile_path, block_size))
    {
      PanicAlertT("\"%s\" failed to be scrubbed. Probably the image is corrupt.",
                  infile_path.c_str());
      return false;
    }

    scrubbing = true;
  }

  z_stream z = {};
  if (deflateInit(&z, 9) != Z_OK)
    return false;

  callback(GetStringT("Files opened, ready to compress."), 0, arg);

  CompressedBlobHeader header;
  header.magic_cookie = GCZ_MAGIC;
  header.sub_type = sub_type;
  header.block_size = block_size;
  header.data_size = infile.GetSize();

  // round upwards!
  header.num_blocks = (u32)((header.data_size + (block_size - 1)) / block_size);

  std::vector<u64> offsets(header.num_blocks);
  std::vector<u32> hashes(header.num_blocks);
  std::vector<u8> out_buf(block_size);
  std::vector<u8> in_buf(block_size);

  // seek past the header (we will write it at the end)
  outfile.Seek(sizeof(CompressedBlobHeader), SEEK_CUR);
  // seek past the offset and hash tables (we will write them at the end)
  outfile.Seek((sizeof(u64) + sizeof(u32)) * header.num_blocks, SEEK_CUR);
  // seek to the start of the input file to make sure we get everything
  infile.Seek(0, SEEK_SET);

  // Now we are ready to write compressed data!
  u64 position = 0;
  int num_compressed = 0;
  int num_stored = 0;
  int progress_monitor = std::max<int>(1, header.num_blocks / 1000);
  bool success = true;

  for (u32 i = 0; i < header.num_blocks; i++)
  {
    if (i % progress_monitor == 0)
    {
      const u64 inpos = infile.Tell();
      int ratio = 0;
      if (inpos != 0)
        ratio = (int)(100 * position / inpos);

      std::string temp =
          StringFromFormat(GetStringT("%i of %i blocks. Compression ratio %i%%").c_str(), i,
                           header.num_blocks, ratio);
      bool was_cancelled = !callback(temp, (float)i / (float)header.num_blocks, arg);
      if (was_cancelled)
      {
        success = false;
        break;
      }
    }

    offsets[i] = position;

    size_t read_bytes;
    if (scrubbing)
      read_bytes = disc_scrubber.GetNextBlock(infile, in_buf.data());
    else
      infile.ReadArray(in_buf.data(), header.block_size, &read_bytes);
    if (read_bytes < header.block_size)
      std::fill(in_buf.begin() + read_bytes, in_buf.begin() + header.block_size, 0);

    int retval = deflateReset(&z);
    z.next_in = in_buf.data();
    z.avail_in = header.block_size;
    z.next_out = out_buf.data();
    z.avail_out = block_size;

    if (retval != Z_OK)
    {
      ERROR_LOG(DISCIO, "Deflate failed");
      success = false;
      break;
    }

    int status = deflate(&z, Z_FINISH);
    int comp_size = block_size - z.avail_out;

    u8* write_buf;
    int write_size;
    if ((status != Z_STREAM_END) || (z.avail_out < 10))
    {
      // PanicAlert("%i %i Store %i", i*block_size, position, comp_size);
      // let's store uncompressed
      write_buf = in_buf.data();
      offsets[i] |= 0x8000000000000000ULL;
      write_size = block_size;
      num_stored++;
    }
    else
    {
      // let's store compressed
      // PanicAlert("Comp %i to %i", block_size, comp_size);
      write_buf = out_buf.data();
      write_size = comp_size;
      num_compressed++;
    }

    if (!outfile.WriteBytes(write_buf, write_size))
    {
      PanicAlertT("Failed to write the output file \"%s\".\n"
                  "Check that you have enough space available on the target drive.",
                  outfile_path.c_str());
      success = false;
      break;
    }

    position += write_size;

    hashes[i] = HashAdler32(write_buf, write_size);
  }

  header.compressed_data_size = position;

  if (!success)
  {
    // Remove the incomplete output file.
    outfile.Close();
    File::Delete(outfile_path);
  }
  else
  {
    // Okay, go back and fill in headers
    outfile.Seek(0, SEEK_SET);
    outfile.WriteArray(&header, 1);
    outfile.WriteArray(offsets.data(), header.num_blocks);
    outfile.WriteArray(hashes.data(), header.num_blocks);
  }

  // Cleanup
  deflateEnd(&z);

  if (success)
  {
    callback(GetStringT("Done compressing disc image."), 1.0f, arg);
  }
  return success;
}
Ejemplo n.º 5
0
void PixelShaderCache::PrepareShader(
	PIXEL_SHADER_RENDER_MODE render_mode,
	u32 components, 
	const XFMemory &xfr,
	const BPMemory &bpm,
	bool ongputhread)
{
	const API_TYPE api = ((D3D::GetCaps().PixelShaderVersion >> 8) & 0xFF) < 3 ? API_D3D9_SM20 : API_D3D9_SM30;
	PixelShaderUid uid;
	GetPixelShaderUID(uid, render_mode, components, xfr, bpm);
	if (ongputhread)
	{
		Compiler->ProcCompilationResults();
#if defined(_DEBUG) || defined(DEBUGFAST)
		if (g_ActiveConfig.bEnableShaderDebugging)
		{
			ShaderCode code;
			GeneratePixelShaderCodeD3D9(code, uid.GetUidData());
		}
#endif
		// Check if the shader is already set
		if (last_entry[render_mode])
		{
			if (uid == last_uid[render_mode])
			{
				return;
			}
		}
		last_uid[render_mode] = uid;
		GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true);
	}
	else
	{
		if (external_last_uid[render_mode] == uid)
		{
			return;
		}
		external_last_uid[render_mode] = uid;
	}
	PixelShadersLock.lock();
	PSCacheEntry* entry = &PixelShaders[uid];
	PixelShadersLock.unlock();
	if (ongputhread)
	{
		last_entry[render_mode] = entry;
	}
	// Compile only when we have a new instance
	if (entry->initialized.test_and_set())
	{
		return;
	}
	// Need to compile a new shader
	
	ShaderCompilerWorkUnit *wunit = Compiler->NewUnit(PIXELSHADERGEN_BUFFERSIZE);
	wunit->GenerateCodeHandler = [uid, api](ShaderCompilerWorkUnit* wunit)
	{
		ShaderCode code;
		code.SetBuffer(wunit->code.data());
		if (api == API_D3D9_SM20)
		{
			GeneratePixelShaderCodeD3D9SM2(code, uid.GetUidData());
		}
		else
		{
			GeneratePixelShaderCodeD3D9(code, uid.GetUidData());
		}
		wunit->codesize = (u32)code.BufferSize();
	};
	wunit->entrypoint = "main";
	wunit->flags = D3DCOMPILE_SKIP_VALIDATION | D3DCOMPILE_OPTIMIZATION_LEVEL3;
	wunit->target = D3D::PixelShaderVersionString();
	wunit->ResultHandler = [uid, entry](ShaderCompilerWorkUnit* wunit)
	{
		if (SUCCEEDED(wunit->cresult))
		{
			ID3DBlob* shaderBuffer = wunit->shaderbytecode;
			const u8* bytecode = (const u8*)shaderBuffer->GetBufferPointer();
			u32 bytecodelen = (u32)shaderBuffer->GetBufferSize();
			g_ps_disk_cache.Append(uid, bytecode, bytecodelen);
			PushByteCode(uid, bytecode, bytecodelen, entry);
#if defined(_DEBUG) || defined(DEBUGFAST)
			if (g_ActiveConfig.bEnableShaderDebugging)
			{
				u32 code_hash = HashAdler32((const u8 *)wunit->code.data(), wunit->codesize);
				unique_shaders.insert(code_hash);
				entry->code = wunit->code.data();
			}
			if (g_ActiveConfig.iLog & CONF_SAVESHADERS) {
				static int counter = 0;
				char szTemp[MAX_PATH];
				sprintf(szTemp, "%sps_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), counter++);

				SaveData(szTemp, wunit->code.data());
			}
#endif			
		}
		else
		{
			static int num_failures = 0;
			std::string filename = StringFromFormat("%sbad_ps_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), num_failures++);
			std::ofstream file;
			OpenFStream(file, filename, std::ios_base::out);
			file << ((const char *)wunit->code.data());
			file << ((const char*)wunit->error->GetBufferPointer());
			file.close();

			PanicAlert("Failed to compile pixel shader!\nThis usually happens when trying to use Dolphin with an outdated GPU or integrated GPU like the Intel GMA series.\n\nIf you're sure this is Dolphin's error anyway, post the contents of %s along with this error message at the forums.\n\nDebug info (%s):\n%s",
				filename,
				D3D::VertexShaderVersionString(),
				(const char*)wunit->error->GetBufferPointer());
		}
	};
	Compiler->CompileShaderAsync(wunit);
}
Ejemplo n.º 6
0
void CompressedBlobReader::GetBlock(u64 block_num, u8 *out_ptr)
{
	bool uncompressed = false;
	u32 comp_block_size = (u32)GetBlockCompressedSize(block_num);
	u64 offset = block_pointers[block_num] + data_offset;

	if (offset & (1ULL << 63))
	{
		if (comp_block_size != header.block_size)
			PanicAlert("Uncompressed block with wrong size");
		uncompressed = true;
		offset &= ~(1ULL << 63);
	}

	// clear unused part of zlib buffer. maybe this can be deleted when it works fully.
	memset(zlib_buffer + comp_block_size, 0, zlib_buffer_size - comp_block_size);
	
	m_file.Seek(offset, SEEK_SET);
	m_file.ReadBytes(zlib_buffer, comp_block_size);

	u8* source = zlib_buffer;
	u8* dest = out_ptr;

	// First, check hash.
	u32 block_hash = HashAdler32(source, comp_block_size);
	if (block_hash != hashes[block_num])
		PanicAlert("Hash of block %lli is %08x instead of %08x.\n"
		           "Your ISO, %s, is corrupt.",
		           block_num, block_hash, hashes[block_num],
				   file_name.c_str());

	if (uncompressed)
	{
		memcpy(dest, source, comp_block_size);
	}
	else
	{
		z_stream z;
		memset(&z, 0, sizeof(z));
		z.next_in  = source;
		z.avail_in = comp_block_size;
		if (z.avail_in > header.block_size)
		{
			PanicAlert("We have a problem");
		}
		z.next_out  = dest;
		z.avail_out = header.block_size;
		inflateInit(&z);
		int status = inflate(&z, Z_FULL_FLUSH);
		u32 uncomp_size = header.block_size - z.avail_out;
		if (status != Z_STREAM_END)
		{
			// this seem to fire wrongly from time to time
			// to be sure, don't use compressed isos :P
			PanicAlert("Failure reading block %lli - out of data and not at end.", block_num);
		}
		inflateEnd(&z);
		if (uncomp_size != header.block_size)
			PanicAlert("Wrong block size");
	}
}
Ejemplo n.º 7
0
bool CompressFileToBlob(const char* infile, const char* outfile, u32 sub_type,
						int block_size, CompressCB callback, void* arg)
{
	bool scrubbing = false;

	if (IsCompressedBlob(infile))
	{
		PanicAlertT("%s is already compressed! Cannot compress it further.", infile);
		return false;
	}

	if (sub_type == 1)
	{
		if (!DiscScrubber::SetupScrub(infile, block_size))
		{
			PanicAlertT("%s failed to be scrubbed. Probably the image is corrupt.", infile);
			return false;
		}

		scrubbing = true;
	}

	File::IOFile inf(infile, "rb");
	File::IOFile f(outfile, "wb");

	if (!f || !inf)
		return false;

	callback("Files opened, ready to compress.", 0, arg);

	CompressedBlobHeader header;
	header.magic_cookie = kBlobCookie;
	header.sub_type   = sub_type;
	header.block_size = block_size;
	header.data_size  = File::GetSize(infile);

	// round upwards!
	header.num_blocks = (u32)((header.data_size + (block_size - 1)) / block_size);

	u64* offsets = new u64[header.num_blocks];
	u32* hashes = new u32[header.num_blocks];
	u8* out_buf = new u8[block_size];
	u8* in_buf = new u8[block_size];

	// seek past the header (we will write it at the end)
	f.Seek(sizeof(CompressedBlobHeader), SEEK_CUR);
	// seek past the offset and hash tables (we will write them at the end)
	f.Seek((sizeof(u64) + sizeof(u32)) * header.num_blocks, SEEK_CUR);

	// Now we are ready to write compressed data!
	u64 position = 0;
	int num_compressed = 0;
	int num_stored = 0;
	int progress_monitor = max<int>(1, header.num_blocks / 1000);

	for (u32 i = 0; i < header.num_blocks; i++)
	{
		if (i % progress_monitor == 0)
		{
			const u64 inpos = inf.Tell();
			int ratio = 0;
			if (inpos != 0)
				ratio = (int)(100 * position / inpos);
			char temp[512];
			sprintf(temp, "%i of %i blocks. Compression ratio %i%%", i, header.num_blocks, ratio);
			callback(temp, (float)i / (float)header.num_blocks, arg);
		}

		offsets[i] = position;
		// u64 start = i * header.block_size;
		// u64 size = header.block_size;
		std::fill(in_buf, in_buf + header.block_size, 0);
		if (scrubbing)
			DiscScrubber::GetNextBlock(inf.GetHandle(), in_buf);
		else
			inf.ReadBytes(in_buf, header.block_size);
		z_stream z;
		memset(&z, 0, sizeof(z));
		z.zalloc = Z_NULL;
		z.zfree  = Z_NULL;
		z.opaque = Z_NULL;
		z.next_in   = in_buf;
		z.avail_in  = header.block_size;
		z.next_out  = out_buf;
		z.avail_out = block_size;
		int retval = deflateInit(&z, 9);

		if (retval != Z_OK)
		{
			ERROR_LOG(DISCIO, "Deflate failed");
			goto cleanup;
		}

		int status = deflate(&z, Z_FINISH);
		int comp_size = block_size - z.avail_out;
		if ((status != Z_STREAM_END) || (z.avail_out < 10))
		{
			//PanicAlert("%i %i Store %i", i*block_size, position, comp_size);
			// let's store uncompressed
			offsets[i] |= 0x8000000000000000ULL;
			f.WriteBytes(in_buf, block_size);
			hashes[i] = HashAdler32(in_buf, block_size);
			position += block_size;
			num_stored++;
		}
		else
		{
			// let's store compressed
			//PanicAlert("Comp %i to %i", block_size, comp_size);
			f.WriteBytes(out_buf, comp_size);
			hashes[i] = HashAdler32(out_buf, comp_size);
			position += comp_size;
			num_compressed++;
		}

		deflateEnd(&z);
	}

	header.compressed_data_size = position;

	// Okay, go back and fill in headers
	f.Seek(0, SEEK_SET);
	f.WriteArray(&header, 1);
	f.WriteArray(offsets, header.num_blocks);
	f.WriteArray(hashes, header.num_blocks);

cleanup:
	// Cleanup
	delete[] in_buf;
	delete[] out_buf;
	delete[] offsets;
	delete[] hashes;

	DiscScrubber::Cleanup();
	callback("Done compressing disc image.", 1.0f, arg);
	return true;
}
Ejemplo n.º 8
0
bool PixelShaderCache::SetShader(PSGRENDER_MODE PSGRenderMode, u32 components)
{
	const API_TYPE api = ((D3D::GetCaps().PixelShaderVersion >> 8) & 0xFF) < 3 ? API_D3D9_SM20 : API_D3D9_SM30;
	PIXELSHADERUID uid;
	GetPixelShaderId(&uid, PSGRenderMode, components);

	// Check if the shader is already set
	if (last_entry)
	{
		if (uid == last_uid)
		{
			GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true);
			ValidatePixelShaderIDs(api, last_entry->safe_uid, last_entry->code, PSGRenderMode, components);
			return last_entry->shader != NULL;
		}
	}

	last_uid = uid;

	// Check if the shader is already in the cache
	PSCache::iterator iter;
	iter = PixelShaders.find(uid);
	if (iter != PixelShaders.end())
	{
		const PSCacheEntry &entry = iter->second;
		last_entry = &entry;

		if (entry.shader) D3D::SetPixelShader(entry.shader);
		GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true);
		ValidatePixelShaderIDs(api, entry.safe_uid, entry.code, PSGRenderMode, components);
		return (entry.shader != NULL);
	}


	// Need to compile a new shader
	const char *code = GeneratePixelShaderCode(PSGRenderMode, api, components);

	if (g_ActiveConfig.bEnableShaderDebugging)
	{
		u32 code_hash = HashAdler32((const u8 *)code, strlen(code));
		unique_shaders.insert(code_hash);
		SETSTAT(stats.numUniquePixelShaders, unique_shaders.size());
	}

#if defined(_DEBUG) || defined(DEBUGFAST)
	if (g_ActiveConfig.iLog & CONF_SAVESHADERS && code) {
		static int counter = 0;
		char szTemp[MAX_PATH];
		sprintf(szTemp, "%sps_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), counter++);

		SaveData(szTemp, code);
	}
#endif

	u8 *bytecode = 0;
	int bytecodelen = 0;
	if (!D3D::CompilePixelShader(code, (int)strlen(code), &bytecode, &bytecodelen)) {
		GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true);
		return false;
	}

	// Insert the bytecode into the caches
	g_ps_disk_cache.Append(uid, bytecode, bytecodelen);

	// And insert it into the shader cache.
	bool success = InsertByteCode(uid, bytecode, bytecodelen, true);
	delete [] bytecode;

	if (g_ActiveConfig.bEnableShaderDebugging && success)
	{
		PixelShaders[uid].code = code;
		GetSafePixelShaderId(&PixelShaders[uid].safe_uid, PSGRenderMode, components);
	}

	GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true);
	return success;
}