/* * * * * * * * * * * * * * * * 
 * 	 ______     __  __     ______     __  __     ______     ______     __  __    
 *	/\  __ \   /\ \/\ \   /\  == \   /\ \_\ \   /\  __ \   /\  ___\   /\ \/ /    
 *	\ \ \/\ \  \ \ \_\ \  \ \  __<   \ \  __ \  \ \  __ \  \ \ \____  \ \  _"-.  
 *	 \ \_____\  \ \_____\  \ \_____\  \ \_\ \_\  \ \_\ \_\  \ \_____\  \ \_\ \_\ 
 *	  \/_____/   \/_____/   \/_____/   \/_/\/_/   \/_/\/_/   \/_____/   \/_/\/_/ 
 *
 *  - Version : 3L
 *  - By Oubliette
 * * * * * * * * * * * * * * * */

#include "oubhack_fileio.h"
#include "oubhack.h"

#include <iostream>
#include <fstream>
#include <time.h>
#include <stdio.h>

#include <algorithm>

/* ========================================
	Name : IFileIO::IFileIO
	-- Constructor --
=========================================== */

IFileIO::IFileIO(){

	this->CreateDirectories( "OubHack/configs" );
	this->CreateDirectories( "OubHack/scripts" );
	this->CreateDirectories( "OubHack/cache" );
	this->CreateDirectories( "OubHack/bin" );
	this->CreateDirectories( "OubHack/logs" );

}

/* ========================================
	Name : IFileIO::ReadFile
	Returns : std::string
=========================================== */

std::string IFileIO::ReadFile( const char* ccPath ){

	std::ifstream iFile(ccPath, std::ios::binary);
	std::string str = "";

	while (!iFile.eof()){

		str.append(1, iFile.get());

	}

	str = str.substr( 0, str.length() - 1 );

	return str;

}

/* ========================================
	Name : IFileIO::WriteFile
	Returns : void
=========================================== */

void IFileIO::WriteFile( const char* ccPath, const char* ccString, const char* ccMode ){
	
	FILE* pFile = fopen( ccPath, ccMode );

	if ( pFile != NULL ){

		fputs( ccString, pFile );
		fclose( pFile );

	}

}

/* ========================================
	Name : IFileIO::FileDump
	Returns : void
=========================================== */

void IFileIO::FileDump( const char* ccPath, const char* ccString ){

	// Take the filename of the end of the path
	std::string stdPath = "";
	const char* ccName = "";

	char * ccStr = strdup( ccPath );
	char * ccPch = strtok( ccStr, "/" );

	while ( ccPch != NULL ){

		char * tmp = strtok( NULL, "/" );

		if ( tmp == NULL ){
			ccName = tmp;
			break;
		}
		else {
			stdPath += "/";
			stdPath += ccPch;
			ccPch = tmp;
		}

	}

	// Create the stripped path
	this->CreateDirectories( stdPath.c_str() );
	
	// Write file

	ofstream sFile;

	time_t _t = time(0);
	struct tm * t = localtime( &_t );

	sFile.open( ccPath );

	sFile << "--[[\n";
	sFile << " = = = = = = = = = = = = = = = = = = = = = = = =\n";
	sFile << "\t == OubHack script dump ==\n";
	sFile << "\t * File : " << stdPath.c_str()		<< '\n';
	sFile << "\t * Size : " << strlen( ccString )	<< '\n';
	sFile << "\t * Date : " << t->tm_mday << "/" << t->tm_mon << "/" << t->tm_year << " - " << t->tm_hour << ":" << t->tm_min << ":" << t->tm_sec << '\n';
	sFile << "\t == OubHack script dump ==\n";
	sFile << " = = = = = = = = = = = = = = = = = = = = = = = =\n";
	sFile << "]]--\n\n";
	sFile << ccString;

	sFile.close();

}

/* ========================================
	Name : IFileIO::ReadDecryptedFile
	Returns : std::string
=========================================== */

std::string IFileIO::ReadDecryptedFile( const char* ccPath ){

	std::string sPath = ccPath;

	if ( this->FileExists( (std::string( "OubHack/" ) + std::string( ccPath )).c_str() ) ) sPath = (std::string( "OubHack/" ) + std::string( ccPath ));

	std::string sCacheText = oubhack->mapDataCache.find(std::string( sPath.c_str() )) != oubhack->mapDataCache.end() ? 
		oubhack->mapDataCache[std::string( sPath.c_str() )] : "";

	std::string decText = this->FileExists( sPath.c_str() ) ? this->ReadFile( sPath.c_str() ) : sCacheText;

	if (decText == ""){
		return "";
	}

	AES aes;

	byte bKey[ 16 ], bIV[ 16 ];

	if (this->FileExists( sPath.c_str() )){

		std::string sid = std::to_string( oubhack->m_llSteamID64 );

		CryptoPP::MD5 keyHash, ivHash;

		keyHash.CalculateDigest( bIV, (byte*) sid.c_str(), sid.length() );
		keyHash.CalculateDigest( bKey, (byte*) oubhack->m_sUsername.c_str(), oubhack->m_sUsername.length() );

	}
	else {

		memcpy( bKey, oubhack->m_bDecKey, 16 );
		memcpy( bIV, oubhack->m_bDecIV, 16 );

	}

	return aes.decrypt(decText, bKey, bIV);

}

/* ========================================
	Name : IFileIO::GetFiles
	Returns : std::vector<std::string>
=========================================== */

std::vector<std::string> IFileIO::GetFiles( const char* ccPath, int* iAmount ){

	return std::vector<std::string>();

}

/* ========================================
	Name : IFileIO::GetDirectories
	Returns : std::vector<std::string>
=========================================== */

std::vector<std::string> IFileIO::GetDirectories( const char* ccPath, int* iAmount ){

	return std::vector<std::string>();

}

/* ========================================
	Name : IFileIO::CreateDirectory
	Returns : void
=========================================== */

void IFileIO::CreateDirectories( const char* ccPath ){

	std::string stdCurPath = "";

	char * ccStr = strdup( ccPath );
	char * ccPch = strtok( ccStr, "/" );

	stdCurPath += ccPch;
	stdCurPath += "/";

	while ( ccPch != NULL ){

		CreateDirectory( stdCurPath.c_str(), NULL );

		char * tmp = strtok( NULL, "/" );

		if ( tmp == NULL ){
			break;
		}
		else {
			ccPch = tmp;
			stdCurPath += tmp;
			stdCurPath += "/";
		}

	}

}

/* ========================================
	Name : IFileIO::FileExists
	Returns : bool
=========================================== */

bool IFileIO::FileExists( const char* ccPath ){

	bool bRes;

	ifstream ifFile(ccPath);

	if ( ifFile )
		bRes = true;
	else
		bRes = false;

	ifFile.close();

	return bRes;

}

/* ========================================
	Name : IFileIO::DoMD5
	Returns : std::string
=========================================== */

std::string IFileIO::DoMD5( std::string sMessage ){

	CryptoPP::HexEncoder encoder;
	CryptoPP::MD5 hash;

	std::string output;

	byte digest[ CryptoPP::MD5::DIGESTSIZE ];

	hash.CalculateDigest( digest, (byte*) sMessage.c_str(), sMessage.length() );

	encoder.Attach( new CryptoPP::StringSink( output ) );
	encoder.Put( digest, sizeof(digest) );
	encoder.MessageEnd();

	std::transform(output.begin(), output.end(), output.begin(), ::tolower);


	return output;

}

/* ========================================
	Name : IFileIO::DoMD5
	Returns : std::string
=========================================== */

byte* IFileIO::DoMD5B( std::string sMessage ){

	CryptoPP::MD5 hash;

	byte digest[ CryptoPP::MD5::DIGESTSIZE ];

	hash.CalculateDigest( digest, (byte*) sMessage.c_str(), sMessage.length() );

	return digest;

}