Jump to content

abdoumatrix

Member
  • Posts

    4
  • Joined

  • Last visited

Reputation

5 Neutral

Personal Information

  • Pronouns
    he/him

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. Tachyons don't support all PHY types and animation keyframe formats for example: 103.c3 if you try to import any animations current progress : import only my main goal was to import .c3 into blender to allow export into other formats that can be used into another engines (Unity) as apps like Biturn (.c3 extension) extract static models and my editing skills is limited
  2. C3 Blender Add-On A Blender add-on for importing and animating C3 model format files, with support for multiple PHY types and animation keyframe formats. Download Latest Release https://github.com/abdouthematrix/c3-blender-addon/releases/download/latest/c3-blender-addon-latest.zip Or browse all releases: https://github.com/abdouthematrix/c3-blender-addon/releases Features: - Import C3 Models - Import Texture - Import/Replace Animation - Import/Replace Mesh Parts Installation: 1. Download the latest release zip file from the link above 2. In Blender, go to `Edit > Preferences > Add-ons` 3. Click `Install` and select the downloaded zip file 4. Enable the add-on by checking the box next to "Import-Export: C3 Add-On" Note: Do not unzip the file - Blender will install it directly from the zip. Usage: # Importing a C3 Model: 1. Go to the top menu bar and click `C3 Add-On > Import .C3 Model` 2. Browse to your `.c3` file and select it 3. Options: - New Scene : Import into a new scene (enabled by default) - debugpy : Enable remote debugging (for development) 4. Click `Import .C3 Model` # Importing a Texture 1. Select a mesh object 2. Go to `C3 Add-On > Import Texture` 3. Select a texture file (DDS, TGA, PNG, or JPG) 4. The texture will be applied to the selected mesh # Importing/Replacing Animation 1. Select a mesh object that was previously imported from a C3 file 2. Go to `C3 Add-On > Import Animation` 3. Options: - Use Original File: Uses the stored file path from the original import (enabled by default) 4. Click `Import Animation` # Credits - Author: abdoumatrix - Development Assistance: Claude (Anthropic) - Inspired by: [C3-Operator] by Tachyon-S
  3. namespace Comet.Network.Security { using System; using System.Threading; /// <summary> /// TQ Digital Entertainment's in-house asymmetric counter-based XOR-cipher. Counters /// are separated by encryption direction to create cipher streams. This implementation /// implements both directions for encrypting and decrypting data on the server side. /// </summary> /// <remarks> /// This cipher algorithm does not provide effective security, and does not make use /// of any NP-hard calculations for encryption or key generation. Key derivations are /// susceptible to brute-force or static key attacks. Only implemented for /// interoperability with the pre-existing game client. Do not use, otherwise. /// </remarks> public sealed class TQCipher : ICipher { private const int KEY_SIZE = 0x100; // Local fields and properties private byte[] mCryptKey1; private byte[] mCryptKey2; private byte[] mCryptKey3; private byte[] mCryptKey4; private int mDecryptCounter; private int mEncryptCounter; private bool mExchanged; /// <summary> /// Instantiates a new instance of <see cref="TQCipher"/> with initial IV keys. /// </summary> public TQCipher() { this.mExchanged = false; this.mDecryptCounter = 0; this.mEncryptCounter = 0; CreateKeys(); } /// <summary> /// Generate the initialization vector keys using static seed values. /// </summary> private void CreateKeys() { this.mCryptKey1 = new byte[KEY_SIZE]; this.mCryptKey2 = new byte[KEY_SIZE]; byte iKey1 = 0x1F; byte iKey2 = 0x3F; for (int i = 0; i < KEY_SIZE; i++) { this.mCryptKey1[i] = iKey1; this.mCryptKey2[i] = iKey2; iKey1 = (byte)(((byte)(iKey1 << 5) + 0xFD) * iKey1 + 7); iKey2 = (byte)((iKey2 * 0x7A - 0x31) * iKey2 - 0x1B); } } /// <summary> /// Generates keys for the game server using the player's token and account ID /// as key derivation variables. Invoked after the first packet is received on /// the game server. /// </summary> /// <param name="seeds">Array of seeds for generating keys (expects Token and AccountID as uint)</param> public void GenerateKeys(object[] seeds) { uint token = Convert.ToUInt32(seeds[0]); uint accountId = Convert.ToUInt32(seeds[1]); this.mCryptKey3 = new byte[KEY_SIZE]; this.mCryptKey4 = new byte[KEY_SIZE]; int tmpkey1 = (int)((token + accountId) ^ 0x4321 ^ token); int tmpkey2 = tmpkey1 * tmpkey1; byte[] tmp1 = BitConverter.GetBytes(tmpkey1); byte[] tmp2 = BitConverter.GetBytes(tmpkey2); for (int i = 0; i < KEY_SIZE; i++) { this.mCryptKey3[i] = (byte)(this.mCryptKey1[i] ^ tmp1[i % 4]); this.mCryptKey4[i] = (byte)(this.mCryptKey2[i] ^ tmp2[i % 4]); } this.mExchanged = true; } /// <summary> /// Decrypts the specified span by XORing the source span with the cipher's /// keystream. The source and destination may be the same slice, but otherwise /// should not overlap. /// </summary> /// <param name="src">Source span that requires decrypting</param> /// <param name="dst">Destination span to contain the decrypted result</param> public void Decrypt(Span<byte> src, Span<byte> dst) { for (int i = 0; i < src.Length; i++) { byte key1Index = (byte)(this.mDecryptCounter & 0xFF); byte key2Index = (byte)(this.mDecryptCounter >> 8); if (this.mExchanged) { dst[i] = (byte)(src[i] ^ (this.mCryptKey4[key2Index] ^ this.mCryptKey3[key1Index])); } else { dst[i] = (byte)(src[i] ^ (this.mCryptKey2[key2Index] ^ this.mCryptKey1[key1Index])); } Interlocked.Increment(ref this.mDecryptCounter); } } /// <summary> /// Encrypt the specified span by XORing the source span with the cipher's /// keystream. The source and destination may be the same slice, but otherwise /// should not overlap. /// </summary> /// <param name="src">Source span that requires encrypting</param> /// <param name="dst">Destination span to contain the encrypted result</param> public void Encrypt(Span<byte> src, Span<byte> dst) { for (int i = 0; i < src.Length; i++) { byte key1Index = (byte)(this.mEncryptCounter & 0xFF); byte key2Index = (byte)(this.mEncryptCounter >> 8); dst[i] = (byte)(src[i] ^ (this.mCryptKey1[key1Index] ^ this.mCryptKey2[key2Index])); Interlocked.Increment(ref this.mEncryptCounter); } } /// <summary> /// Reset both encryption and decryption counters. /// </summary> public void ResetCounters() { this.mEncryptCounter = 0; this.mDecryptCounter = 0; } } } namespace Comet.Network.Security { using System; using Comet.Core.Mathematics; /// <summary> /// Rivest Cipher 5 is implemented for interoperability with the Conquer Online game /// client's login procedure. Passwords are encrypted in RC5 by the client, and decrypted /// on the server to be hashed and compared to the database saved password hash. In /// newer clients, this was replaced with SRP-6A (a hash based exchange protocol). /// </summary> /// <remarks> /// RC5 has a 32, 64 or 128-bit block size and a variable key length from /// 1 bit up to 2040 bits. It is a Feistel-like cipher using between 1 to /// 255 rounds. The suggested values are respectively 128, 64 and 12, although /// 12-round RC5 (with 64-bit blocks) is susceptible to a differential attack /// using 2^44 chosen plaintexts. To be still an effective cipher, 18-20 rounds /// are now suggested. /// </remarks> public sealed class RC5 : ICipher { // Constants and static properties private const int KEY_SIZE = 16; // The key size in bytes (0-255, 16 suggested) private const int BLOCK_SIZE = 8; // The block size in bytes (4, 8, or 16, 8 suggested) private const int ROUNDS = 12; // The number of rounds (1-255, 12 suggested) // Magic values private const uint RC5_PW32 = 0xB7E15163; private const uint RC5_QW32 = 0x61C88647; // Internal key sizes for uint32 arrays private const int RC5_SUB = (ROUNDS * 2) + 2; private const int RC5_KEY = KEY_SIZE / 4; // Local fields and properties private readonly uint[] mKey; private readonly uint[] mSub; /// <summary> /// Initializes a new instance of <see cref="RC5"/> to be interoperable with /// the Conquer Online game client. Automatically generates keys using the /// default seed. In later versions of the client, a random buffer is used /// to seed the cipher. /// </summary> public RC5() { this.mKey = new uint[RC5_KEY]; this.mSub = new uint[RC5_SUB]; // Zero-fill arrays for security purposes Array.Clear(this.mKey, 0, this.mKey.Length); Array.Clear(this.mSub, 0, this.mSub.Length); // Generate keys with default seed GenerateKeys(new object[] { new byte[] { 0x3C, 0xDC, 0xFE, 0xE8, 0xC4, 0x54, 0xD6, 0x7E, 0x16, 0xA6, 0xF8, 0x1A, 0xE8, 0xD0, 0x38, 0xBE } }); } /// <summary> /// Generates keys and the subkey words for RC5 using a shared seed, whether /// that seed is shared statically or shared using a method of transport. Though /// only one seed is expected to generate keys, multiple may be used. Seed must be /// divisible by the selected cipher word size (16 bytes in this implementation). /// </summary> /// <param name="seeds">An array of seeds used to generate keys</param> public void GenerateKeys(object[] seeds) { var aSeed = seeds[0] as byte[]; // Copy seed into key array for (int i = 0; i < RC5_KEY; i++) { this.mKey[i] = BitConverter.ToUInt32(aSeed, i * 4); } // Initialize sub-key array this.mSub[0] = RC5_PW32; for (int i = 1; i < RC5_SUB; i++) { this.mSub[i] = this.mSub[i - 1] - RC5_QW32; } // Generate key schedule uint x = 0, y = 0; int iIdx = 0, jIdx = 0; int iterations = 3 * Math.Max(RC5_KEY, RC5_SUB); for (int k = 0; k < iterations; k++) { this.mSub[iIdx] = (this.mSub[iIdx] + x + y).RotateLeft(3); x = this.mSub[iIdx]; iIdx = (iIdx + 1) % RC5_SUB; this.mKey[jIdx] = (this.mKey[jIdx] + x + y).RotateLeft((int)(x + y)); y = this.mKey[jIdx]; jIdx = (jIdx + 1) % RC5_KEY; } } /// <summary> /// Decrypts bytes from the client. The buffer length must be a multiple of /// the BLOCK_SIZE constant (8 bytes). The source and destination may be the /// same slice. /// </summary> /// <param name="src">Source span that requires decrypting</param> /// <param name="dst">Destination span to contain the decrypted result</param> public void Decrypt(Span<byte> src, Span<byte> dst) { int blockCount = src.Length / BLOCK_SIZE; for (int i = 0; i < blockCount; i++) { int offset = i * BLOCK_SIZE; uint ld = BitConverter.ToUInt32(src.Slice(offset, 4)); uint rd = BitConverter.ToUInt32(src.Slice(offset + 4, 4)); // Decrypt rounds for (int j = ROUNDS; j >= 1; j--) { rd = ((rd - this.mSub[2 * j + 1]).RotateRight((int)ld)) ^ ld; ld = ((ld - this.mSub[2 * j]).RotateRight((int)rd)) ^ rd; } uint a = rd - this.mSub[1]; uint b = ld - this.mSub[0]; BitConverter.GetBytes(b).CopyTo(dst.Slice(offset, 4)); BitConverter.GetBytes(a).CopyTo(dst.Slice(offset + 4, 4)); } } /// <summary> /// Encrypts bytes from the server. The buffer length must be a multiple of the /// BLOCK_SIZE constant (8 bytes). The source and destination may be the same slice. /// </summary> /// <param name="src">Source span that requires encrypting</param> /// <param name="dst">Destination span to contain the encrypted result</param> public void Encrypt(Span<byte> src, Span<byte> dst) { int blockCount = src.Length / BLOCK_SIZE; for (int i = 0; i < blockCount; i++) { int offset = i * BLOCK_SIZE; uint a = BitConverter.ToUInt32(src.Slice(offset, 4)); uint b = BitConverter.ToUInt32(src.Slice(offset + 4, 4)); uint le = a + this.mSub[0]; uint re = b + this.mSub[1]; // Encrypt rounds for (int j = 1; j < ROUNDS; j++) { le = ((le ^ re).RotateLeft((int)re)) + this.mSub[2 * j]; re = ((re ^ le).RotateLeft((int)le)) + this.mSub[2 * j + 1]; } BitConverter.GetBytes(le).CopyTo(dst.Slice(offset, 4)); BitConverter.GetBytes(re).CopyTo(dst.Slice(offset + 4, 4)); } } } } work with comet base
×
×
  • Create New...