/*League of Legends Custom Skin Tool-Kitby JahrainThis is a simple tool that can be used to compile and decompile .skn and .skl filesin order to create custom skin models for League of Legends*/#include#include #include #include using namespace std; struct Header { //Structure for header on skl files char fileType[8]; int numObjects; int skeletonHash; int numElements;}; struct Bone { //Structure for a bone in skl files char name[32]; int parent; float scale; float matrix[3][4];}; struct SkinModelHeader { //Structure for the Header in skn files int magic; short numMaterials; short numObjects;};struct SkinModelMaterial { //Structure for a material block in skn files int matIndex; char name[64]; int startVertex; int numVertices; int startIndex; int numIndices;};struct SkinModelVertex { //Vertex block in skn files float position[3]; char boneIndex[4]; float weights[4]; float normal[3]; float texcoords[2];};struct SkinModelData { //data block in skn files int numIndices; int numVertices; vector indices; vector verteces;}; struct SkinModel { //Skin model data structure SkinModelHeader header; vector materials; SkinModelData modelData; void exportObj(string fileName) { //exports this skin model as a .obj file ofstream fout(fileName.c_str()); for (int i = 0; i < modelData.verteces.size(); i++) { fout << "v " << modelData.verteces[i].position[0] << " " << modelData.verteces[i].position[1] << " " << modelData.verteces[i].position[2] << endl; fout << "vn " << modelData.verteces[i].normal[0] << " " << modelData.verteces[i].normal[1] << " " << modelData.verteces[i].normal[2] << endl; fout << "vt " << modelData.verteces[i].texcoords[0] << " " << 1-modelData.verteces[i].texcoords[1] << endl; } if (materials.size()) { fout << "g mat_" << materials[0].name << endl; } for (int i = 0; i < modelData.numIndices/3; i++) { int a = modelData.indices[i*3] + 1; int b = modelData.indices[i*3 + 1] + 1; int c = modelData.indices[i*3 + 2] + 1; fout << "f " << a << '/' << a << '/' << a << " " << b << '/' << b << '/' << b << " " << c << '/' << c << '/' << c << endl; } } void exportSkn(string fileName) { //export this SkinModel as .skn which can be used by league of legends ofstream fout; fout.open(fileName.c_str(), ios::binary); fout.write((char*)(void*)&header,sizeof(SkinModelHeader)); for (int i = 0; i < header.numMaterials; i++) { fout.write((char*)(void*)&materials[i], sizeof(SkinModelMaterial)); } fout.write((char*)(void*)&modelData.numIndices,4); fout.write((char*)(void*)&modelData.numVertices,4); for (int i = 0; i < modelData.numIndices; i++) { fout.write((char*)(void*)&modelData.indices[i], 2); } for (int i = 0; i < modelData.numVertices; i++) { fout.write((char*)(void*)&modelData.verteces[i],sizeof(SkinModelVertex)); } } void importSkn(string infile) { //import from .skn file ifstream fin; fin.open(infile.c_str(), ios::binary); if (fin.fail()) { cout << "ERROR: could not open " << infile << endl; exit(1); } fin.read((char*)(void*)&header,sizeof(SkinModelHeader)); for (int i = 0; i < header.numMaterials; i++) { SkinModelMaterial mat; fin.read((char*)(void*)&mat, sizeof(SkinModelMaterial)); materials.push_back(mat); } fin.read((char*)(void*)&modelData.numIndices,4); fin.read((char*)(void*)&modelData.numVertices,4); for (int i = 0; i < modelData.numIndices; i++) { short idx; fin.read((char*)(void*)&idx, 2); modelData.indices.push_back(idx); } for (int i = 0; i < modelData.numVertices; i++) { SkinModelVertex vtx; fin.read((char*)(void*)&vtx,sizeof(SkinModelVertex)); modelData.verteces.push_back(vtx); } } void importMesh(string infile) { //import from ASCII mesh output from Maya Script //cout << "Compiling skin file: " << infile << " to binary file: " << outfile << endl; ifstream fin(infile.c_str()); if (fin.fail()) { cout << "ERROR: could not open " << infile << endl; exit(1); } int numVerts; fin >> numVerts; modelData.numVertices = numVerts; for (int i = 0; i < numVerts; i++) { SkinModelVertex vtx; fin >> vtx.position[0] >> vtx.position[1] >> vtx.position[2]; for (int j = 0; j < 3; j++) { int boneIdx = 0; string boneIdxStr; fin >> boneIdxStr; if (boneIdxStr != "#NULL") { string tmp; for (int k = boneIdxStr.length() - 1; k >= 0; k--) { if (boneIdxStr[k] == '_') { boneIdxStr[k] = ' '; break; } } } char name[32]; sscanf(boneIdxStr.c_str(), "%s %d",name,&boneIdx); vtx.boneIndex[j] = boneIdx; } vtx.boneIndex[3] = 0; fin >> vtx.weights[0] >> vtx.weights[1] >> vtx.weights[2]; vtx.weights[3] = 0; //normalize weights float weightSum = vtx.weights[0] + vtx.weights[1] + vtx.weights[2]; vtx.weights[0] *= 1.0f/weightSum; vtx.weights[1] *= 1.0f/weightSum; vtx.weights[2] *= 1.0f/weightSum; fin >> vtx.normal[0] >> vtx.normal[1] >> vtx.normal[2]; fin >> vtx.texcoords[0] >> vtx.texcoords[1]; vtx.texcoords[1] = 1 - vtx.texcoords[1]; //flip the UVs modelData.verteces.push_back(vtx); } SkinModelMaterial mat; memset((void*)&mat,0,sizeof(SkinModelMaterial)); fin >> mat.name; int numFaces; fin >> numFaces; string nameStr = string(mat.name); if (nameStr != "lambert1") { cout << "NOTE: The material name is " << nameStr << ". Make sure this matches the same material as the original skin file!!!" << endl; header.numMaterials = 1; mat.matIndex = 1; mat.numIndices = numFaces*3; mat.numVertices = numVerts; mat.startIndex = 0; mat.startVertex = 0; materials.push_back(mat); } else { cout << "WARNING: The default material was used because this model used the default material lambert1. Only do this if the original .skn file has no materials." << endl; header.numMaterials = 0; } header.magic = 1122867; header.numObjects = 1; modelData.numIndices = numFaces*3; for (int i = 0; i < numFaces; i++) { for (int j = 0; j < 3; j++) { short face; fin >> face; modelData.indices.push_back(face); } } } }; void dumpSkeleton(string infile, string outfile) { //dump out the contents of a .skl file as ASCII for importing into Maya or some other 3d Application. ifstream fin; fin.open(infile.c_str(), ios::binary); if (fin.fail()) { cout << "ERROR: could not open " << infile << endl; exit(1); } ofstream fout(outfile.c_str()); Header header; fin.read((char*)(void*)&header, sizeof(header)); cout << "Found " << header.numElements << " bones" << endl; fout << header.fileType << endl; fout << header.numObjects << endl; fout << header.skeletonHash << endl; fout << header.numElements << endl; for (int i = 0; i < header.numElements; i++) { Bone bone; fin.read((char*)(void*)&bone, sizeof(Bone)); fout << bone.name << endl; fout << bone.parent << endl; fout << bone.scale << endl; for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { fout << bone.matrix[i][j] << " "; } fout << endl; } }} void compileSkeleton(string infile, string outfile) { //Compile the ASCII skeleton file back into a .skl file ifstream fin; fin.open(infile.c_str()); if (fin.fail()) { cout << "ERROR: could not open " << infile << endl; exit(1); } ofstream fout; fout.open(outfile.c_str(), ios::binary); Header header; fin >> header.fileType; fin >> header.numObjects; fin >> header.skeletonHash; fin >> header.numElements; fout.write((const char*)&header,sizeof(Header)); for (int i = 0; i < header.numElements; i++) { Bone bone; memset(bone.name,0,32); fin >> bone.name; fin >> bone.parent; fin >> bone.scale; for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { fin >> bone.matrix[i][j]; } } fout.write((const char*)&bone, sizeof(Bone)); }} int main(int argc, char *argv[]) { if (argc != 5) { cout << "Usage: lolskintool " << endl; cout << "Valid commands are: c (compile), d (decompile)" << endl; cout << "Supported types are: skl (skeleton), skn (skin)" << endl; return 2; } string cmd = string(argv[1]); string type = string(argv[2]); string inputFile = string(argv[3]); string outputFile = string(argv[4]); if (cmd == "compile" || cmd == "c") { if (type == "skl" || type == "skeleton") { cout << "Compiling ASCII skeleton file: " << inputFile << " to binary .skl file: " << outputFile << endl; compileSkeleton(inputFile, outputFile); cout << "Done!" << endl; } else if (type == "skn" || type == "skin") { SkinModel model; cout << "Compiling ASCII skin mesh file: " << inputFile << " to binary .skn file: " << outputFile << endl; model.importMesh(inputFile); model.exportSkn(outputFile); cout << "Done!" << endl; } else { cout << "Unsupported file type" << endl; return 2; } } else if (cmd == "decompile" || cmd == "d") { if (type == "skl" || type == "skeleton") { cout << "Decompiling binary .skl file: " << inputFile << " to ASCII file: " << outputFile << endl; dumpSkeleton(inputFile, outputFile); cout << "Done!" << endl; } else if (type == "skn" || type == "skin") { cout << "Decompiling binary .skn file: " << inputFile << " to .obj file: " << outputFile << endl; SkinModel model; model.importSkn(inputFile); model.exportObj(outputFile); cout << "Done!" << endl; } else { cout << "Unsupported file type" << endl; return 2; } } else { cout << "Unsupported command" << endl; return 2; } return 0;}