Source: Metin2FileExtractor
-
Recientemente me adentre al mundo de c++ y otros como delphi hace rato se habia hecho publica la source con la que hicieron el descompilador de metin2 hoy la dejo aqui lista para ser aplicada no la he podido testear ya que ningún programa de programación en c me quiere servir...
Source code:
/*
Metin2FileExtractor
pushedx
edxLabsThis program serves as a file extractor for the Metin2 data files. The
EIX files are the header files and the EPK files are the data files.
The data can be uncompressed, encrypted, and/or compressed. This code
shows how the client performs the logic.This was a "for fun" project and done in about ~12 hours of work. I am
releasing this tool and the source code to the elitepvperss' Metin2
community to help spread new knowledge. I have no immediate plans for
a file editor at this time as I'm not playing the game or doing
anything with it. Maybe sometime later though.I hope you enjoy!
*/#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include//--------------------------------------------------------------------------
// Dumps a complete archive
bool DumpArchive(const char * inFolder, const char * name);//--------------------------------------------------------------------------
int main(int argc, char * argv[])
{
system("cls");
printf("Welcome to the Metin2 File Extractor!\n");
printf("This program was made by pushedx for the elitepvpers' Metin2 community.\n");
printf("This is a free tool for all to use.\n");
printf("It should work on the US/DE version files but Korea is not tested.\n");
printf("Enjoy :)\n\n");
if(argc != 3)
{
printf("Usage: Metin2FileExtractor \n");
printf("Examples:\n");
printf("\tMetin2FileExtractor \"C:\\Program Files\\Subagames\\Metin2\\pack\" BGM\n");
printf("\tMetin2FileExtractor \"C:\\Program Files\\Subagames\\Metin2\\pack\" PC\n");
printf("\tMetin2FileExtractor \"C:\\Program Files\\Metin2_Germany\\pack\" ETC\n");
printf("\tMetin2FileExtractor \"C:\\Program Files\\Metin2_Germany\\pack\" root\n");
system("pause");
return -1;
}
std::string safecheck = argv[2];
if(safecheck.find_first_of("!@#$%^&*()+={}[]|\\:\";\'<>?,./") != std::string::npos)
{
printf("Error: The Archive title (%s) contains invalid characters. The program will now exit.\n", argv[2]);
return -1;
}
printf("Beginning the archive dump. Please be patient while it finishes.\n\n");
bool result = DumpArchive(argv[1], argv[2]);
if(result == true)
{
printf("The archive dump was successful!\n");
}
else
{
printf("The archive dump was not successful.\n");
}
printf("Thank you for using the Metin2 File Extractor!\n");
system("pause");
return 0;
}//--------------------------------------------------------------------------
// The expected magic header value
#define LZ_KEY 0x5A4F434D//--------------------------------------------------------------------------
struct TEntry1
{
DWORD index;
char filename[160];
DWORD dw1;
DWORD dw2;
DWORD dw3;
DWORD dwSrcSize;
DWORD unpackedCRC;
DWORD dwFileOffset;
BYTE packedType;
BYTE b2;
BYTE b3;
BYTE b4;
};struct TEntry2
{
DWORD header;
DWORD decryptedBlockSize;
DWORD compressedBlockSize;
DWORD decompressedBlockSize;
};struct TEntry3
{
DWORD header;
DWORD version;
DWORD fileCount;
};//--------------------------------------------------------------------------
// For decompressing (ripped from client)
BYTE gLZOData[] =
{
0xB9, 0x9E, 0xB0, 0x02, 0x6F, 0x69, 0x81, 0x05,
0x63, 0x98, 0x9B, 0x28, 0x79, 0x18, 0x1A, 0x00,
};// For decrypting (ripped from client)
BYTE gLZOData2[] =
{
0x22, 0xB8, 0xB4, 0x04, 0x64, 0xB2, 0x6E, 0x1F,
0xAE, 0xEA, 0x18, 0x00, 0xA6, 0xF6, 0xFB, 0x1C,
};//--------------------------------------------------------------------------
// Utility decompress function
__declspec(naked) void ASM_LZO_FUNC1()
{
__asm
{
MOV EDX, DWORD PTR SS:[ESP + 0x08]
MOV ECX, DWORD PTR SS:[ESP + 0x04]
PUSH EBX
PUSH EBP
PUSH ESI
MOV ESI, DWORD PTR SS:[ESP + 0x18]
PUSH EDI
MOV EAX, 0xC6EF3720
MOV EDI, 0x20
LEA EBX, DWORD PTR DS:[EBX]
LABEL1:
MOV EBX, EDX
SHR EBX, 0x5
MOV EBP, EDX
SHL EBP, 0x4
XOR EBX, EBP
MOV EBP, EAX
SHR EBP, 0x0B
AND EBP, 0x03
MOV EBP, DWORD PTR DS:[ESI + EBP * 0x04]
ADD EBP, EAX
ADD EBX, EDX
XOR EBX, EBP
SUB ECX, EBX
MOV EBX, ECX
SHR EBX, 0x05
MOV EBP, ECX
SHL EBP, 0x04
XOR EBX, EBP
ADD EAX, 0x61C88647
MOV EBP, EAX
AND EBP, 0x03
MOV EBP, DWORD PTR DS:[ESI + EBP * 0x04]
ADD EBX, ECX
ADD EBP, EAX
XOR EBX, EBP
SUB EDX, EBX
DEC EDI
JNZ LABEL1
MOV EAX, DWORD PTR SS:[ESP + 0x20]
POP EDI
POP ESI
POP EBP
MOV DWORD PTR DS:[EAX], EDX
MOV DWORD PTR DS:[EAX + 0x04], ECX
POP EBX
RETN
}
}//--------------------------------------------------------------------------
// Decompress function in the client
__declspec(naked) void ASM_LZO_CHECKKEY()
{
__asm
{
MOV EAX,DWORD PTR SS:[ESP + 0x10]
MOV ECX, EAX
AND ECX, 0x80000007
JNG LABEL1
DEC ECX
OR ECX, 0xFFFFFFF8
INC ECX
LABEL1:
JE LABEL2
SUB EAX, ECX
ADD EAX, 8
MOV DWORD PTR SS:[ESP + 0x10],EAX
JMP LABEL3;
LABEL2:
MOV DWORD PTR SS:[ESP + 0x10],EAX
LABEL3:
PUSH EBX
MOV EBX, EAX
SAR EBX, 0x03
TEST EBX, EBX
JLE LABEL5PUSH EBP
//MOV EBP, lzoData
MOV EBP, [ESP + 0x14]PUSH ESI
//MOV ESI, inData
MOV ESI, [ESP + 0x14]PUSH EDI
//MOV EDI, outBuffer
MOV EDI, [ESP + 0x14]
LABEL4:
MOV EAX,DWORD PTR DS:[ESI]
MOV ECX,[ESI + 0x04]
PUSH EDI
PUSH EBP
PUSH EAX
PUSH ECX
CALL ASM_LZO_FUNC1
ADD ESP, 0x10
ADD EDI, 0x08
ADD ESI, 0x08
DEC EBX
JNZ LABEL4
MOV EAX,DWORD PTR SS:[ESP + 0x20]
POP EDI
POP ESI
POP EBP
LABEL5:
POP EBX
RET
}
}//--------------------------------------------------------------------------
// Wrapper function to decompress data
int LZObject_CheckKey(LPBYTE outBuffer, LPBYTE inData, LPBYTE lzoData, DWORD dwSize)
{
int result = 1;
__asm
{
mov edx, dwSizemov ecx, inData
sub ecx, 4mov eax, lzoData
mov edi, outBuffer
push edx
push eax
push ecx
push edicall ASM_LZO_CHECKKEY
MOV EDX, DWORD PTR DS:[EDI]
MOV EAX, LZ_KEY
ADD ESP, 0x10
CMP EDX, EAX
JE LABEL1
mov result, 0
LABEL1:
NOP
}
return result;
}//--------------------------------------------------------------------------
// Ripped from the client via OllyDbg. It was tedious, but simple work since
// you can set labels in OllyDbg for the new jump locations.
__declspec(naked) void ASM_LZO_DECOMPRESS()
{
__asm
{
MOV EAX,DWORD PTR SS:[ESP+0x08]
PUSH EBX
MOV EBX,DWORD PTR SS:[ESP+0x14]
PUSH EBP
PUSH ESI
MOV ESI,DWORD PTR SS:[ESP+0x10]
MOV DWORD PTR DS:[EBX],0x00
PUSH EDI
MOV CL,BYTE PTR DS:[ESI]
LEA EBP,DWORD PTR DS:[ESI+EAX]
MOV EAX,DWORD PTR SS:[ESP+0x1C]
CMP CL,0x11
JBE label1
AND ECX,0xFF
SUB ECX,0x11
INC ESI
CMP ECX,0x04
JB label2
label3:
MOV DL,BYTE PTR DS:[ESI]
MOV BYTE PTR DS:[EAX],DL
INC EAX
INC ESI
DEC ECX
JNZ label3
JMP label4
label1:
XOR ECX,ECX
MOV CL,BYTE PTR DS:[ESI]
INC ESI
CMP ECX,0x10
JNB label5
TEST ECX,ECX
JNZ label6
CMP BYTE PTR DS:[ESI],0x00
JNZ label7
label8:
MOV DL,BYTE PTR DS:[ESI+0x01]
ADD ECX,0xFF
INC ESI
TEST DL,DL
JE label8
label7:
XOR EDX,EDX
MOV DL,BYTE PTR DS:[ESI]
INC ESI
LEA ECX,DWORD PTR DS:[ECX+EDX+0x0F]
label6:
MOV EDX,DWORD PTR DS:[ESI]
ADD ESI,0x04
MOV DWORD PTR DS:[EAX],EDX
ADD EAX,0x04
DEC ECX // Switch (cases 1..4)
JE label4
CMP ECX,0x04
JB label9
label10:
MOV EDX,DWORD PTR DS:[ESI] // Default case of switch 0055BACA
SUB ECX,0x04
MOV DWORD PTR DS:[EAX],EDX
ADD EAX,0x04
ADD ESI,0x04
CMP ECX,0x04
JNB label10
TEST ECX,ECX
JBE label4
label11:
MOV DL,BYTE PTR DS:[ESI]
MOV BYTE PTR DS:[EAX],DL
INC EAX
INC ESI
DEC ECX
JNZ label11
JMP label4
label9:
MOV DL,BYTE PTR DS:[ESI] // Cases 2,3,4 of switch 0055BACA
MOV BYTE PTR DS:[EAX],DL
INC EAX
INC ESI
DEC ECX
JNZ label9
label4:
XOR ECX,ECX // Case 1 of switch 0055BACA
MOV CL,BYTE PTR DS:[ESI]
INC ESI
CMP ECX,0x10
JNB label5
SHR ECX,0x02
MOV EDX,EAX
SUB EDX,ECX
XOR ECX,ECX
MOV CL,BYTE PTR DS:[ESI]
SHL ECX,0x02
SUB EDX,ECX
MOV CL,BYTE PTR DS:[EDX-0x801]
SUB EDX,0x0801
INC ESI
MOV BYTE PTR DS:[EAX],CL
INC EAX
INC EDX
lable28:
MOV CL,BYTE PTR DS:[EDX]
MOV BYTE PTR DS:[EAX],CL
MOV DL,BYTE PTR DS:[EDX+0x01]
INC EAX
MOV BYTE PTR DS:[EAX],DL
INC EAX
label14:
MOV CL,BYTE PTR DS:[ESI-0x02]
AND ECX,0x03
JE label1
label2:
MOV DL,BYTE PTR DS:[ESI]
MOV BYTE PTR DS:[EAX],DL
INC EAX
INC ESI
DEC ECX
JNZ label2
XOR ECX,ECX
MOV CL,BYTE PTR DS:[ESI]
INC ESI
label5:
CMP ECX,0x40 // Switch (cases 0..3F)
JB label12
MOV EDX,ECX // Default case of switch label5
MOV EDI,EAX
SHR EDX,0x02
AND EDX,0x07
SUB EDI,EDX
XOR EDX,EDX
MOV DL,BYTE PTR DS:[ESI]
SHL EDX,0x03
SUB EDI,EDX
DEC EDI
INC ESI
SHR ECX,0x05
DEC ECX
label25:
MOV DL,BYTE PTR DS:[EDI]
MOV BYTE PTR DS:[EAX],DL
MOV DL,BYTE PTR DS:[EDI+0x01]
INC EAX
INC EDI
MOV BYTE PTR DS:[EAX],DL
INC EAX
INC EDI
label13:
MOV DL,BYTE PTR DS:[EDI]
MOV BYTE PTR DS:[EAX],DL
INC EAX
INC EDI
DEC ECX
JNZ label13
JMP label14
label12:
CMP ECX,0x20
JB label15
AND ECX,0x1F // Cases 20,21,22,23,24,25,26,27,28,29,2A,2B,2C,2D,2E,2F,30,31,32,33,34,35,36,37,38,39,3A,3B,3C,3D,3E,3F of switch label5
JNZ label16
CMP BYTE PTR DS:[ESI],0
JNZ label17
label18:
MOV DL,BYTE PTR DS:[ESI+0x01]
ADD ECX,0xFF
INC ESI
TEST DL,DL
JE label18
label17:
XOR EDX,EDX
MOV DL,BYTE PTR DS:[ESI]
INC ESI
LEA ECX,DWORD PTR DS:[ECX+EDX+0x1F]
label16:
XOR EDX,EDX
MOV EDI,EAX
MOV DX,WORD PTR DS:[ESI]
SHR EDX,0x02
SUB EDI,EDX
DEC EDI
ADD ESI,0x02
JMP label19
label15:
CMP ECX,0x10
JB label20
MOV EDX,ECX // Cases 10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F of switch label5
MOV EDI,EAX
AND EDX,0x08
SHL EDX,0x0B
SUB EDI,EDX
AND ECX,0x07
JNZ label21
CMP BYTE PTR DS:[ESI],0x00
JNZ label22
label23:
MOV DL,BYTE PTR DS:[ESI+0x01]
ADD ECX,0xFF
INC ESI
TEST DL,DL
JE label23
label22:
XOR EDX,EDX
MOV DL,BYTE PTR DS:[ESI]
INC ESI
LEA ECX,DWORD PTR DS:[ECX+EDX+0x07]
label21:
XOR EDX,EDX
MOV DX,WORD PTR DS:[ESI]
ADD ESI,0x02
SHR EDX,0x02
SUB EDI,EDX
CMP EDI,EAX
JE label24
SUB EDI,0x4000
label19:
CMP ECX,0x06
JB label25
MOV EDX,EAX
SUB EDX,EDI
CMP EDX,0x04
JL label25
MOV EDX,DWORD PTR DS:[EDI]
ADD EDI,0x04
MOV DWORD PTR DS:[EAX],EDX
ADD EAX,0x04
SUB ECX,0x02
label26:
MOV EDX,DWORD PTR DS:[EDI]
SUB ECX,0x04
MOV DWORD PTR DS:[EAX],EDX
ADD EAX,0x04
ADD EDI,0x04
CMP ECX,0x04
JNB label26
TEST ECX,ECX
JBE label14
label27:
MOV DL,BYTE PTR DS:[EDI]
MOV BYTE PTR DS:[EAX],DL
INC EAX
INC EDI
DEC ECX
JNZ label27
JMP label14
label20:
SHR ECX,0x02 // Cases 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F of switch label5
MOV EDX,EAX
SUB EDX,ECX
XOR ECX,ECX
MOV CL,BYTE PTR DS:[ESI]
SHL ECX,0x02
SUB EDX,ECX
DEC EDX
INC ESI
JMP lable28
label24:
MOV ECX,DWORD PTR SS:[ESP+0x1C]
SUB EAX,ECX
CMP ESI,EBP
MOV DWORD PTR DS:[EBX],EAX
JNZ label29
POP EDI
POP ESI
POP EBP
XOR EAX,EAX
POP EBX
RETN
label29:
SBB EAX,EAX
POP EDI
AND AL,0xFC
POP ESI
POP EBP
ADD EAX, -4
POP EBX
RETN
}
}//--------------------------------------------------------------------------
// Decompress wrapper function
void LZObject_Decompress(LPBYTE src, DWORD srcLen, LPBYTE dst, DWORD * ptrNewLen, void * workMemory)
{
__asm
{
MOV EDI, src
MOV EAX, dst
push workMemory
MOV EDX, ptrNewLen
push EDX
MOV EDX, srcLen
PUSH EAX
PUSH EDX
ADD EDI, 4
PUSH EDI
call ASM_LZO_DECOMPRESS
ADD ESP, 0x14
TEST EAX, EAX
JE LABEL1
INT 3 // Error, don't continue
LABEL1:
NOP
}
}//--------------------------------------------------------------------------
// Tokenizes a string into a vector
std::vector TokenizeString(const std::string& str, const std::string& delim)
{
// [http://www.gamedev.net/community/forums/topic.asp?topic_id=381544#TokenizeString][0]">[http://www.gamedev.net/community/forums][1] ... nizeString
using namespace std;
vector tokens;
size_t p0 = 0, p1 = string::npos;
while(p0 != string::npos)
{
p1 = str.find_first_of(delim, p0);
if(p1 != p0)
{
string token = str.substr(p0, p1 - p0);
tokens.push_back(token);
}
p0 = str.find_first_not_of(delim, p1);
}
return tokens;
}//--------------------------------------------------------------------------
// Saves the file to a specific folder based on the path. The root
// directory I choose to use is named 'output'.
void SaveFile(const char * originalFilename, LPBYTE outBuffer, DWORD outBufferSize)
{
std::stringstream dirPath;
std::vector pathTokens = TokenizeString(originalFilename, "\\/");dirPath << "output";
CreateDirectoryA(dirPath.str().c_str(), NULL);
dirPath << "\\";size_t index = 0;
for(index = 0; index < pathTokens.size() - 1; ++index)
{
if(pathTokens[index].find_first_of(":") != std::string::npos)
continue;
dirPath << pathTokens[index];
CreateDirectoryA(dirPath.str().c_str(), NULL);
dirPath << "\\";
}dirPath << pathTokens[index];
FILE * of = fopen(dirPath.str().c_str(), "wb");
if(of)
{
fwrite(outBuffer, 1, outBufferSize, of);
fclose(of);
}
else
{
printf("Could not save the file %s\n", dirPath.str().c_str());
}
}//--------------------------------------------------------------------------
// Dumps a complete archive. I have combined two sets of logic for this,
// but you can separate them if you want a more unique tool that allows
// you to extract individual files or explore the contents.
bool DumpArchive(const char * inFolder, const char * name)
{
HANDLE eixHandle = INVALID_HANDLE_VALUE;
HANDLE epkHandle = INVALID_HANDLE_VALUE;
SECURITY_ATTRIBUTES eixSecurity = {0};
SECURITY_ATTRIBUTES epkSecurity = {0};
DWORD eixFileSize = 0;
DWORD epkFileSize = 0;
HANDLE eixFileMapping = NULL;
HANDLE epkFileMapping = NULL;
LPBYTE eixFileBufferPtr = NULL;
LPBYTE epkFileBufferPtr = NULL;
bool bWasError = false;std::string eixName_ = inFolder;
if(!(eixName_[eixName_.size() - 1] == '\\' || eixName_[eixName_.size() - 1] == '/'))
{
eixName_ += "\\";
}
eixName_ += name;
eixName_ += ".eix";std::string epkName_ = inFolder;
if(!(epkName_[epkName_.size() - 1] == '\\' || epkName_[epkName_.size() - 1] == '/'))
{
epkName_ += "\\";
}
epkName_ += name;
epkName_ += ".epk";const char * eixName = eixName_.c_str();
const char * epkName = epkName_.c_str();eixSecurity.nLength = sizeof(SECURITY_ATTRIBUTES);
eixSecurity.bInheritHandle = TRUE;
eixSecurity.lpSecurityDescriptor = NULL;epkSecurity.nLength = sizeof(SECURITY_ATTRIBUTES);
epkSecurity.bInheritHandle = TRUE;
epkSecurity.lpSecurityDescriptor = NULL;// Open the files to access
while(bWasError == false)
{
eixHandle = CreateFileA(eixName, GENERIC_READ, FILE_SHARE_READ, &eixSecurity, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(eixHandle == INVALID_HANDLE_VALUE)
{
printf("Could not open the %s file. The program will now exit.\n", eixName);
bWasError = true;
break;
}epkHandle = CreateFileA(epkName, GENERIC_READ, FILE_SHARE_READ, &epkSecurity, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(epkHandle == INVALID_HANDLE_VALUE)
{
printf("Could not open the %s file. The program will now exit.\n", epkName);
bWasError = true;
break;
}
break; // All done now
}// Create the access handles for reading the files
while(bWasError == false)
{
// We need to store the size of the file for file mapping
eixFileSize = GetFileSize(eixHandle, NULL);
if(eixFileSize == INVALID_FILE_SIZE)
{
DWORD dwError = GetLastError();
if(dwError != NO_ERROR)
{
printf("There was an error [%i] getting the file size of the %s file. The program will now exit.\n", dwError, eixName);
bWasError = true;
break;
}
}// We need to store the size of the file for file mapping
epkFileSize = GetFileSize(epkHandle, NULL);
if(epkFileSize == INVALID_FILE_SIZE)
{
DWORD dwError = GetLastError();
if(dwError != NO_ERROR)
{
printf("There was an error [%i] getting the file size of the %s file. The program will now exit.\n", dwError, epkName);
bWasError = true;
break;
}
}// Create a file mapping object
eixFileMapping = CreateFileMapping(eixHandle, NULL, PAGE_READONLY, 0, eixFileSize, NULL);
if(eixFileMapping == 0)
{
printf("Could not create a file mapping object for the %s file. The program will now exit.\n", eixName);
bWasError = true;
break;
}// Create a file mapping object
epkFileMapping = CreateFileMapping(epkHandle, NULL, PAGE_READONLY, 0, epkFileSize, NULL);
if(epkFileMapping == 0)
{
printf("Could not create a file mapping object for the %s file. The program will now exit.\n", epkName);
bWasError = true;
break;
}// Create a file mapping view
eixFileBufferPtr = reinterpret_cast(MapViewOfFile(eixFileMapping, FILE_MAP_READ, 0, 0, eixFileSize));
if(eixFileBufferPtr == 0)
{
printf("Could not create a view of the the %s file. The program will now exit.\n", eixName);
bWasError = true;
break;
}// Create a file mapping view
epkFileBufferPtr = reinterpret_cast(MapViewOfFile(epkFileMapping, FILE_MAP_READ, 0, 0, epkFileSize));
if(epkFileBufferPtr == 0)
{
printf("Could not create a view of the the %s file. The program will now exit.\n", epkName);
bWasError = true;
break;
}break; // All done now
}// Now we need to verify the files we just loaded
while(bWasError == false)
{
// We need at least 12 bytes
if(eixFileSize < 0x0C)
{
printf("The file size for the %s file is too small. The program will now exit.\n", eixName);
bWasError = true;
break;
}// Verify the header
LPDWORD eixHeader = reinterpret_cast(eixFileBufferPtr);
if(*eixHeader != 0x444B5045) // Some hard coded check
{
// Important: This value is read from the client itself. If the client updates, this value
// would need to be updated as well if it ever changed.
if(*eixHeader != LZ_KEY)
{
printf("The file header for the %s file is incorrect. The program will now exit.\n", eixName);
bWasError = true;
break;
}// Get a file header pointer from the buffer
TEntry2 * eixHeader = (TEntry2 *)eixFileBufferPtr;// Store a pointer to the encrypted data
LPBYTE eixDataBuffer = eixFileBufferPtr + 0x14;// We don't care about this check because we will create the
// buffers ourselves in dynamic memory. The game wants to be as
// efficient as possible though.
if(eixHeader->decompressedBlockSize[0]: <a href=
[1]: http://www.gamedev.net/community/forums -
puf pero está en ensablador, lo interesante del programa al menos.
Ya vi ese código en epvp si no recuerdo mal, cuando intentaba hacer un comp/descomp en java para usarlo en mi programa.Prefiero usar un programa de un tercero antes que descifrar ese puto código infernal _xD_
PD: usa el bbcode "codigo" en vez del bbcode "code", queda más bonito
-
@Mrs.Troll said:
Recientemente me adentre al mundo de c# y otros como delphi [...]
LOL pero sabes que ese código no es C# sino C++, ¿no? O más bien, como dijo salva la parte interesante está en ensamblador, que a ver quien tiene huevos a descifrarlo xD. Aunque por cierto, me parece raro que el que creó el programa use clases y funciones de C++ propiamente dicho (como vector, bool y string) mezcladas con funciones importadas de C (como printf, en lugar de cout que es lo que se usa en C++, o fprintf, en lugar de ostream).