akmenu-next/arm9/source/files.cpp
2024-10-04 22:35:39 -07:00

517 lines
14 KiB
C++

/*
files.cpp
Copyright (C) 2007 Acekard, www.acekard.com
Copyright (C) 2007-2009 somebody
Copyright (C) 2009 yellow wood goblin
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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <nds.h>
#include <string>
#include "files.h"
//#include "dbgtool.h"
#include <elm.h>
#include <sys/iosupport.h>
#include "msgbox.h"
#include "progresswnd.h"
#include <errno.h>
#include <cstdio>
#include <unistd.h>
#include "language.h"
#include "datetime.h"
#include "dsrom.h"
#include "favorites.h"
#define USE_OPEN
#ifdef USE_OPEN
#include <fcntl.h>
#endif
using namespace akui;
static SRC_FILE_MODE _srcFileMode = SFM_COPY;
static std::string _srcFilename = "";
bool loadFile( void * buffer, const std::string & filename, size_t offset, size_t & readCount )
{
if( "" == filename )
return false;
if( NULL == buffer ) {
dbg_printf("invalid buffer pointer\n");
struct stat st;
stat( filename.c_str(), &st );
readCount = st.st_size;
return false;
}
FILE * f = fopen( filename.c_str(), "rb" );
if( NULL == f ) {
dbg_printf("file does not exist\n");
readCount = 0;
return false;
}
fseek( f, 0, SEEK_END );
int fileSize = ftell( f );
if( -1 == fileSize ) {
fclose( f );
readCount = 0;
return false;
}
fseek( f, offset, SEEK_SET );
size_t readed = fread( buffer, 1, fileSize, f );
fclose( f );
readCount = readed;
if( readed != (size_t)fileSize-offset ) {
dbg_printf("fread fail: %d/%d\n", readed, fileSize );
readCount = 0;
return false;
}
return true;
}
int getFileSize( const std::string & filename )
{
if( "" == filename )
return -1;
struct stat st;
if( -1 == stat( filename.c_str(), &st ) ) {
return -1;
}
return st.st_size;
}
//---------------------------------------------------------------------------------//
bool stopCopying = false;
bool copyingFile = false;
bool copyFile( const std::string & srcFilename, const std::string & destFilename, bool silently, size_t copyLength )
{
dbg_printf("copy %s to %s\n", srcFilename.c_str(), destFilename.c_str() );
struct stat srcSt;
if( 0 != stat( srcFilename.c_str(), &srcSt ) ) {
messageBox( NULL, LANG("copy file error","title"), LANG("copy file error","text"), MB_OK );
return false;
}
u64 total = 0;
u64 used = 0;
u64 freeSpace = 0;
std::string destDiskName = destFilename.substr( 0, 6 );
if( destDiskName != "fat0:/" && destDiskName != "fat1:/" )
return false;
if( !getDiskSpaceInfo( destDiskName, total, used, freeSpace ) ) {
messageBox( NULL, LANG("no free space","title"), LANG("no free space","text"), MB_OK );
return false;
}
if( 0 == copyLength || copyLength > (size_t)srcSt.st_size )
copyLength = srcSt.st_size;
if( freeSpace < copyLength ) {
messageBox( NULL, LANG("no free space","title"), LANG("no free space","text"), MB_OK );
return false;
}
if( !silently ) {
struct stat destSt;
if( 0 == stat( destFilename.c_str(), &destSt ) ) {
if( !( destSt.st_mode & S_IFDIR ) ) {
u32 ret = messageBox( NULL, LANG("copy file exists","title"),
LANG("copy file exists","text"), MB_YES | MB_NO );
if( ret != ID_YES )
return false;
} else {
messageBox( NULL, LANG("copy dest is directory","title"),
LANG("copy dest is directory","text"), MB_CANCEL );
return false;
}
}
}
u32 copyBufferSize=gs().CopyBufferSize();
u8* copyBuffer=new(std::nothrow)u8[copyBufferSize];
if(copyBuffer==NULL)
{
messageBox(NULL,LANG("ram allocation","title"),LANG("ram allocation","memory allocation error"),MB_OK);
return false;
}
std::string tempText = LANG("progress window", "processing copy");
std::string copyTip = formatString( tempText.c_str(), (char)0x02 );
progressWnd().setTipText( copyTip );
progressWnd().show();
progressWnd().setPercent( 0 );
stopCopying = false;
copyingFile = true;
NandFast();
#ifdef USE_OPEN
int wf=open(destFilename.c_str(),O_WRONLY|O_CREAT|O_TRUNC);
int rf=open(srcFilename.c_str(),O_RDONLY);
#else
FILE * wf = fopen( destFilename.c_str(), "wb" );
FILE * rf = fopen( srcFilename.c_str(), "rb" );
#endif
u8 percent = 0;
dbg_printf("start: %s", datetime().getTimeString().c_str() );
u32 writeCount = copyLength / copyBufferSize;
if( copyLength % copyBufferSize )
writeCount++;
dbg_printf("write count %d\n", writeCount );
u32 remain = copyLength;
for( size_t i = 0; i < writeCount; ++i ) {
if( stopCopying ) {
copyingFile = false;
u32 ret = messageBox( &progressWnd(), LANG("stop copying file","title"),
LANG("stop copying file","text"), MB_YES | MB_NO );
if( ID_YES == ret ) {
#ifdef USE_OPEN
close(rf);
close(wf);
#else
fclose( rf );
fclose( wf );
#endif
NandFlush();
progressWnd().hide();
copyingFile = false;
delete[] copyBuffer;
return false;
}
copyingFile = true;
stopCopying = false;
}
u32 toRead = remain > copyBufferSize ? copyBufferSize : remain;
#ifdef USE_OPEN
ssize_t readed=read(rf,copyBuffer,toRead);
ssize_t written=write(wf,copyBuffer,readed);
#else
u32 readed = fread( copyBuffer, 1, toRead, rf );
u32 written = fwrite( copyBuffer, 1, (int)readed, wf );
#endif
if( written != readed ) {
dbg_printf("err %d\n", errno );
dbg_printf("COPY FILE ERROR! %d/%d\n", readed, written );
// todo: judge error types in errno
#ifdef USE_OPEN
close(rf);
close(wf);
#else
fclose( rf );
fclose( wf );
#endif
NandFlush();
progressWnd().hide();
copyingFile = false;
delete[] copyBuffer;
messageBox( NULL, LANG("no free space","title"), LANG("no free space","text"), MB_OK );
return false;
}
remain -= written;
percent = i * 100 / writeCount;
progressWnd().setPercent( percent );
}
#ifdef USE_OPEN
close(rf);
close(wf);
#else
fclose( rf );
fclose( wf );
#endif
NandFlush();
progressWnd().hide();
copyingFile = false;
delete[] copyBuffer;
dbg_printf("finish: %s", datetime().getTimeString().c_str());
return true;
}
bool renameFile( const std::string & oldName, const std::string & newName )
{
if( "" == oldName || "" == newName )
return false;
struct stat destSt;
if( 0 == stat( newName.c_str(), &destSt ) ) {
if( !( destSt.st_mode & S_IFDIR ) ) {
u32 ret = messageBox( NULL, LANG("copy file exists","title"),
LANG("copy file exists","text"), MB_YES | MB_NO );
if( ret != ID_YES )
return false;
} else {
messageBox( NULL, LANG("move dest is directory","title"),
LANG("move dest is directory","text"), MB_CANCEL );
return false;
}
}
if( 0 != rename( oldName.c_str(), newName.c_str() ) ) {
if( EEXIST == errno || EXDEV == errno ) {
bool success = copyFile( oldName, newName, true );
if( success ) {
unlink( oldName.c_str() );
return true;
} else {
unlink( newName.c_str() );
return false;
}
}
return false;
}
return true;
}
bool deleteFile( const std::string & filename )
{
if( "" == filename )
return false;
struct stat destSt;
if( 0 != stat( filename.c_str(), &destSt ) ) {
return false;
}
std::string confirmText = LANG("confirm delete file","text");
std::string showname,realname;
if( '/' == filename[filename.size()-1] )
showname = filename.substr( 0, filename.size() - 1 );
else
showname = filename;
realname = showname;
size_t slashPos = showname.find_last_of( '/' );
if( showname.npos != slashPos )
showname = showname.substr( slashPos + 1 );
confirmText = formatString( confirmText.c_str(), showname.c_str() );
u32 result = messageBox( NULL, LANG("confirm delete file","title"), confirmText.c_str(), MB_YES | MB_NO );
if( result != ID_YES ) {
return false;
}
int ret = unlink( realname.c_str() );
if( 0 != ret ) {
if( EACCES == errno ) {
messageBox( NULL, LANG("do not delete directory","title"), LANG("do not delete directory","text"), MB_OK );
}
return false;
}
cFavorites::RemoveFromFavorites(filename);
return true;
}
void setSrcFile( const std::string & filename, SRC_FILE_MODE mode )
{
_srcFilename = filename;
_srcFileMode = mode;
}
const std::string& getSrcFile(void)
{
return _srcFilename;
}
bool copyOrMoveFile( const std::string & destDir )
{
if( "" == _srcFilename )
return false;
const char * pPath = _srcFilename.c_str();
const char * pName = NULL;
while( pPath < _srcFilename.c_str() + _srcFilename.size() )
{
if( '/' == *pPath++ )
pName = pPath;
}
if( 0 == *pName )
return false;
std::string destPath = destDir + pName;
if( destPath == _srcFilename )
return false;
if( SFM_COPY == _srcFileMode )
{
u32 copyLength = 0;
if( gs().romTrim ) {
std::string extName;
size_t lastDotPos = _srcFilename.find_last_of( '.' );
if( _srcFilename.npos != lastDotPos )
extName = _srcFilename.substr( lastDotPos );
else
extName = "";
for( size_t i = 0; i < extName.size(); ++i )
extName[i] = tolower( extName[i] );
if( ".nds" == extName ) {
DSRomInfo info;
info.MayBeDSRom(_srcFilename);
if( info.isDSRom() && !info.isHomebrew() ) {
FILE * f = fopen( _srcFilename.c_str(), "rb" );
fseek( f, 0x80, SEEK_SET );
fread( &copyLength, 1, 4, f );
fclose(f);
copyLength += 0x88; // to keep RSA signature
}
}
}
bool copyOK = copyFile( _srcFilename, destPath, false, copyLength );
if( copyOK ) {
_srcFilename = "";
}
return copyOK;
}
if( SFM_CUT == _srcFileMode )
{
bool moveOK = renameFile( _srcFilename, destPath );
if( moveOK ) {
cFavorites::UpdateFavorites(_srcFilename,destPath);
_srcFilename = "";
}
return moveOK;
}
return false;
}
bool getDirSize( const std::string & path, bool includeSubdirs, u64 * dirSize )
{
if( "" == path )
return false;
u64 size = 0;
std::string dirPath = path;
if( dirPath[dirPath.size()-1] != '/' )
dirPath += "/";
if( dirPath.size() > MAX_FILENAME_LENGTH )
return false;
DIR_ITER * dir = NULL;
dir = diropen(dirPath.c_str());
if (dir == NULL) {
//dbg_printf("getDirSize couldn't open dir %s", path.c_str() );
return false;
}
size = 0;
char filename[MAX_FILENAME_LENGTH];
struct stat stat_buf;
while (dirnext(dir, filename, &stat_buf) == 0) {
if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) {
continue;
}
//dbg_printf("getDirSize dir entry '%s'", path.c_str());
if (!(stat_buf.st_mode & S_IFDIR)) {
//dbg_printf("getDirSize add size %d for '%s'", (int)stat_buf.st_size, path.c_str());
size += (u64)stat_buf.st_size;
} else if (includeSubdirs) {
/* calculate the size recursively */
u64 subDirSize = 0;
bool succ = getDirSize( dirPath + filename, includeSubdirs, &subDirSize );
/* ignore failure in subdirs */
if( succ ) {
size += subDirSize;
}
}
}
dirclose(dir);
*dirSize = size;
return true;
}
static bool getDiskFromString(const std::string& diskName,u32& disk)
{
if(tolower(diskName[0])=='f'&&tolower(diskName[1])=='a'&&tolower(diskName[2])=='t'&&(diskName[3]=='0'||diskName[3]=='1')&&diskName[4]==':')
{
disk=diskName[3]-'0';
return true;
}
return false;
}
static bool getDiskTotalSpace(u32 disk,u64& diskSpace)
{
u32 clusters,clusterSize;
if(ELM_ClusterSizeFromDisk(disk,&clusterSize)&&ELM_ClustersFromDisk(disk,&clusters))
{
diskSpace=(u64)clusters*(u64)clusterSize;
return true;
}
return false;
}
static bool getDiskFreeSpace(u32 disk,u64& freeSpace)
{
u32 clusters,clusterSize;
if(ELM_ClusterSizeFromDisk(disk,&clusterSize)&&ELM_FreeClustersFromDisk(disk,&clusters))
{
freeSpace=(u64)clusters*(u64)clusterSize;
return true;
}
return false;
}
bool getDiskSpaceInfo(const std::string& diskName,u64& total,u64& used,u64& freeSpace)
{
if(""==diskName) return false;
u32 disk;
if(!getDiskFromString(diskName,disk))
{
cwl();
return false;
}
if(!getDiskTotalSpace(disk,total))
{
cwl();
return false;
}
if(!getDiskFreeSpace(disk,freeSpace))
{
cwl();
return false;
}
cwl();
used=total-freeSpace;
return true;
}