Skip to content

Instantly share code, notes, and snippets.

@MerijnHendriks
Created December 12, 2021 23:24
Show Gist options
  • Save MerijnHendriks/488d4104b50c0c0450bb9ee0d7830982 to your computer and use it in GitHub Desktop.
Save MerijnHendriks/488d4104b50c0c0450bb9ee0d7830982 to your computer and use it in GitHub Desktop.
Escape From Tarkov cache read utils

EFT-CacheUtils

Escape From Tarkov cache files utilities.

About

While the code does nothing on it's own, you can use it for:

  • Read / write cache files (<gamedir>/cache/)
  • Generate cache filename
  • Decode cache filename

Requirements

  • dotnet standard 2.0
  • ComponentAce Zlib.Net (link)

License

NCSA

Credits

  • Senko-san
  • Waffle.Lord
using System.Collections.Generic;
public class CacheDataProvider
{
public CacheDataProvider()
{
AddNormal();
AddLocales();
AddMaps();
}
private void AddNormal()
{
string[] urls = [
"https://prod.escapefromtarkov.com/client/items",
"https://prod.escapefromtarkov.com/client/customization",
"https://prod.escapefromtarkov.com/client/globals",
"https://trading.escapefromtarkov.com/client/trading/api/traderSettings",
"https://prod.escapefromtarkov.com/client/settings",
"https://prod.escapefromtarkov.com/client/locations"
];
foreach (string url in urls)
{
CacheUtil.AddUrl(url);
}
}
private void AddLocales()
{
string[] locales = [
"ch",
"cz".
"en",
"fr",
"ge",
"hu",
"it",
"jp",
"kr",
"pl",
"po",
"sk",
"es",
"es-mx",
"tu",
"ru"
];
foreach (string locale in locales)
{
CacheUtil.AddUrl($"https://prod.escapefromtarkov.com/client/menu/locale/{locale}");
CacheUtil.AddUrl($"https://prod.escapefromtarkov.com/client/locale/{locale}");
}
}
private void AddMaps()
{
// baselocalgame class
int variantMin = 1;
int variantMax = 6;
// location class
string[] maps = [
"bigmap",
"develop",
"factory4_day",
"factory4_night",
"hideout",
"Interchange",
"laboratory",
"Lighthouse",
"RezervBase",
"Shoreline",
"Woods"
];
foreach (string map in maps)
{
for (int i = variantMin; i < variantMax + 1; i++)
{
CacheUtil.AddUrl($"https://prod.escapefromtarkov.com/client/location/getLocalloot?locationId={map}&variantId={variant}");
}
}
}
}
using System.Collections.Generic;
using System.Text;
using Aki.Common;
public static class CacheUtil
{
private static Dictionary<string, string> cached;
static CacheUtil()
{
cached = new Dictionary<string, string>();
}
public static string GetUrl(string hash)
{
string result = null;
cached.TryGetValue(hash, out result);
return result;
}
public static void AddUrl(string url)
{
cached.Add(GetHash(url), url);
}
public static string Load(string filepath)
{
// read data
byte[] bytes = File.ReadAllBytes(filepath);
// undo xor
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] ^= 13;
}
// get decompressed utf8 text
string data = Zlib.Decompress(bytes);
return Encoding.UTF8.GetString(data);
}
public static void Save(string filepath, string text)
{
// get compressed utf8 text
byte[] data = Encoding.UTF8.GetBytes(text);
byte[] bytes = Zlib.Decompress(data);
// apply xor
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] ^= 13;
}
// write data
File.WriteAllBytes(filepath, bytes);
}
}
using System.Security.Cryptography;
using System.Text;
public static class HashUtil
{
private static HashAlgorithm md5;
static HashUtil()
{
md5 = MD5.Create();
}
public static string GetHash(string text)
{
byte[] bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(text));
StringBuilder sr = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
sr.Append(bytes[i].ToString("x2"));
}
return sr.ToString();
}
}
using System;
using System.IO;
using ComponentAce.Compression.Libs.zlib;
namespace Aki.Common
{
public enum ZlibCompression
{
Store = 0,
Fastest = 1,
Fast = 3,
Normal = 5,
Ultra = 7,
Maximum = 9
}
public static class Zlib
{
// Level | CM/CI FLG
// ----- | ---------
// 1 | 78 01
// 2 | 78 5E
// 3 | 78 5E
// 4 | 78 5E
// 5 | 78 5E
// 6 | 78 9C
// 7 | 78 DA
// 8 | 78 DA
// 9 | 78 DA
/// <summary>
/// Check if the file is ZLib compressed
/// </summary>
/// <param name="Data">Data</param>
/// <returns>If the file is Zlib compressed</returns>
public static bool IsCompressed(byte[] Data)
{
// We need the first two bytes;
// First byte: Info (CM/CINFO) Header, should always be 0x78
// Second byte: Flags (FLG) Header, should define our compression level.
if (Data == null || Data.Length < 3 || Data[0] != 0x78)
{
return false;
}
switch (Data[1])
{
case 0x01: // fastest
case 0x5E: // low
case 0x9C: // normal
case 0xDA: // max
return true;
}
return false;
}
/// <summary>
/// Deflate data.
/// </summary>
public static byte[] Compress(byte[] data, ZlibCompression level)
{
byte[] buffer = new byte[data.Length + 24];
ZStream zs = new ZStream()
{
avail_in = data.Length,
next_in = data,
next_in_index = 0,
avail_out = buffer.Length,
next_out = buffer,
next_out_index = 0
};
zs.deflateInit((int)level);
zs.deflate(zlibConst.Z_FINISH);
data = new byte[zs.next_out_index];
Array.Copy(zs.next_out, 0, data, 0, zs.next_out_index);
return data;
}
/// <summary>
/// Inflate data.
/// </summary>
public static byte[] Decompress(byte[] data)
{
byte[] buffer = new byte[4096];
ZStream zs = new ZStream()
{
avail_in = data.Length,
next_in = data,
next_in_index = 0,
avail_out = buffer.Length,
next_out = buffer,
next_out_index = 0
};
zs.inflateInit();
using (MemoryStream ms = new MemoryStream())
{
do
{
zs.avail_out = buffer.Length;
zs.next_out = buffer;
zs.next_out_index = 0;
int result = zs.inflate(0);
if (result != 0 && result != 1)
{
break;
}
ms.Write(zs.next_out, 0, zs.next_out_index);
}
while (zs.avail_in > 0 || zs.avail_out == 0);
return ms.ToArray();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment