void ProjectExplorerWindow::RemoveFileSymbols(wxTreeItemId node, const stdext::hash_set<Project::File*>& fileSet)
{

    ItemData* data = static_cast<ItemData*>(m_tree->GetItemData(node));

    if (data != NULL && data->isFile && fileSet.find((Project::File *)data->file) != fileSet.end())
    {
        m_tree->Delete(node);
    }
    else
    {

        // Recurse into the children.

        wxTreeItemIdValue cookie;
        wxTreeItemId child = m_tree->GetFirstChild(node, cookie);

        while (child.IsOk())
        {
            wxTreeItemId next = m_tree->GetNextChild(node, cookie);
            RemoveFileSymbols(child, fileSet);
            child = next;
        }

    }
    
}
/**
	@brief 클러스터의 후보들 중 현재 클러스터에 가장 적합하게 연결될 클러스터를 찾는다

	@param inClusterNo 현재 클러스터 번호
	@param inClusterCandidates 뒤에 연결될 클러스터의 후보
	@param inClustersHaveRecord 레코드를 가지고 있는 클러스터
	@param outAlreadyAddedCluster 클러스터 후보들중에 이미 추가되기로 결정된 클러스터가 있는지 여부
*/
std::vector<uint64_t> ClusterReassembler::findBestMatchingClusters(uint64_t inClusterNo, std::vector<CLUSTER_INFO>& inClusterCandidates, const stdext::hash_set<uint64_t>& inClustersHaveRecord, bool& outAlreadyAddedCluster)
{
    std::vector<uint64_t> resultClusters;
    uint8_t clusterBuffer[CLUSTER_SIZE] = {0,};
    uint8_t* mergedClusterBuffer = NULL;
    uint32_t clusterCandidateIndex = 0;
    uint32_t recordPos = 0;
    uint32_t nextClusterBufferPos = 0;

    outAlreadyAddedCluster = false;

    for (clusterCandidateIndex = 0; clusterCandidateIndex < inClusterCandidates.size(); ++clusterCandidateIndex)
    {
        if (inClusterNo == inClusterCandidates[clusterCandidateIndex].clusterNo)
        {
            outAlreadyAddedCluster = true;
            break;
        }
    }

    if (!outAlreadyAddedCluster)
    {
        nextClusterBufferPos = CLUSTER_SIZE;
        fragmentedFile_.readData(inClusterNo * CLUSTER_SIZE, clusterBuffer, CLUSTER_SIZE);
        uint32_t lastRecordPos = 0;
        EVTX_RECORD_HEADER* lastRecord = NULL;

        // 현재 클러스터에서(1클러스터) 가장 마지막 레코드를 찾는다
        for (recordPos = 0; recordPos < CLUSTER_SIZE - EVTX_RECORD_HEADER_SIZE; recordPos += 8)
        {
            if (EvtxRecordValidator::isValidRecordHeader(clusterBuffer + recordPos))
            {
                lastRecordPos = recordPos;
                break;
            }
        }

        lastRecord = (EVTX_RECORD_HEADER*)(clusterBuffer + lastRecordPos);

        // 가장 마지막 레코드가 3개 이상의 클러스터에 걸쳐져 있으면
        if (lastRecordPos + lastRecord->length1 > CLUSTER_SIZE * 2)
        {
            uint32_t needClusterCount = 0;
            uint64_t checkClusterNo = 0;
            bool clustersHaveNoRecord = true;

            if (lastRecordPos + lastRecord->length1 % CLUSTER_SIZE == 0)
            {
                needClusterCount = ((lastRecordPos + lastRecord->length1) - CLUSTER_SIZE) / CLUSTER_SIZE;
            }
            else
            {
                needClusterCount = ((lastRecordPos + lastRecord->length1) - CLUSTER_SIZE) / CLUSTER_SIZE + 1;
            }

            mergedClusterBuffer = new uint8_t[CLUSTER_SIZE * (needClusterCount + 2)];

            for (checkClusterNo = inClusterNo + 1; checkClusterNo < inClusterNo + 1 + needClusterCount; ++checkClusterNo)
            {
                if (inClustersHaveRecord.find(checkClusterNo) != inClustersHaveRecord.end())
                {
                    clustersHaveNoRecord = false;
                    break;
                }
            }

            // 어쩔수 없이 레코드의 클러스터들이 연속되어 있다고 가정하고 맨처음과 마지막을 뺀 중간 클러스터 데이터들을 정한다
            if (clustersHaveNoRecord)
            {
                for (uint64_t insertClusterNo = inClusterNo + 1; insertClusterNo < inClusterNo + 1 + needClusterCount; ++insertClusterNo)
                {
                    resultClusters.push_back(insertClusterNo);
                    fragmentedFile_.readData(insertClusterNo * CLUSTER_SIZE, mergedClusterBuffer + nextClusterBufferPos, CLUSTER_SIZE);
                    nextClusterBufferPos += CLUSTER_SIZE;
                }
            }
        }
        else
        {
            mergedClusterBuffer = new uint8_t[CLUSTER_SIZE * 2];
        }

        memcpy(mergedClusterBuffer, clusterBuffer, CLUSTER_SIZE);

        // 이어붙일 클러스터 후보군에 대하여
        for (clusterCandidateIndex = 0; clusterCandidateIndex < inClusterCandidates.size(); ++clusterCandidateIndex)
        {
            uint32_t mergedLastRecordPos = 0;
            EVTX_RECORD_HEADER* mergedLastRecord = NULL;

            // 실제로 클러스터들을 조립하여 보고
            fragmentedFile_.readData(inClusterCandidates[clusterCandidateIndex].clusterNo * CLUSTER_SIZE, (mergedClusterBuffer + nextClusterBufferPos), CLUSTER_SIZE);

            for (recordPos = 0; recordPos < CLUSTER_SIZE; recordPos += 8)
            {
                if (EvtxRecordValidator::isValidRecordHeader(mergedClusterBuffer + recordPos))
                {
                    mergedLastRecordPos = recordPos;
                    break;
                }
            }

            // 올바른 레코드가 되는지 확인하여 올바른 레코드가 되면 가장 적합한 클러스터라 정한다
            if (EvtxRecordValidator::isValidRecord(mergedClusterBuffer + mergedLastRecordPos, nextClusterBufferPos + CLUSTER_SIZE - mergedLastRecordPos))
            {
                mergedLastRecord = (EVTX_RECORD_HEADER*)(mergedClusterBuffer + mergedLastRecordPos);
                uint32_t parseSize = 0;

                BinXmlStream xmlStream(mergedClusterBuffer + mergedLastRecordPos + EVTX_RECORD_HEADER_SIZE,
                                       (mergedLastRecord->length1 - 0x1C > nextClusterBufferPos) ? nextClusterBufferPos : (mergedLastRecord->length1 - 0x1C), mergedLastRecord->numLogRecord);

                xmlStream.parse(&parseSize);

                if (parseSize > nextClusterBufferPos || mergedLastRecord->length1 - 0x1C - parseSize < 8)
                {
                    resultClusters.push_back(inClusterCandidates[clusterCandidateIndex].clusterNo);
                    break;
                }
            }
        }

        if (mergedClusterBuffer != NULL)
        {
            delete [] mergedClusterBuffer;
        }
    }

    return resultClusters;
}
/**
	@brief 파일로부터 레코드의 정보들을 모은다

	@param inRecordCarvedAreas 스킵할 영역
	@param outRecordInfoMap 클러스터 번호 / 레코드정보 쌍으로 된 맵
	@param outClustersHaveRecord 레코드를 가지고 있는 클러스터들
*/
void ClusterReassembler::buildRecordInfoMap(CarvedAreas& inCarvedAreas, RecordInfoMap& outRecordInfoMap, stdext::hash_set<uint64_t>& outClustersHaveRecord)
{
    SignatureFinder recordHeaderFinder(fragmentedFile_, EVTX_RECORD_HEADER_MAGIC, 4, 8, &inCarvedAreas, EVTX_RECORD_HEADER_SIZE);
    uint8_t* recordBuffer = NULL;
    EVTX_RECORD_HEADER* evtxRecordHeader = NULL;
    uint64_t foundFilePos = 0;
    uint64_t clusterNumber = 0;
    uint32_t clusterCrc = 0;
    uint8_t clusterBuffer[CLUSTER_SIZE] = {0,};
    uint32_t clusterIndex = 0;

    outClustersHaveRecord.clear();

    while ((recordBuffer = recordHeaderFinder.getNext(&foundFilePos)) != NULL)
    {
        evtxRecordHeader = (EVTX_RECORD_HEADER*)recordBuffer;

        if (EvtxRecordValidator::isValidRecordHeader(recordBuffer))
        {
            clusterNumber = foundFilePos / CLUSTER_SIZE;
            fragmentedFile_.readData(clusterNumber * CLUSTER_SIZE, clusterBuffer, CLUSTER_SIZE);

            clusterCrc = update_crc32(0, clusterBuffer, CLUSTER_SIZE);

            if (outClustersHaveRecord.find(clusterNumber) == outClustersHaveRecord.end())
            {
                outClustersHaveRecord.insert(clusterNumber);
            }

            // 레코드와 클러스터를 매핑중 중복된 레코드 아이디가 있으면
            if (outRecordInfoMap.find(evtxRecordHeader->numLogRecord) != outRecordInfoMap.end())
            {
                bool crcFound = false;

                // 클러스터의 crc를 비교하여 없는 crc이면 해당 클러스터를 레코드 아이디의 클러스터 후보군에 추가
                for (clusterIndex = 0; clusterIndex < outRecordInfoMap[evtxRecordHeader->numLogRecord].size(); ++clusterIndex)
                {
                    if (outRecordInfoMap[evtxRecordHeader->numLogRecord][clusterIndex].clusterCrc == clusterCrc)
                    {
                        crcFound = true;
                        break;
                    }
                }

                if (!crcFound)
                {
                    outRecordInfoMap[evtxRecordHeader->numLogRecord].push_back(CLUSTER_INFO(clusterNumber, clusterCrc));
                }
            }
            else
            {
                std::vector<CLUSTER_INFO> clusterInfos;

                clusterInfos.push_back(CLUSTER_INFO(clusterNumber, clusterCrc));
                outRecordInfoMap[evtxRecordHeader->numLogRecord] = clusterInfos;
            }
        }
    }

    recordHeaderFinder.closeMap();
}