//this file is part of NeoMule
//Copyright (C)2006 David Xanatos ( Xanatos@Lycos.at / http://neomule.sourceforge.net )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#include "stdafx.h"
#ifdef _DEBUG
#include "DebugHelpers.h"
#endif

#include <io.h>
#include "FilePreferences.h"
#include "Preferences.h"
#include "PartFile.h"
#include "SafeFile.h"
#include "Packets.h"
#include "emule.h"
#include "Log.h"
#include "emuledlg.h"
#include "Sion/Defaults.h"
#include "Sion/Functions.h"
#include "Sion/SionOpCodes.h"
#include "KnownFileList.h"
#include "Sion/EMBackup.h" // MOD: NB - [NeoBackup]

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// MOD: FCFG - [FileConfiguration] -- Loulach -->

#define KNOWNPREFS_MET_FILENAME	_T("KnownPrefs.met")

bool CPartFile::SaveSionFile()
{
	CString strSionFile(m_fullname);
	strSionFile += PARTSION_EXT;

	CString strTmpFile(strSionFile);
	strTmpFile += PARTSION_TMP_EXT;

	// save tweak to part.sion file
	CSafeBufferedFile file;
	CFileException fexp;
	if (!file.Open(strTmpFile, CFile::modeWrite|CFile::modeCreate|CFile::typeBinary|CFile::shareDenyWrite, &fexp)){
		CString strError;
		strError.Format(GetResString(IDS_X_ERR_SAVENEO), strSionFile, GetFileName());
		TCHAR szError[MAX_CFEXP_ERRORMSG];
		if (fexp.GetErrorMessage(szError, ARRSIZE(szError))){
			strError += _T(" - ");
			strError += szError;
		}
		ModLogError(LOG_STATUSBAR, _T("%s"), strError);
		return false;
	}
	setvbuf(file.m_pStream, NULL, _IOFBF, 16384);

	try{
		//version
		file.WriteUInt8(NEOFILE_VERSION);


		// Save All Sion File settings here

		PartPrefs.SavePartPrefs(&file);
		KnownPrefs.SaveKnownPrefs(&file);

		// MOD: NPT - [NeoPartTraffic]
		if(statistic.HasTraffic() /* && thePrefs.UsePartTraffic()*/){
			file.WriteUInt8(TRAFFICFILE_VERSION);
			statistic.SaveTraffic(&file);
		}else
			file.WriteUInt8(NULL);
		// MOD: NPT END

		// MOD: XC - [ExtendedComments]
		if(HasComments() /* && thePrefs.UseSaveComments()*/){
			file.WriteUInt8(COMMENTSFILE_VERSION);
			SaveComments(&file);
		}else
			file.WriteUInt8(NULL);
		// MOD: XC END

		if (thePrefs.GetCommitFiles() >= 2 || (thePrefs.GetCommitFiles() >= 1 && !theApp.emuledlg->IsRunning())){
			file.Flush(); // flush file stream buffers to disk buffers
			if (_commit(_fileno(file.m_pStream)) != 0) // commit disk buffers to disk
				AfxThrowFileException(CFileException::hardIO, GetLastError(), file.GetFileName());
		}
		file.Close();
	}
	catch(CFileException* error){
		CString strError;
		strError.Format(GetResString(IDS_X_ERR_SAVENEO), strSionFile, GetFileName());
		TCHAR szError[MAX_CFEXP_ERRORMSG];
		if (error->GetErrorMessage(szError, ARRSIZE(szError))){
			strError += _T(" - ");
			strError += szError;
		}
		ModLogError(LOG_STATUSBAR, _T("%s"), strError);
		error->Delete();

		// remove the partially written or otherwise damaged temporary file
		file.Abort(); // need to close the file before removing it. call 'Abort' instead of 'Close', just to avoid an ASSERT.
		(void)_tremove(strTmpFile);
		return false;
	}

	// after successfully writing the temporary part.Sion file...
	if (_tremove(strSionFile) != 0 && errno != ENOENT){
		if (thePrefs.GetVerbose())
			DebugLogError(_T("Failed to remove \"%s\" - %s"), strSionFile, strerror(errno));
	}

	if (_trename(strTmpFile, strSionFile) != 0){
		int iErrno = errno;
		if (thePrefs.GetVerbose())
			DebugLogError(_T("Failed to move temporary part.met.Sion file \"%s\" to \"%s\" - %s"), strTmpFile, strSionFile, strerror(iErrno));

		CString strError;
		strError.Format(GetResString(IDS_X_ERR_SAVENEO), strSionFile, GetFileName());
		strError += _T(" - ");
		strError += strerror(iErrno);
		ModLogError(_T("%s"), strError);
		return false;
	}

	// create a backup of the successfully written part.met file
	CString BAKName(strSionFile);
	BAKName.Append(PARTSION_BAK_EXT);
	if (!::CopyFile(strSionFile, BAKName, FALSE)){
		if (thePrefs.GetVerbose())
			DebugLogError(_T("Failed to create backup of %s (%s) - %s"), strSionFile, GetFileName(), GetErrorMessage(GetLastError()));
	}

	return true;
}

bool CPartFile::LoadSionFile()
{
	CString strSionFile(m_fullname);
	strSionFile += PARTSION_EXT;

	uint8 version;
	
	// readfile tweaks form part.sion file
	CSafeBufferedFile file;
	CFileException fexpMet;
	if (!file.Open(strSionFile, CFile::modeRead|CFile::osSequentialScan|CFile::typeBinary|CFile::shareDenyWrite, &fexpMet)){
		if (fexpMet.m_cause != CFileException::fileNotFound){
			CString strError;
			strError.Format(GetResString(IDS_X_ERR_OPENNEO), strSionFile, _T(""));
			TCHAR szError[MAX_CFEXP_ERRORMSG];
			if (fexpMet.GetErrorMessage(szError, ARRSIZE(szError))){
				strError += _T(" - ");
				strError += szError;
			}
			ModLogError(LOG_STATUSBAR, _T("%s"), strError);
			theApp.BackupEngine->SetLastErrorState(EMB_ERROR_STATE_UNRECOVERABLE); // MOD: NB - [NeoBackup]
			return false;
		}
		theApp.BackupEngine->SetLastErrorState(EMB_ERROR_STATE_RECOVERABLE); // MOD: NB - [NeoBackup]
		return false;
	}
	setvbuf(file.m_pStream, NULL, _IOFBF, 16384);

	try{
		version = file.ReadUInt8();
		
		if (version > NEOFILE_VERSION || version <= NEOFILE_VERSION_OLD){
			ModLogError(LOG_STATUSBAR, GetResString(IDS_X_ERR_BADNEOVERSION), strSionFile, GetFileName());
			file.Close();
			theApp.BackupEngine->SetLastErrorState(EMB_ERROR_STATE_RECOVERABLE); // MOD: NB - [NeoBackup]
			return false;
		}
		

		//Load All Sion File settings here

		PartPrefs.LoadPartPrefs(&file);
		KnownPrefs.LoadKnownPrefs(&file);

		// MOD: NPT - [NeoPartTraffic]
		uint8 TrafficVersion = file.ReadUInt8();
		if(TrafficVersion){
			if (TrafficVersion < TRAFFICFILE_VERSION_OLD || TrafficVersion > TRAFFICFILE_VERSION){
				ModLogError(LOG_STATUSBAR, GetResString(IDS_X_ERR_TRAFFICMET_NEO_UNKNOWN_VERSION), strSionFile, GetFileName());
				file.Close();
				theApp.BackupEngine->SetLastErrorState(EMB_ERROR_STATE_RECOVERABLE); // MOD: NB - [NeoBackup]
				return false;
			}
			if(version < TRAFFICFILE_VERSION_400) // X! backwards compatybility
				statistic.LoadOldTraffic(&file);
			else
				statistic.LoadTraffic(&file);
		}
		// MOD: NPT END

		// MOD: XC - [ExtendedComments]
		uint8 CommentsVersion = file.ReadUInt8();
		if(CommentsVersion){
			if (CommentsVersion < COMMENTSFILE_VERSION_OLD || CommentsVersion > COMMENTSFILE_VERSION){
				ModLogError(LOG_STATUSBAR, GetResString(IDS_X_ERR_COMMENTSMET_NEO_UNKNOWN_VERSION), strSionFile, GetFileName());
				file.Close();
				theApp.BackupEngine->SetLastErrorState(EMB_ERROR_STATE_RECOVERABLE); // MOD: NB - [NeoBackup]
				return false;
			}
			LoadComments(&file);
		}
		// MOD: XC END

		file.Close();
	}
	catch(CFileException* error){
		if (error->m_cause == CFileException::endOfFile){
			ModLogError(LOG_STATUSBAR, GetResString(IDS_X_ERR_NEOMEOCORRUPT), strSionFile, GetFileName());
			theApp.BackupEngine->SetLastErrorState(EMB_ERROR_STATE_RECOVERABLE); // MOD: NB - [NeoBackup]
		}else{
			TCHAR buffer[MAX_CFEXP_ERRORMSG];
			error->GetErrorMessage(buffer,ARRSIZE(buffer));
			ModLogError(LOG_STATUSBAR, GetResString(IDS_X_ERR_FILEERROR), strSionFile, GetFileName(), buffer);
			theApp.BackupEngine->SetLastErrorState(EMB_ERROR_STATE_UNRECOVERABLE); // MOD: NB - [NeoBackup]
		}
		error->Delete();
		return false;
	}
	catch(...){
		ModLogError(LOG_STATUSBAR, GetResString(IDS_ERR_METCORRUPT), strSionFile, GetFileName());
		theApp.BackupEngine->SetLastErrorState(EMB_ERROR_STATE_UNRECOVERABLE); // MOD: NB - [NeoBackup]
		ASSERT(0);
		return false;
	}

	theApp.BackupEngine->SetLastErrorState(EMB_ERROR_STATE_CORRECT); // MOD: NB - [NeoBackup]
	return true;
}

bool CKnownFileList::LoadKnownPreferences()
{
	CString fullpath=thePrefs.GetConfigDir();
	fullpath.Append(KNOWNPREFS_MET_FILENAME);
	CSafeBufferedFile file;
	CFileException fexp;
	if (!file.Open(fullpath,CFile::modeRead|CFile::osSequentialScan|CFile::typeBinary|CFile::shareDenyWrite, &fexp)){
		if (fexp.m_cause != CFileException::fileNotFound){
			CString strError(_T("Failed to load ") KNOWNPREFS_MET_FILENAME _T(" file"));
			TCHAR szError[MAX_CFEXP_ERRORMSG];
			if (fexp.GetErrorMessage(szError, ARRSIZE(szError))){
				strError += _T(" - ");
				strError += szError;
			}
			ModLogError(LOG_STATUSBAR, _T("%s"), strError);
			theApp.BackupEngine->SetLastErrorState(EMB_ERROR_STATE_UNRECOVERABLE); // MOD: NB - [NeoBackup]
			return false;
		}
		theApp.BackupEngine->SetLastErrorState(EMB_ERROR_STATE_RECOVERABLE); // MOD: NB - [NeoBackup]
		return false;
	}
	setvbuf(file.m_pStream, NULL, _IOFBF, 16384);

	try {
		uint8 version = file.ReadUInt8();
		if (version > KNOWNPREFSFILE_VERSION || version <= KNOWNPREFSFILE_VERSION_OLD){
			ModLogError(LOG_STATUSBAR, GetResString(IDS_X_ERR_KNOWNPREFSMET_UNKNOWN_VERSION));
			file.Close();
			theApp.BackupEngine->SetLastErrorState(EMB_ERROR_STATE_RECOVERABLE); // MOD: NB - [NeoBackup]
			return false;
		}
		
//		const uint32 dwExpired = time(NULL) - 1296000; // MOD: MKN - [MergeKnown]
		UINT RecordsNumber = file.ReadUInt32();
		uchar cur_hash[16];
		CKnownFile* cuf_file=NULL;
		for (UINT i = 0; i < RecordsNumber; i++) {
			file.ReadHash16(cur_hash);
			if((cuf_file = FindKnownFileByID(cur_hash)) != NULL){
/*				// MOD: MKN - [MergeKnown]
				if (cuf_file->GetLastSeen() < dwExpired){
					ModLogError(GetResString(IDS_X_ERR_KNOWNPREFSCLEANUP), cuf_file->GetFileName()); 
					ClearPreferencesEntry(&file);
					continue;
				}
*/				// MOD: MKN END
				if(!cuf_file->KnownPrefs.LoadKnownPrefs(&file)){
					ModLogError(GetResString(IDS_X_ERR_KNOWNPREFSMET_ENTRY_CORRUPT), cuf_file->GetFileName());
				}
			}else{
				ModLogError(GetResString(IDS_X_ERR_KNOWNPREFSASYNCHRONIZED), md4str(cur_hash)); 
				ClearPreferencesEntry(&file);
			}
		}
		file.Close();
	}
	catch(CFileException* error){
		if (error->m_cause == CFileException::endOfFile){
			ModLogError(LOG_STATUSBAR, GetResString(IDS_X_ERR_KNOWNPREFSMET_BAD));
			theApp.BackupEngine->SetLastErrorState(EMB_ERROR_STATE_RECOVERABLE); // MOD: NB - [NeoBackup]
		}else{
			TCHAR buffer[MAX_CFEXP_ERRORMSG];
			error->GetErrorMessage(buffer,MAX_CFEXP_ERRORMSG);
			ModLogError(LOG_STATUSBAR, GetResString(IDS_X_ERR_KNOWNPREFSMET_UNKNOWN),buffer);
			theApp.BackupEngine->SetLastErrorState(EMB_ERROR_STATE_UNRECOVERABLE); // MOD: NB - [NeoBackup]
		}
		error->Delete();
		return false;
	}

	theApp.BackupEngine->SetLastErrorState(EMB_ERROR_STATE_CORRECT); // MOD: NB - [NeoBackup]
	return true;
}

bool CKnownFileList::ClearPreferencesEntry(CFileDataIO* file)
{
	UINT tagcount = file->ReadUInt32();
	for (UINT j = 0; j < tagcount; j++){
		CTag* newtag = new CTag(file, false);
        delete newtag;
	}
	return true;
}

bool CKnownFileList::SaveKnownPreferences()
{
	if (thePrefs.GetLogFileSaving())
		AddDebugLogLine(false,_T("Saving KnownPrefs files list file \"%s\""), KNOWNPREFS_MET_FILENAME);
	CString fullpath=thePrefs.GetConfigDir();
	fullpath += KNOWNPREFS_MET_FILENAME;
	CSafeBufferedFile file;
	CFileException fexp;
	if (!file.Open(fullpath, CFile::modeWrite|CFile::modeCreate|CFile::typeBinary|CFile::shareDenyWrite, &fexp)){
		CString strError(_T("Failed to save ") KNOWNPREFS_MET_FILENAME _T(" file"));
		TCHAR szError[MAX_CFEXP_ERRORMSG];
		if (fexp.GetErrorMessage(szError, ARRSIZE(szError))){
			strError += _T(" - ");
			strError += szError;
		}
		ModLogError(LOG_STATUSBAR, _T("%s"), strError);
		return false;
	}
	setvbuf(file.m_pStream, NULL, _IOFBF, 16384);

	try{
		file.WriteUInt8(KNOWNPREFSFILE_VERSION);

		uint32 uTagCount = 0;
		ULONG uTagCountFilePos = (ULONG)file.GetPosition();
		file.WriteUInt32(uTagCount);

		POSITION pos = m_Files_map.GetStartPosition();
		while( pos != NULL )
		{
			CKnownFile* pFile;
			CCKey key;
			m_Files_map.GetNextAssoc( pos, key, pFile );
			if(pFile->KnownPrefs.HasPreferences()){
				file.WriteHash16(pFile->GetFileHash());
				pFile->KnownPrefs.SaveKnownPrefs(&file);
				uTagCount++;
			}
		}

		file.Seek(uTagCountFilePos, CFile::begin);
		file.WriteUInt32(uTagCount);
		file.Seek(0, CFile::end);

		if (thePrefs.GetCommitFiles() >= 2 || (thePrefs.GetCommitFiles() >= 1 && !theApp.emuledlg->IsRunning())){
			file.Flush(); // flush file stream buffers to disk buffers
			if (_commit(_fileno(file.m_pStream)) != 0) // commit disk buffers to disk
				AfxThrowFileException(CFileException::hardIO, GetLastError(), file.GetFileName());
		}
		file.Close();
	}
	catch(CFileException* error){
		CString strError(_T("Failed to save ") KNOWNPREFS_MET_FILENAME _T(" file"));
		TCHAR szError[MAX_CFEXP_ERRORMSG];
		if (error->GetErrorMessage(szError, ARRSIZE(szError))){
			strError += _T(" - ");
			strError += szError;
		}
		ModLogError(LOG_STATUSBAR, _T("%s"), strError);
		error->Delete();
	}

	return true;
}


CKnownPreferences::CKnownPreferences(){
	KnownFile = NULL;

	ResetTweaks();
}

void CKnownPreferences::ResetTweaks(){
	// MOD: IPS - [InteligentPartSharing]
	m_iMaxProzentToHide = 0;

	m_iHideOverAvaliblyParts = 0;
	m_iHideOverAvaliblyMode = 0;
	m_fHideOverAvaliblyValue = 0;
	m_iHideOverAvaliblyCatch = 0;

	m_iHideOverSharedParts = 0;
	m_iHideOverSharedMode = 0;
	m_fHideOverSharedValue = 0;
	m_iHideOverSharedCalc = 0;
	
	m_iDontHideUnderAvaliblyParts = 0;
	m_iDontHideUnderAvaliblyMode = 0;
	m_fDontHideUnderAvaliblyValue = 0;

	m_iBlockHighOverAvaliblyParts = 0;
	m_fBlockHighOverAvaliblyFactor = 0;

	m_iBlockHighOverSharedParts = 0;
	m_fBlockHighOverSharedFactor = 0;

	m_iShowAlwaysNeeded = 0;
	m_iShowAlwaysParts = 0;

	m_iShowAlwaysIncomplete = 0;
	// MOD: IPS END

}

CKnownPreferences::~CKnownPreferences(){
	ClearTags();
}

bool CKnownPreferences::HasPreferences(){
	return (
		// MOD: IPS - [InteligentPartSharing]
		m_iMaxProzentToHide ||

		m_iHideOverAvaliblyParts ||
		m_iHideOverAvaliblyMode ||
		m_fHideOverAvaliblyValue ||
		m_iHideOverAvaliblyCatch ||

		m_iHideOverSharedParts ||
		m_iHideOverSharedMode ||
		m_fHideOverSharedValue ||
		m_iHideOverSharedCalc ||
	
		m_iDontHideUnderAvaliblyParts ||
		m_iDontHideUnderAvaliblyMode ||
		m_fDontHideUnderAvaliblyValue ||

		m_iBlockHighOverAvaliblyParts ||
		m_fBlockHighOverAvaliblyFactor ||

		m_iBlockHighOverSharedParts ||
		m_fBlockHighOverSharedFactor ||

		m_iShowAlwaysNeeded ||
		m_iShowAlwaysParts ||

		m_iShowAlwaysIncomplete ||
		// MOD: IPS END

		HasManagedParts()
		);
}

bool CKnownPreferences::SaveKnownPrefs(CFileDataIO* file)
{
	UINT uTagCount = 0;
	ULONG uTagCountFilePos = (ULONG)file->GetPosition();
	file->WriteUInt32(uTagCount);

	// MOD: IPS - [InteligentPartSharing]
	if (m_iMaxProzentToHide){
		CTag tag(NFT_IPS_MAXIMUM, m_iMaxProzentToHide);
		tag.WriteTagToFile(file);
		uTagCount++;
	}

	if (m_iHideOverAvaliblyParts){
		CTag tag(NFT_IPS_HIDE_OA, m_iHideOverAvaliblyParts);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iHideOverAvaliblyMode){
		CTag tag(NFT_IPS_OA_MODE, m_iHideOverAvaliblyMode);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_fHideOverAvaliblyValue){
		CED2KTag tag(NFT_IPS_OA_VALUE, m_fHideOverAvaliblyValue);
		tag.WriteNewEd2kTag(file);
		uTagCount++;
	}
	if (m_iHideOverAvaliblyCatch){
		CTag tag(NFT_IPS_OA_CATCH, m_iHideOverAvaliblyCatch);
		tag.WriteTagToFile(file);
		uTagCount++;
	}

	if (m_iHideOverSharedParts){
		CTag tag(NFT_IPS_HIDE_OS, m_iHideOverSharedParts);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iHideOverSharedMode){
		CTag tag(NFT_IPS_OS_MODE, m_iHideOverSharedMode);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_fHideOverSharedValue){
		CED2KTag tag(NFT_IPS_OS_VALUE, m_fHideOverSharedValue);
		tag.WriteNewEd2kTag(file);
		uTagCount++;
	}
	if (m_iHideOverSharedCalc){
		CTag tag(NFT_IPS_OS_CALC, m_iHideOverSharedCalc);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	
	if (m_iDontHideUnderAvaliblyParts){
		CTag tag(NFT_IPS_SHOW_UA, m_iDontHideUnderAvaliblyParts);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iDontHideUnderAvaliblyMode){
		CTag tag(NFT_IPS_UA_MODE, m_iDontHideUnderAvaliblyMode);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_fDontHideUnderAvaliblyValue){
		CED2KTag tag(NFT_IPS_UA_VALUE, m_fDontHideUnderAvaliblyValue);
		tag.WriteNewEd2kTag(file);
		uTagCount++;
	}

	if (m_iBlockHighOverAvaliblyParts){
		CTag tag(NFT_IPS_BLOCK_OA, m_iBlockHighOverAvaliblyParts);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_fBlockHighOverAvaliblyFactor){
		CED2KTag tag(NFT_IPS_OA_FACTOR, m_fBlockHighOverAvaliblyFactor);
		tag.WriteNewEd2kTag(file);
		uTagCount++;
	}

	if (m_iBlockHighOverSharedParts){
		CTag tag(NFT_IPS_BLOCK_OS, m_iBlockHighOverSharedParts);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_fBlockHighOverSharedFactor){
		CED2KTag tag(NFT_IPS_OS_FACTOR, m_fBlockHighOverSharedFactor);
		tag.WriteNewEd2kTag(file);
		uTagCount++;
	}

	if (m_iShowAlwaysNeeded){
		CTag tag(NFT_IPS_SHOW_ALWAYS_NEEDED, m_iShowAlwaysNeeded);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iShowAlwaysParts){
		CTag tag(NFT_IPS_SHOW_ALWAYS_PARTS, m_iShowAlwaysParts);
		tag.WriteTagToFile(file);
		uTagCount++;
	}

	if (m_iShowAlwaysIncomplete){
		CTag tag(NFT_IPS_SHOW_ALWAYS_INCOMPLETE, m_iShowAlwaysIncomplete);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	// MOD: IPS END

	// MOD: MPS - [ManualPartSharing]
	if(HasManagedParts())
		for(uint16 part = 0; part < KnownFile->GetPartCount(); part++){
			uint8 status = GetManagedPart(part);
			if(status != PR_PART_NORMAL){
				uint8 TagID;
				switch(status){
				case PR_PART_ON: TagID = NFT_PR_PART_ON; break;
				case PR_PART_HIDEN: TagID = NFT_PR_PART_HIDEN; break;
				case PR_PART_OFF: TagID = NFT_PR_PART_OFF; break;
				default: ASSERT(0); TagID = NFT_PR_PART_NORMAL;
				}
				CTag tag(TagID, part);
				tag.WriteTagToFile(file);
				uTagCount++;
			}
		}
	// MOD: MPS END

	for (int j = 0; j < taglist.GetCount(); j++){
		if (taglist[j]->IsStr() || taglist[j]->IsInt()){
			taglist[j]->WriteNewEd2kTag(file);
			uTagCount++;
		}
	}

	file->Seek(uTagCountFilePos, CFile::begin);
	file->WriteUInt32(uTagCount);
	file->Seek(0, CFile::end);

	return true;
}

void CKnownPreferences::ClearTags()
{
	for (int i = 0; i < taglist.GetSize(); i++)
		delete taglist[i];
	taglist.RemoveAll();
}

bool CKnownPreferences::LoadKnownPrefs(CFileDataIO* file)
{
	UINT tagcount = file->ReadUInt32();
	for (UINT j = 0; j < tagcount; j++){
		CTag* newtag = new CTag(file, false);
		switch (newtag->GetNameID()){

			// MOD: IPS - [InteligentPartSharing]
			case NFT_IPS_MAXIMUM:{
                ASSERT( newtag->IsInt() );
                m_iMaxProzentToHide = newtag->GetInt();
                delete newtag;
                break;
            }

			case NFT_IPS_HIDE_OA:{
                ASSERT( newtag->IsInt() );
                m_iHideOverAvaliblyParts = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_IPS_OA_MODE:{
                ASSERT( newtag->IsInt() );
                m_iHideOverAvaliblyMode = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_IPS_OA_VALUE:{
                ASSERT( newtag->IsFloat() );
                m_fHideOverAvaliblyValue = newtag->GetFloat();
                delete newtag;
                break;
            }
			case NFT_IPS_OA_CATCH:{
                ASSERT( newtag->IsInt() );
                m_iHideOverAvaliblyCatch = newtag->GetInt();
                delete newtag;
                break;
            }

			case NFT_IPS_HIDE_OS:{
                ASSERT( newtag->IsInt() );
                m_iHideOverSharedParts = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_IPS_OS_MODE:{
                ASSERT( newtag->IsInt() );
                m_iHideOverSharedMode = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_IPS_OS_VALUE:{
                ASSERT( newtag->IsFloat() );
                m_fHideOverSharedValue = newtag->GetFloat();
                delete newtag;
                break;
            }
			case NFT_IPS_OS_CALC:{
                ASSERT( newtag->IsInt() );
                m_iHideOverSharedCalc = newtag->GetInt();
                delete newtag;
                break;
            }

			case NFT_IPS_SHOW_UA:{
                ASSERT( newtag->IsInt() );
                m_iDontHideUnderAvaliblyParts = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_IPS_UA_MODE:{
                ASSERT( newtag->IsInt() );
                m_iDontHideUnderAvaliblyMode = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_IPS_UA_VALUE:{
                ASSERT( newtag->IsFloat() );
                m_fDontHideUnderAvaliblyValue = newtag->GetFloat();
                delete newtag;
                break;
            }

			case NFT_IPS_BLOCK_OA:{
                ASSERT( newtag->IsInt() );
                m_iBlockHighOverAvaliblyParts = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_IPS_OA_FACTOR:{
                ASSERT( newtag->IsFloat() );
                m_fBlockHighOverAvaliblyFactor = newtag->GetFloat();
                delete newtag;
                break;
            }

			case NFT_IPS_BLOCK_OS:{
                ASSERT( newtag->IsInt() );
                m_iBlockHighOverSharedParts = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_IPS_OS_FACTOR:{
                ASSERT( newtag->IsFloat() );
                m_fBlockHighOverSharedFactor = newtag->GetFloat();
                delete newtag;
                break;
            }

			case NFT_IPS_SHOW_ALWAYS_NEEDED:{
                ASSERT( newtag->IsInt() );
                m_iShowAlwaysNeeded = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_IPS_SHOW_ALWAYS_PARTS:{
                ASSERT( newtag->IsInt() );
                m_iShowAlwaysParts = newtag->GetInt();
                delete newtag;
                break;
            }

			case NFT_IPS_SHOW_ALWAYS_INCOMPLETE:{
                ASSERT( newtag->IsInt() );
                m_iShowAlwaysIncomplete = newtag->GetInt();
                delete newtag;
                break;
            }
			// MOD: IPS END

			// MOD: MPS - [ManualPartSharing]
			case NFT_PR_PART_NORMAL:{
                ASSERT( newtag->IsInt() );
                delete newtag;
                break;
            }
			case NFT_PR_PART_ON:{
                ASSERT( newtag->IsInt() );
                SetManagedPart(newtag->GetInt(), PR_PART_ON);
                delete newtag;
                break;
            }
			case NFT_PR_PART_HIDEN:{
                ASSERT( newtag->IsInt() );
                SetManagedPart(newtag->GetInt(), PR_PART_HIDEN);
                delete newtag;
                break;
            }
			case NFT_PR_PART_OFF:{
                ASSERT( newtag->IsInt() );
                SetManagedPart(newtag->GetInt(), PR_PART_OFF);
                delete newtag;
                break;
            }
			// MOD: MPS END

			default:{
				taglist.Add(newtag);
			}
		}
	}

	CheckTweaks();

	return true;
}

void CKnownPreferences::CheckTweaks(){
	// MOD: IPS - [InteligentPartSharing]
	if(m_iMaxProzentToHide) MinMax(&m_iMaxProzentToHide,VAL_IPS_MAX_MIN,VAL_IPS_MAX_MAX);

	if(m_fHideOverAvaliblyValue) MinMax(&m_fHideOverAvaliblyValue, m_iHideOverAvaliblyMode ? VAL_IPS_OA_MIN2 : VAL_IPS_OA_MIN1, m_iHideOverAvaliblyMode ? VAL_IPS_OA_MAX2 : VAL_IPS_OA_MAX1);
	if(m_fHideOverSharedValue) MinMax(&m_fHideOverSharedValue, m_iHideOverSharedMode ? VAL_IPS_OS_MIN2 : VAL_IPS_OS_MIN1, m_iHideOverSharedMode ? VAL_IPS_OS_MAX2 : VAL_IPS_OS_MAX1);

	if(m_fDontHideUnderAvaliblyValue) MinMax(&m_fDontHideUnderAvaliblyValue, m_iDontHideUnderAvaliblyMode ? VAL_IPS_UA_MIN2 : VAL_IPS_UA_MIN1, m_iDontHideUnderAvaliblyMode ? VAL_IPS_UA_MAX2 : VAL_IPS_UA_MAX1);

	if(m_fBlockHighOverAvaliblyFactor) MinMax(&m_fBlockHighOverAvaliblyFactor,VAL_IPS_OAF_MIN,VAL_IPS_OAF_MAX);
	if(m_fBlockHighOverSharedFactor) MinMax(&m_fBlockHighOverSharedFactor,VAL_IPS_OSF_MIN,VAL_IPS_OSF_MAX);

	if(m_iShowAlwaysParts) MinMax(&m_iShowAlwaysParts,VAL_SHOW_ALWAYS_PARTS_MIN,VAL_SHOW_ALWAYS_PARTS_MAX);
	// MOD: IPS END
}

// MOD: IPS - [InteligentPartSharing]
/* 
*     Global setting     File Setting
*     0 - Disable        0 - Default
*     1 - Enable KF	     1 - Enable
*     2 - Enable K/PF    2 - Disable
*     
*     for dan't hide
*     Global setting     File Setting
*     0 - Disable        0 - Default
*     1 - Enable PF	     1 - Enable
*     2 - Enable K/PF    2 - Disable
*/
bool CKnownPreferences::IsInteligentPartSharing(){
	if(!thePrefs.IsInteligentPartSharing())
		return false;
	return (IsHideOverAvaliblyParts() || IsHideOverSharedParts());
}
int CKnownPreferences::GetMaxProzentToHide(){
	return m_iMaxProzentToHide ? m_iMaxProzentToHide : thePrefs.GetMaxProzentToHide();
}

bool CKnownPreferences::IsHideOverAvaliblyParts(){
	if(m_iHideOverAvaliblyParts) 
		return m_iHideOverAvaliblyParts == 1 ? 1 : 0;

	int state = thePrefs.IsHideOverAvaliblyParts();
	if(state == 1)
		return !(KnownFile->IsPartFile());
	else if (state == 2)
		return true;
	else
		return false;
}
int CKnownPreferences::GetHideOverAvaliblyMode(){
	if(m_iHideOverAvaliblyMode) 
		return m_iHideOverAvaliblyMode == 1 ? 1 : 0;
	return thePrefs.GetHideOverAvaliblyMode();
}
float CKnownPreferences::GetHideOverAvaliblyValue(){
	return m_fHideOverAvaliblyValue ? m_fHideOverAvaliblyValue : thePrefs.GetHideOverAvaliblyValue();
}
/*
// MOD: NPC - [NeoPartCatch]
int	CKnownPreferences::GetHideOverAvaliblyCatch(){
	if((m_iHideOverAvaliblyCatch == 0 && thePrefs.UseRealPartStatus() == 1 && thePrefs.UseAntiHideOS() == 1) 
	 || (m_iHideOverAvaliblyCatch == 2 && thePrefs.UseRealPartStatus() && thePrefs.UseAntiHideOS()))
		return PART_CATCH_REAL_HIDEN;
	else if((m_iHideOverAvaliblyCatch == 0 && thePrefs.UseRealPartStatus() == 1) 
	 || (m_iHideOverAvaliblyCatch == 1 && thePrefs.UseRealPartStatus()))
		return PART_CATCH_REAL;
	else if((m_iHideOverAvaliblyCatch == 0 && thePrefs.UseAntiHideOS() == 1) 
	 || (m_iHideOverAvaliblyCatch == 3 && thePrefs.UseAntiHideOS())) 
		return PART_CATCH_HIDEN;
	else 
		return PART_CATCH_DISABLED;
}
// MOD: NPC END
*/
bool CKnownPreferences::IsHideOverSharedParts(){
	if(!thePrefs.UsePartTraffic())
		return false;

	if(m_iHideOverSharedParts) 
		return m_iHideOverSharedParts == 1 ? 1 : 0;

	int state = thePrefs.IsHideOverSharedParts();
	if(state == 1)
		return !(KnownFile->IsPartFile());
	else if (state == 2)
		return true;
	else
		return false;
}
int CKnownPreferences::GetHideOverSharedMode(){
	if(m_iHideOverSharedMode) 
		return m_iHideOverSharedMode == 1 ? 1 : 0;
	return thePrefs.GetHideOverSharedMode();
}
float CKnownPreferences::GetHideOverSharedValue(){
	return m_fHideOverSharedValue ? m_fHideOverSharedValue : thePrefs.GetHideOverSharedValue();
}
int CKnownPreferences::GetHideOverSharedCalc(){
	if(m_iHideOverSharedCalc) 
		return m_iHideOverSharedCalc == 2 ? 1 : 0;
	return thePrefs.GetHideOverSharedCalc();
}

bool CKnownPreferences::IsDontHideUnderAvaliblyParts(){
	if(m_iDontHideUnderAvaliblyParts) 
		return m_iDontHideUnderAvaliblyParts == 1 ? 1 : 0;

	int state = thePrefs.IsDontHideUnderAvaliblyParts();
	if(state == 1)
		return I2B(KnownFile->IsPartFile());
	else if (state == 2)
		return true;
	else
		return false;
}
int CKnownPreferences::GetDontHideUnderAvaliblyMode(){
	if(m_iDontHideUnderAvaliblyMode) 
		return m_iDontHideUnderAvaliblyMode == 1 ? 1 : 0;
	return thePrefs.GetDontHideUnderAvaliblyMode();
}
 float CKnownPreferences::GetDontHideUnderAvaliblyValue(){
	return m_fDontHideUnderAvaliblyValue ? m_fDontHideUnderAvaliblyValue : thePrefs.GetDontHideUnderAvaliblyValue();
}

bool CKnownPreferences::IsBlockHighOverAvaliblyParts(){
	if(!IsHideOverAvaliblyParts())
		return false;

	if(m_iBlockHighOverAvaliblyParts) 
		return m_iBlockHighOverAvaliblyParts == 1 ? 1 : 0;

	int state = thePrefs.IsBlockHighOverAvaliblyParts();
	if(state == 1)
		return !(KnownFile->IsPartFile());
	else if (state == 2)
		return true;
	else
		return false;
}
float CKnownPreferences::GetBlockHighOverAvaliblyFactor(){
	return m_fBlockHighOverAvaliblyFactor ? m_fBlockHighOverAvaliblyFactor : thePrefs.GetBlockHighOverAvaliblyFactor();
}

bool CKnownPreferences::IsBlockHighOverSharedParts(){
	if(!IsHideOverSharedParts())
		return false;

	if(m_iBlockHighOverSharedParts) 
		return m_iBlockHighOverSharedParts == 1 ? 1 : 0;

	int state = thePrefs.IsBlockHighOverSharedParts();
	if(state == 1)
		return !(KnownFile->IsPartFile());
	else if (state == 2)
		return true;
	else
		return false;
}
float CKnownPreferences::GetBlockHighOverSharedFactor(){
	return m_fBlockHighOverSharedFactor ? m_fBlockHighOverSharedFactor : thePrefs.GetBlockHighOverSharedFactor();
}

bool CKnownPreferences::IsShowAlwaysNeeded(){
	if(m_iShowAlwaysNeeded) 
		return m_iShowAlwaysNeeded == 1 ? 1 : 0;

	int state = thePrefs.IsShowAlwaysNeeded();
	if(state == 1)
		return KnownFile->IsPartFile();
	else if (state == 2)
		return true;
	else
		return false;
}
int CKnownPreferences::GetShowAlwaysParts(){
	return m_iShowAlwaysParts ? m_iShowAlwaysParts : thePrefs.GetShowAlwaysParts();
}

bool CKnownPreferences::IsShowAlwaysIncomplete(){
	if(m_iShowAlwaysIncomplete) 
		return m_iShowAlwaysIncomplete == 1 ? 1 : 0;

	int state = thePrefs.IsShowAlwaysIncomplete();
	if(state == 1)
		return KnownFile->IsPartFile();
	else if (state == 2)
		return true;
	else
		return false;
}
// MOD: IPS END

// MOD: MPS - [ManualPartSharing]
uint8  CKnownPreferences::GetManagedPart(UINT part) const 
{ 
	uint8 status = PR_PART_NORMAL; 
	if(m_ManagedParts.Lookup(part, status)) 
		return status; 
	else 
		return PR_PART_NORMAL; 
}

bool  CKnownPreferences::HasManagedParts() const
{
	POSITION	pos = m_ManagedParts.GetStartPosition();
	UINT		part;
	uint8		status;
	while(pos)
	{
		m_ManagedParts.GetNextAssoc(pos, part, status);
		if(status != PR_PART_NORMAL)
			return true;
	}
	return false;
}
// MOD: MPS END

/////////////////////
CPartPreferences::CPartPreferences(){
	PartFile = NULL;

	ResetTweaks();
}

void CPartPreferences::ResetTweaks()
{

	// MOD: DS - [DropSources]
	m_iDropNnP = 0;
	m_iDropNnPLimit = 0;
	m_iDropNnPLimitMode = 0;
	m_iDropNnPTimer = 0;

	m_iDropFullQ = 0;
	m_iDropFullQLimit = 0;
	m_iDropFullQLimitMode = 0;
	m_iDropFullQTimer = 0;

	m_iDropHighQ = 0;
	m_iDropHighQValue = 0;
	m_iDropHighQLimit = 0;
	m_iDropHighQLimitMode = 0;
	m_iDropHighQTimer = 0;

	m_iUseDontAskThisIP = 0;
	m_iDontAskThisIPTime = 0;
	// MOD: DS END


	// MOD: DR - [DownloadReask]
	m_iReaskIntervals = 0;

	m_iNnPReask = 0;
	m_iNnPReaskIntervals = 0;

	m_iFullQReask = 0;
	m_iFullQReaskIntervals = 0;
	// MOD: DR END

}

CPartPreferences::~CPartPreferences(){
	ClearTags();
}

bool CPartPreferences::SavePartPrefs(CFileDataIO* file)
{

	UINT uTagCount = 0;
	ULONG uTagCountFilePos = (ULONG)file->GetPosition();
	file->WriteUInt32(uTagCount);


	// MOD: DS - [DropSources]
	if (m_iDropNnP){
		CTag tag(NFT_DROP_NNP_ENABLE, m_iDropNnP);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iDropNnPLimit){
		CTag tag(NFT_DROP_NNP_LIMIT, m_iDropNnPLimit);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iDropNnPLimitMode){
		CTag tag(NFT_DROP_NNP_LIMIT_MODE, m_iDropNnPLimitMode);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iDropNnPTimer){
		CTag tag(NFT_DROP_NNP_TIMER, m_iDropNnPTimer);
		tag.WriteTagToFile(file);
		uTagCount++;
	}

	if (m_iDropFullQ){
		CTag tag(NFT_DROP_FULLQ_ENABLE, m_iDropFullQ);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iDropFullQLimit){
		CTag tag(NFT_DROP_FULLQ_LIMIT, m_iDropFullQLimit);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iDropFullQLimitMode){
		CTag tag(NFT_DROP_FULLQ_LIMIT_MODE, m_iDropFullQLimitMode);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iDropFullQTimer){
		CTag tag(NFT_DROP_FULLQ_TIMER, m_iDropFullQTimer);
		tag.WriteTagToFile(file);
		uTagCount++;
	}

	if (m_iDropHighQ){
		CTag tag(NFT_DROP_HIGHQ_ENABLE, m_iDropHighQ);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iDropHighQValue){
		CTag tag(NFT_DROP_HIGHQ_VALUE, m_iDropHighQValue);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iDropHighQLimit){
		CTag tag(NFT_DROP_HIGHQ_LIMIT, m_iDropHighQLimit);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iDropHighQLimitMode){
		CTag tag(NFT_DROP_HIGHQ_LIMIT_MODE, m_iDropHighQLimitMode);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iDropHighQTimer){
		CTag tag(NFT_DROP_HIGHQ_TIMER, m_iDropHighQTimer);
		tag.WriteTagToFile(file);
		uTagCount++;
	}

	if (m_iUseDontAskThisIP){
		CTag tag(NFT_USE_DONT_ASK_THIS_IP, m_iUseDontAskThisIP);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iDontAskThisIPTime){
		CTag tag(NFT_DONT_ASK_THIS_IP_TIME, m_iDontAskThisIPTime);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	// MOD: DS END


	// MOD: DR - [DownloadReask]
	if (m_iReaskIntervals){
		CTag tag(NFT_REASK_TIMER, m_iReaskIntervals);
		tag.WriteTagToFile(file);
		uTagCount++;
	}

	if (m_iNnPReask){
		CTag tag(NFT_NNP_REASK_ENABLE, m_iNnPReask);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iNnPReaskIntervals){
		CTag tag(NFT_NNP_REASK_TIMER, m_iNnPReaskIntervals);
		tag.WriteTagToFile(file);
		uTagCount++;
	}

	if (m_iFullQReask){
		CTag tag(NFT_FULLQ_REASK_ENABLE, m_iFullQReask);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	if (m_iFullQReaskIntervals){
		CTag tag(NFT_FULLQ_REASK_TIMER, m_iFullQReaskIntervals);
		tag.WriteTagToFile(file);
		uTagCount++;
	}
	// MOD: DR END

	for (int j = 0; j < taglist.GetCount(); j++){
		if (taglist[j]->IsStr() || taglist[j]->IsInt()){
			taglist[j]->WriteNewEd2kTag(file);
			uTagCount++;
		}
	}

	file->Seek(uTagCountFilePos, CFile::begin);
	file->WriteUInt32(uTagCount);
	file->Seek(0, CFile::end);

	return true;
}

void CPartPreferences::ClearTags()
{
	for (int i = 0; i < taglist.GetSize(); i++)
		delete taglist[i];
	taglist.RemoveAll();
}

bool CPartPreferences::LoadPartPrefs(CFileDataIO* file)
{
	UINT tagcount = file->ReadUInt32();
	for (UINT j = 0; j < tagcount; j++){
		CTag* newtag = new CTag(file, false);
		switch (newtag->GetNameID()){

			// MOD: DS - [DropSources]
			case NFT_DROP_NNP_ENABLE:{
                ASSERT( newtag->IsInt() );
                m_iDropNnP = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_DROP_NNP_LIMIT:{
                ASSERT( newtag->IsInt() );
                m_iDropNnPLimit = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_DROP_NNP_LIMIT_MODE:{
                ASSERT( newtag->IsInt() );
                m_iDropNnPLimitMode = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_DROP_NNP_TIMER:{
                ASSERT( newtag->IsInt() );
                m_iDropNnPTimer = newtag->GetInt();
                delete newtag;
                break;
            }

			case NFT_DROP_FULLQ_ENABLE:{
                ASSERT( newtag->IsInt() );
                m_iDropFullQ = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_DROP_FULLQ_LIMIT:{
                ASSERT( newtag->IsInt() );
                m_iDropFullQLimit = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_DROP_FULLQ_LIMIT_MODE:{
                ASSERT( newtag->IsInt() );
                m_iDropFullQLimitMode = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_DROP_FULLQ_TIMER:{
                ASSERT( newtag->IsInt() );
                m_iDropFullQTimer = newtag->GetInt();
                delete newtag;
                break;
            }

			case NFT_DROP_HIGHQ_ENABLE:{
                ASSERT( newtag->IsInt() );
                m_iDropHighQ = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_DROP_HIGHQ_VALUE:{
                ASSERT( newtag->IsInt() );
                m_iDropHighQValue = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_DROP_HIGHQ_LIMIT:{
                ASSERT( newtag->IsInt() );
                m_iDropHighQLimit = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_DROP_HIGHQ_LIMIT_MODE:{
                ASSERT( newtag->IsInt() );
                m_iDropHighQLimitMode = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_DROP_HIGHQ_TIMER:{
                ASSERT( newtag->IsInt() );
                m_iDropHighQTimer = newtag->GetInt();
                delete newtag;
                break;
            }

			case NFT_USE_DONT_ASK_THIS_IP:{
                ASSERT( newtag->IsInt() );
                m_iUseDontAskThisIP = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_DONT_ASK_THIS_IP_TIME:{
                ASSERT( newtag->IsInt() );
                m_iDontAskThisIPTime = newtag->GetInt();
                delete newtag;
                break;
            }
			// MOD: DS END

			// MOD: DR - [DownloadReask]
			case NFT_REASK_TIMER:{
                ASSERT( newtag->IsInt() );
                m_iReaskIntervals = newtag->GetInt();
                delete newtag;
                break;
            }

			case NFT_NNP_REASK_ENABLE:{
                ASSERT( newtag->IsInt() );
                m_iNnPReask = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_NNP_REASK_TIMER:{
                ASSERT( newtag->IsInt() );
                m_iNnPReaskIntervals = newtag->GetInt();
                delete newtag;
                break;
            }

			case NFT_FULLQ_REASK_ENABLE:{
                ASSERT( newtag->IsInt() );
                m_iFullQReask = newtag->GetInt();
                delete newtag;
                break;
            }
			case NFT_FULLQ_REASK_TIMER:{
                ASSERT( newtag->IsInt() );
                m_iFullQReaskIntervals = newtag->GetInt();
                delete newtag;
                break;
            }
			// MOD: DR END

			default:{
				taglist.Add(newtag);
			}
		}
	}

	CheckTweaks();

	return true;
}

void CPartPreferences::CheckTweaks(){

	// MOD: DR - [DownloadReask]
	if(m_iReaskIntervals) MinMax(&m_iReaskIntervals,TIM_REASK_MIN,TIM_REASK_MAX);
	if(m_iNnPReaskIntervals) MinMax(&m_iNnPReaskIntervals,TIM_REASK_NNP_MIN,TIM_REASK_NNP_MAX);
	if(m_iFullQReaskIntervals) MinMax(&m_iFullQReaskIntervals,TIM_REASK_FULLQ_MIN,TIM_REASK_FULLQ_MAX);
	// MOD: DR END


	// MOD: DS - [DropSources]
	if(m_iDropNnPTimer) MinMax(&m_iDropNnPTimer,TIM_DROP_MIN,TIM_DROP_MAX);
	if(m_iDropNnPLimit) MinMax(&m_iDropHighQLimit,VAL_DROP_LIMIT_MIN(m_iDropHighQLimitMode),VAL_DROP_LIMIT_MAX(m_iDropHighQLimitMode));

	if(m_iDropFullQTimer) MinMax(&m_iDropFullQTimer,TIM_DROP_MIN,TIM_DROP_MAX);
	if(m_iDropFullQLimit) MinMax(&m_iDropHighQLimit,VAL_DROP_LIMIT_MIN(m_iDropHighQLimitMode),VAL_DROP_LIMIT_MAX(m_iDropHighQLimitMode));

	if(m_iDropHighQValue) MinMax(&m_iDropHighQValue,VAL_DROP_HQ_VALUE_MIN,VAL_DROP_HQ_VALUE_MAX);
	if(m_iDropHighQLimit) MinMax(&m_iDropHighQLimit,VAL_DROP_LIMIT_MIN(m_iDropHighQLimitMode),VAL_DROP_LIMIT_MAX(m_iDropHighQLimitMode));
	if(m_iDropHighQTimer) MinMax(&m_iDropHighQTimer,TIM_DROP_MIN,TIM_DROP_MAX);

	if(m_iDontAskThisIPTime) MinMax(&m_iDontAskThisIPTime,VAL_DONT_ASK_THIS_IP_TIME_MIN,VAL_DONT_ASK_THIS_IP_TIME_MAX);
	// MOD: DS END
}

// MOD: DR - [DownloadReask]
int CPartPreferences::GetReaskIntervals(){
	return m_iReaskIntervals ? m_iReaskIntervals : thePrefs.GetReaskIntervals();
}

bool CPartPreferences::DontNnPReask(){
	if(m_iNnPReask) 
		return m_iNnPReask == 1 ? 0 : 1;
	return thePrefs.DontNnPReask();
}
int CPartPreferences::GetNnPReaskIntervals(){
	return m_iNnPReaskIntervals ? m_iNnPReaskIntervals : thePrefs.GetNnPReaskIntervals();
}

bool CPartPreferences::DontFullQReask(){
	if(m_iFullQReask) 
		return m_iFullQReask == 1 ? 0 : 1;
	return thePrefs.DontFullQReask();
}
int CPartPreferences::GetFullQReaskIntervals(){
	return m_iFullQReaskIntervals ? m_iFullQReaskIntervals : thePrefs.GetFullQReaskIntervals();
}
// MOD: DR END


// MOD: DS - [DropSources]
UINT CPartPreferences::UseDropNnP(){
	if(m_iDropNnP) 
		return m_iDropNnP == 3 ? 0 : m_iDropNnP;
	return thePrefs.UseDropNnP();
}
int CPartPreferences::GetDropNnPLimit(){
	return m_iDropNnPLimit ? m_iDropNnPLimit : thePrefs.GetDropNnPLimit();
}
int CPartPreferences::GetDropNnPLimitMode(){
	if(m_iDropNnPLimitMode) 
		return m_iDropNnPLimitMode == 4 ? 0 : m_iDropNnPLimitMode;
	return thePrefs.GetDropNnPLimitMode();
}
int CPartPreferences::GetDropNnPTimer(){
	return m_iDropNnPTimer ? m_iDropNnPTimer : thePrefs.GetDropNnPTimer();
}

UINT CPartPreferences::UseDropFullQ(){
	if(m_iDropFullQ) 
		return m_iDropFullQ == 3 ? 0 : m_iDropFullQ;
	return thePrefs.UseDropFullQ();
}
int CPartPreferences::GetDropFullQLimit(){
	return m_iDropFullQLimit ? m_iDropFullQLimit : thePrefs.GetDropFullQLimit();
}
int CPartPreferences::GetDropFullQLimitMode(){
	if(m_iDropFullQLimitMode) 
		return m_iDropFullQLimitMode == 4 ? 0 : m_iDropFullQLimitMode;
	return thePrefs.GetDropFullQLimitMode();
}
int CPartPreferences::GetDropFullQTimer(){
	return m_iDropFullQTimer ? m_iDropFullQTimer : thePrefs.GetDropFullQTimer();
}

UINT CPartPreferences::UseDropHighQ(){
	if(m_iDropHighQ) 
		return m_iDropHighQ == 3 ? 0 : m_iDropHighQ;
	return thePrefs.UseDropHighQ();
}
int CPartPreferences::GetDropHighQValue(){
	return m_iDropHighQValue ? m_iDropHighQValue : thePrefs.GetDropHighQValue();
}
int CPartPreferences::GetDropHighQLimit(){
	return m_iDropHighQLimit ? m_iDropHighQLimit : thePrefs.GetDropHighQLimit();
}
int CPartPreferences::GetDropHighQLimitMode(){
	if(m_iDropHighQLimitMode) 
		return m_iDropHighQLimitMode == 4 ? 0 : m_iDropHighQLimitMode;
	return thePrefs.GetDropHighQLimitMode();
}
int CPartPreferences::GetDropHighQTimer(){
	return m_iDropHighQTimer ? m_iDropHighQTimer : thePrefs.GetDropHighQTimer();
}

bool CPartPreferences::UseDontAskThisIPList(){
	if(m_iUseDontAskThisIP) 
		return m_iUseDontAskThisIP == 1 ? 1 : 0;
	return thePrefs.UseDontAskThisIPList();
}
int CPartPreferences::GetDontAskThisIPCleanUpTime(){
	return m_iDontAskThisIPTime ? m_iDontAskThisIPTime : thePrefs.GetDontAskThisIPCleanUpTime();
}
// MOD: DS END

// MOD: FCFG END <-- Loulach --
