/* * $Id$ * Copyright (C) 2010 gingko - http://gingko.homeip.net/ * * This file is part of Pouchin TV Mod, a free DVB-T viewer. * See http://www.pouchintv.fr/ for updates. * * Pouchin TV Mod 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. * * Pouchin TV Mod 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * Most files of this project have been changed from the Pouchin TV * project (Copyright (C) 2006 Pouchin ), * to use with a modified version of this software. * See http://pouchinteve.free.fr/ for the original project. */ /** \file * \brief Implémentation de méthodes permettant l'accès à un fichier en mode "file mapping" * * Ce fichier implémente les méthodes permettant d'accèder à un fichier en mode "file mapping", * dans lequel une partie du contenu du fichier est placée dans une fenêtre de mémoire * dédiée, permettant un accès rapide à son contenu. **/ #include "stdafx.h" #include "mapping.h" /** \brief Constructeur * * Initialisation d'un accès fichier en mode "file mapping" * \param[in] bWrite \p true si fichier ouvert en écriture * \param[in] pszFileName Si != NULL, nom de fichier à ouvrir * \param[in] viewsize Taille de vue souhaitée (ajustée à la valeur de granularité inférieure) **/ FileMapping::FileMapping(bool bWrite, PCTSTR pszFileName, UINT32 viewsize) : hFile(INVALID_HANDLE_VALUE), cMap(bWrite ? PAGE_READWRITE : PAGE_READONLY), cView(bWrite ? FILE_MAP_WRITE : FILE_MAP_READ), dwSysGran(GetSystemGranularity()), dwMaxMapSize(max(viewsize - (viewsize % dwSysGran), 3*dwSysGran)), last_progress(UINT32(-1)), bWriteMode(bWrite), qwFileSize(0), qwLastOffset(0), qwRemSize(0) { if (pszFileName) OpenFile(pszFileName); } /// Obtenir la granularité de l'allocation système UINT32 FileMapping::GetSystemGranularity() { SYSTEM_INFO sSysInfo; GetSystemInfo(&sSysInfo); return sSysInfo.dwAllocationGranularity; } UINT64 FileMapping::GetFileSize() const { LARGE_INTEGER sSize; if (GetFileSizeEx(hFile, &sSize)) return sSize.QuadPart; return 0; } bool FileMapping::OpenFile(PCTSTR pszFileName) { if (bWriteMode) hFile = CreateFile(pszFileName, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, 0); else hFile = CreateFile(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); if (isValid()) { qwFileSize = GetFileSize(); if (qwFileSize > 0) { if (! cMap.Set(hFile, qwFileSize)) CloseFile(); } } return isValid(); } /** \brief Positionnement dans le fichier * \param[in] qwPos Position absolue désirée * \param[in] nNeeded Quantité de mémoire disponible souhaitée dans la vue * \param[out] sMm Descripteur d'intervalle mémoire initialisé à la plus large vue possible pour cette position * \return Le nombre d'octets disponibles (= \p sMm.siz), ou 0 si aucun. * \note Si 0 est retourné, l'intervalle renvoyé dans \p sMm ne sera pas valide. **/ UINT FileMapping::seek(UINT64 qwPos, MemRng & sMm, UINT nNeeded) throw(...) { UINT32 nInViewOffset; if (cView.isInView(qwPos, nNeeded)) nInViewOffset = cView.viewOffset(qwPos); else { if (bWriteMode) { UINT64 nReqFileSize = ((qwPos + nNeeded) | (dwSysGran -1)) +1; if (nReqFileSize > qwFileSize) { UINT64 nNewFileSize = (qwPos & ~UINT64(dwSysGran -1)) +dwMaxMapSize; cView.Unmap(); cMap.Set(hFile, qwFileSize = nNewFileSize); } } UINT64 qwNewViewStart = min(qwPos, qwFileSize); nInViewOffset = UINT(qwNewViewStart % dwSysGran); qwNewViewStart -= nInViewOffset; if (!cView.isValid() || qwNewViewStart != cView.nStart) { if ( !cView.Map(cMap, qwNewViewStart, static_cast(min(qwFileSize-qwNewViewStart, dwMaxMapSize))) ) return 0; // fin de fichier } } sMm = cView.GetRng() + nInViewOffset; qwLastOffset = cView.nStart + nInViewOffset; qwRemSize = qwFileSize - qwLastOffset; return sMm.siz; } /** \brief Réalignement de la vue décalée * * Vérifie que l'intervalle \p sMm est toujours entièrement inclus dans la vue, et si nécessaire, procède * à un réalignement de cette vue afin d'y inclure le maximum possible de l'intervalle. * * Normalement destiné à gérer un avancement progressif dans un fichier en cours de lecture. Utiliser * \p seek pour des déplacements aléatoires ou bien plus importants. * \param[in] sMm Intervalle de mémoire dont on requiert la disponibilité du pointeur initial * \param[in] nNeeded Quantité de mémoire disponible souhaitée dans la vue * \param[out] sMm Intervalle avec origine réajustée, et taille plafonnée si fin de fichier. * \return Le nombre d'octets disponibles, ou 0 si aucun. * \note Si 'sMm.ptr' = NULL en entrée, un repositionnement au début du fichier est effectué (avec \p seek). * \note Si 0 est retourné, l'intervalle renvoyé dans \p sMm ne sera pas valide. **/ UINT FileMapping::remap(MemRng & sMm, UINT nNeeded) throw(...) { if (sMm.ptr==NULL) return seek(0, sMm, nNeeded); // Si aucun pointeur défini, on réinitialise tout UINT64 nNewOffset = cView.nStart; // Si l'intervalle proposé est toujours dans la vue active, on quitte sans rien faire if (cView.isValid()) { nNewOffset += UINT32(sMm.ptr - cView.ptr); if (cView.isInView(nNewOffset, nNeeded)) { qwLastOffset = nNewOffset; qwRemSize = qwFileSize - qwLastOffset; return UINT(cView.nStart+cView.nSize - nNewOffset); } } // Réaligner la vue en plaçant le pointeur proposé au plus proche du début, // compte tenu de la granularité de la fenêtre de vue seek(nNewOffset, sMm, nNeeded); if (qwRemSize < sMm.siz) sMm.siz = (UINT)qwRemSize; return sMm.siz; } /// Obtenir l'offset du fichier correspondant à la position de pPtr dans la "vue". UINT64 FileMapping::fileoffset(PCUINT8 pPtr) const { if (pPtr && cView.isValid()) return cView.nStart+(pPtr-cView.ptr); return cView.nStart; } /// Fonction retournant 1 si 'pPtr' pointe la fin du fichier, -1 si 'pPtr' pointe au-delà /// de la fin du fichier, et 0 si la fin du fichier n'est pas atteinte. int FileMapping::eof(PUINT8 pPtr) const { if (pPtr!=NULL) { UINT64 ofs = fileoffset(pPtr); if (ofs >= qwFileSize) { if (ofs>qwFileSize) return -1; return 1; } } return 0; } bool FileMapping::SetEndOfFile(UINT64 nSize) { if (bWriteMode && isValid()) { LARGE_INTEGER sSize; cView.Unmap(); cMap.Reset(); sSize.QuadPart = nSize; if (SetFilePointerEx(hFile, sSize, NULL, FILE_BEGIN)) { ::SetEndOfFile(hFile); qwFileSize = nSize; return true; } } return false; } bool FileMapping::SetEndOfFile(MemRng sMm) { if (cView.isValid()) return SetEndOfFile(cView.nStart + UINT32(sMm.ptr - cView.ptr)); return false; } void FileMapping::CloseFile() { reset(); cMap.Reset(); if (isValid(hFile)) { CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; } } FileMapping::~FileMapping() { CloseFile(); } bool FileMapping::Mapping::Set(HANDLE hFile, UINT64 qwFileSize) { Reset(); // Obtenir et décomposer la taille du fichier DWORD & dwFileSizeLo = LPDWORD(&qwFileSize)[0]; DWORD & dwFileSizeHi = LPDWORD(&qwFileSize)[1]; hMap = CreateFileMapping(hFile, NULL, flProtect, dwFileSizeHi, dwFileSizeLo, NULL); return isValid(); } void FileMapping::Mapping::Reset() { if (isValid()) { CloseHandle(hMap); hMap = NULL; } } bool FileMapping::View::Map(Mapping & cMap, UINT64 nSt, UINT32 nSz) throw(...) { Unmap(); if (nSz == 0) return false; const DWORD & dwLo = LPDWORD(&nSt)[0]; const DWORD & dwHi = LPDWORD(&nSt)[1]; ptr = (PUINT8)MapViewOfFile(cMap.hMap, dwDesiredAccess, dwHi, dwLo, nSz); if (ptr == NULL) throw GetLastError(); nStart = nSt; nSize = nSz; return true; } void FileMapping::View::Unmap() throw(...) { if (ptr != NULL) { //if ((++nFlushCount & 0x1f) == 0) { if (!FlushViewOfFile(ptr, 0)) throw GetLastError(); //} if (!UnmapViewOfFile(ptr)) throw GetLastError(); ptr = NULL; } nStart = 0; nSize = 0; }