1 /*
2  *  Copyright (C) 2002-2015  The DOSBox Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  *  Wengier: LFN support
19  */
20 
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 #include <errno.h>
27 
28 #ifdef VITA
29 #include <psp2/io/stat.h>
30 #include <psp2/io/fcntl.h>
31 #endif
32 
33 #include "dosbox.h"
34 #include "dos_inc.h"
35 #include "drives.h"
36 #include "support.h"
37 #include "cross.h"
38 #include "inout.h"
39 
40 class localFile : public DOS_File {
41 public:
42 	localFile(const char* name, FILE * handle);
43 	bool Read(Bit8u * data,Bit16u * size);
44 	bool Write(Bit8u * data,Bit16u * size);
45 	bool Seek(Bit32u * pos,Bit32u type);
46 	bool Close();
47 	Bit16u GetInformation(void);
48 	bool UpdateDateTimeFromHost(void);
49 	void FlagReadOnlyMedium(void);
50 	void Flush(void);
51 private:
52 	FILE * fhandle;
53 	bool read_only_medium;
54 	enum { NONE,READ,WRITE } last_action;
55 };
56 
57 
FileCreate(DOS_File ** file,char * name,Bit16u)58 bool localDrive::FileCreate(DOS_File * * file,char * name,Bit16u /*attributes*/) {
59 //TODO Maybe care for attributes but not likely
60 	char newname[CROSS_LEN];
61 	strcpy(newname,basedir);
62 	strcat(newname,name);
63 	CROSS_FILENAME(newname);
64 	char* temp_name = dirCache.GetExpandName(newname); //Can only be used in till a new drive_cache action is preformed */
65 	/* Test if file exists (so we need to truncate it). don't add to dirCache then */
66 	bool existing_file=false;
67 
68 	FILE * test=fopen(temp_name,"rb+");
69 	if(test) {
70 		fclose(test);
71 		existing_file=true;
72 
73 	}
74 
75 	FILE * hand=fopen(temp_name,"wb+");
76 	if (!hand){
77 		LOG_MSG("Warning: file creation failed: %s",newname);
78 		return false;
79 	}
80 
81 	if(!existing_file) dirCache.AddEntry(newname, true);
82 	/* Make the 16 bit device information */
83 	*file=new localFile(name,hand);
84 	(*file)->flags=OPEN_READWRITE;
85 
86 	return true;
87 }
88 
FileOpen(DOS_File ** file,char * name,Bit32u flags)89 bool localDrive::FileOpen(DOS_File * * file,char * name,Bit32u flags) {
90 	const char* type;
91 	switch (flags&0xf) {
92 	case OPEN_READ:        type = "rb" ; break;
93 	case OPEN_WRITE:       type = "rb+"; break;
94 	case OPEN_READWRITE:   type = "rb+"; break;
95 	case OPEN_READ_NO_MOD: type = "rb" ; break; //No modification of dates. LORD4.07 uses this
96 	default:
97 		DOS_SetError(DOSERR_ACCESS_CODE_INVALID);
98 		return false;
99 	}
100 	char newname[CROSS_LEN];
101 	strcpy(newname,basedir);
102 	strcat(newname,name);
103 	CROSS_FILENAME(newname);
104 	dirCache.ExpandName(newname);
105 
106 	//Flush the buffer of handles for the same file. (Betrayal in Antara)
107 	Bit8u i,drive=DOS_DRIVES;
108 	localFile *lfp;
109 	for (i=0;i<DOS_DRIVES;i++) {
110 		if (Drives[i]==this) {
111 			drive=i;
112 			break;
113 		}
114 	}
115 	for (i=0;i<DOS_FILES;i++) {
116 		if (Files[i] && Files[i]->IsOpen() && Files[i]->GetDrive()==drive && Files[i]->IsName(name)) {
117 			lfp=dynamic_cast<localFile*>(Files[i]);
118 			if (lfp) lfp->Flush();
119 		}
120 	}
121 
122 	FILE * hand=fopen(newname,type);
123 //	Bit32u err=errno;
124 	if (!hand) {
125 		if((flags&0xf) != OPEN_READ) {
126 			FILE * hmm=fopen(newname,"rb");
127 			if (hmm) {
128 				fclose(hmm);
129 				LOG_MSG("Warning: file %s exists and failed to open in write mode.\nPlease Remove write-protection",newname);
130 			}
131 		}
132 		return false;
133 	}
134 
135 	*file=new localFile(name,hand);
136 	(*file)->flags=flags;  //for the inheritance flag and maybe check for others.
137 //	(*file)->SetFileName(newname);
138 	return true;
139 }
140 
GetSystemFilePtr(char const * const name,char const * const type)141 FILE * localDrive::GetSystemFilePtr(char const * const name, char const * const type) {
142 
143 	char newname[CROSS_LEN];
144 	strcpy(newname,basedir);
145 	strcat(newname,name);
146 	CROSS_FILENAME(newname);
147 	dirCache.ExpandName(newname);
148 
149 	return fopen(newname,type);
150 }
151 
GetSystemFilename(char * sysName,char const * const dosName)152 bool localDrive::GetSystemFilename(char *sysName, char const * const dosName) {
153 
154 	strcpy(sysName, basedir);
155 	strcat(sysName, dosName);
156 	CROSS_FILENAME(sysName);
157 	dirCache.ExpandName(sysName);
158 	return true;
159 }
160 
FileUnlink(char * name)161 bool localDrive::FileUnlink(char * name) {
162 	char newname[CROSS_LEN];
163 	strcpy(newname,basedir);
164 	strcat(newname,name);
165 	CROSS_FILENAME(newname);
166 	char *fullname = dirCache.GetExpandName(newname);
167 	if (unlink(fullname)) {
168 		//Unlink failed for some reason try finding it.
169 		struct stat buffer;
170 		if(stat(fullname,&buffer)) return false; // File not found.
171 
172 		FILE* file_writable = fopen(fullname,"rb+");
173 		if(!file_writable) return false; //No acces ? ERROR MESSAGE NOT SET. FIXME ?
174 		fclose(file_writable);
175 
176 		//File exists and can technically be deleted, nevertheless it failed.
177 		//This means that the file is probably open by some process.
178 		//See if We have it open.
179 		bool found_file = false;
180 		for(Bitu i = 0;i < DOS_FILES;i++){
181 			if(Files[i] && Files[i]->IsName(name)) {
182 				Bitu max = DOS_FILES;
183 				while(Files[i]->IsOpen() && max--) {
184 					Files[i]->Close();
185 					if (Files[i]->RemoveRef()<=0) break;
186 				}
187 				found_file=true;
188 			}
189 		}
190 		if(!found_file) return false;
191 		if (!unlink(fullname)) {
192 			dirCache.DeleteEntry(newname);
193 			return true;
194 		}
195 		return false;
196 	} else {
197 		dirCache.DeleteEntry(newname);
198 		return true;
199 	}
200 }
201 
FindFirst(char * _dir,DOS_DTA & dta,bool fcb_findfirst)202 bool localDrive::FindFirst(char * _dir,DOS_DTA & dta,bool fcb_findfirst) {
203 	char tempDir[CROSS_LEN];
204 	strcpy(tempDir,basedir);
205 	strcat(tempDir,_dir);
206 	CROSS_FILENAME(tempDir);
207 
208 	for (unsigned int i=0;i<strlen(tempDir);i++) tempDir[i]=toupper(tempDir[i]);
209 	if (allocation.mediaid==0xF0 ) {
210 		EmptyCache(); //rescan floppie-content on each findfirst
211 	}
212 
213 	char end[2]={CROSS_FILESPLIT,0};
214 	if (tempDir[strlen(tempDir)-1]!=CROSS_FILESPLIT) strcat(tempDir,end);
215 
216 	Bit16u id;
217 	if (!dirCache.FindFirst(tempDir,id)) {
218 		DOS_SetError(DOSERR_PATH_NOT_FOUND);
219 		return false;
220 	}
221 
222 	strcpy(srchInfo[id].srch_dir,tempDir);
223 	dta.SetDirID(id);
224 
225 	Bit8u sAttr;
226 	dta.GetSearchParams(sAttr,tempDir,true);
227 
228 	if (this->isRemote() && this->isRemovable()) {
229 		// cdroms behave a bit different than regular drives
230 		if (sAttr == DOS_ATTR_VOLUME) {
231 			dta.SetResult(dirCache.GetLabel(),dirCache.GetLabel(),0,0,0,DOS_ATTR_VOLUME);
232 			return true;
233 		}
234 	} else {
235 		if (sAttr == DOS_ATTR_VOLUME) {
236 			if ( strcmp(dirCache.GetLabel(), "") == 0 ) {
237 //				LOG(LOG_DOSMISC,LOG_ERROR)("DRIVELABEL REQUESTED: none present, returned  NOLABEL");
238 //				dta.SetResult("NO_LABEL",0,0,0,DOS_ATTR_VOLUME);
239 //				return true;
240 				DOS_SetError(DOSERR_NO_MORE_FILES);
241 				return false;
242 			}
243 			dta.SetResult(dirCache.GetLabel(),dirCache.GetLabel(),0,0,0,DOS_ATTR_VOLUME);
244 			return true;
245 		} else if ((sAttr & DOS_ATTR_VOLUME)  && (*_dir == 0) && !fcb_findfirst) {
246 		//should check for a valid leading directory instead of 0
247 		//exists==true if the volume label matches the searchmask and the path is valid
248 			if (WildFileCmp(dirCache.GetLabel(),tempDir)) {
249 				dta.SetResult(dirCache.GetLabel(),dirCache.GetLabel(),0,0,0,DOS_ATTR_VOLUME);
250 				return true;
251 			}
252 		}
253 	}
254 	return FindNext(dta);
255 }
256 
FindNext(DOS_DTA & dta)257 bool localDrive::FindNext(DOS_DTA & dta) {
258 
259 	char *dir_ent, *ldir_ent;
260 	struct stat stat_block;
261 	char full_name[CROSS_LEN];
262 	char dir_entcopy[CROSS_LEN], ldir_entcopy[CROSS_LEN];
263 
264 	Bit8u srch_attr;char srch_pattern[LFN_NAMELENGTH+1];
265 	Bit8u find_attr;
266 
267 	dta.GetSearchParams(srch_attr,srch_pattern,true);
268 	Bit16u id = dta.GetDirID();
269 
270 again:
271 	if (!dirCache.FindNext(id,dir_ent,ldir_ent)) {
272 		DOS_SetError(DOSERR_NO_MORE_FILES);
273 		return false;
274 	}
275 	if(!WildFileCmp(dir_ent,srch_pattern)&&!LWildFileCmp(ldir_ent,srch_pattern)) goto again;
276 
277 	strcpy(full_name,srchInfo[id].srch_dir);
278 	strcat(full_name,dir_ent);
279 
280 	//GetExpandName might indirectly destroy dir_ent (by caching in a new directory
281 	//and due to its design dir_ent might be lost.)
282 	//Copying dir_ent first
283 	strcpy(dir_entcopy,dir_ent);
284 	strcpy(ldir_entcopy,ldir_ent);
285 	if (stat(dirCache.GetExpandName(full_name),&stat_block)!=0) {
286 		goto again;//No symlinks and such
287 	}
288 
289 	if(stat_block.st_mode & S_IFDIR) find_attr=DOS_ATTR_DIRECTORY;
290 	else find_attr=DOS_ATTR_ARCHIVE;
291  	if (~srch_attr & find_attr & (DOS_ATTR_DIRECTORY | DOS_ATTR_HIDDEN | DOS_ATTR_SYSTEM)) goto again;
292 
293 	/*file is okay, setup everything to be copied in DTA Block */
294 	char find_name[DOS_NAMELENGTH_ASCII], *lfind_name=ldir_ent;
295 	Bit16u find_date,find_time;Bit32u find_size;
296 
297 	if(strlen(dir_entcopy)<DOS_NAMELENGTH_ASCII){
298 		strcpy(find_name,dir_entcopy);
299 		upcase(find_name);
300 	}
301 	lfind_name[LFN_NAMELENGTH]=0;
302 
303 	find_size=(Bit32u) stat_block.st_size;
304 	struct tm *time;
305 	if((time=localtime((const time_t*)&stat_block.st_mtime))!=0){
306 		find_date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday);
307 		find_time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec);
308 	} else {
309 		find_time=6;
310 		find_date=4;
311 	}
312 	dta.SetResult(find_name,lfind_name,find_size,find_date,find_time,find_attr);
313 	return true;
314 }
315 
GetFileAttr(char * name,Bit16u * attr)316 bool localDrive::GetFileAttr(char * name,Bit16u * attr) {
317 	char newname[CROSS_LEN];
318 	strcpy(newname,basedir);
319 	strcat(newname,name);
320 	CROSS_FILENAME(newname);
321 	dirCache.ExpandName(newname);
322 
323 	struct stat status;
324 	if (stat(newname,&status)==0) {
325 		*attr=DOS_ATTR_ARCHIVE;
326 		if(status.st_mode & S_IFDIR) *attr|=DOS_ATTR_DIRECTORY;
327 		return true;
328 	}
329 	*attr=0;
330 	return false;
331 }
332 
GetFileAttrEx(char * name,struct stat * status)333 bool localDrive::GetFileAttrEx(char* name, struct stat *status) {
334 	char newname[CROSS_LEN];
335 	strcpy(newname,basedir);
336 	strcat(newname,name);
337 	CROSS_FILENAME(newname);
338 	dirCache.ExpandName(newname);
339 	return !stat(newname,status);
340 }
341 
GetCompressedSize(char * name)342 Bit32u localDrive::GetCompressedSize(char* name)
343 	{
344    //win32 is not special
345    return 0;
346       /*
347 #if !defined (WIN32)
348 	return 0;
349 #else
350 	char newname[CROSS_LEN];
351 	strcpy(newname,basedir);
352 	strcat(newname,name);
353 	CROSS_FILENAME(newname);
354 	dirCache.ExpandName(newname);
355 	Bit32u size = GetCompressedFileSize(newname, NULL);
356 	if (size != INVALID_FILE_SIZE) {
357 		if (size != 0 && size == GetFileSize(newname, NULL)) {
358 			Bit32u sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters;
359 			if (GetDiskFreeSpace(newname, &sectors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters)) {
360 				size = ((size - 1) | (sectors_per_cluster * bytes_per_sector - 1)) + 1;
361 			}
362 		}
363 		return size;
364 	} else {
365 		DOS_SetError((Bit16u)GetLastError());
366 		return -1;
367 	}
368 #endif
369       */
370 }
371 
CreateOpenFile(const char * name)372 void* localDrive::CreateOpenFile(const char* name)
373 	{
374 	char newname[CROSS_LEN];
375 	strcpy(newname,basedir);
376 	strcat(newname,name);
377 	CROSS_FILENAME(newname);
378 	dirCache.ExpandName(newname);
379    //win32 is not special
380    return NULL;
381       /*
382 #if defined (WIN32)
383 	HANDLE handle=CreateFile(newname, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
384 	if (handle==INVALID_HANDLE_VALUE)
385 		DOS_SetError((Bit16u)GetLastError());
386 	return handle;
387 #else
388 	return NULL;
389 #endif
390       */
391 }
392 
MakeDir(char * dir)393 bool localDrive::MakeDir(char * dir) {
394 	char newdir[CROSS_LEN];
395 	strcpy(newdir,basedir);
396 	strcat(newdir,dir);
397 	CROSS_FILENAME(newdir);
398 #if defined (WIN32)						/* MS Visual C++ */
399 	int temp=mkdir(dirCache.GetExpandName(newdir));
400 #elif defined(VITA)
401    int temp=sceIoMkdir(dirCache.GetExpandName(newdir), 0777);
402 #else
403 	int temp=mkdir(dirCache.GetExpandName(newdir),0700);
404 #endif
405 	if (temp==0) dirCache.CacheOut(newdir,true);
406 
407 	return (temp==0);// || ((temp!=0) && (errno==EEXIST));
408 }
409 
RemoveDir(char * dir)410 bool localDrive::RemoveDir(char * dir) {
411 	char newdir[CROSS_LEN];
412 	strcpy(newdir,basedir);
413 	strcat(newdir,dir);
414 	CROSS_FILENAME(newdir);
415 #if defined (VITA)
416    int temp=sceIoRmdir(dirCache.GetExpandName(newdir));
417 #else
418    int temp=rmdir(dirCache.GetExpandName(newdir));
419 #endif
420 	if (temp==0) dirCache.DeleteEntry(newdir,true);
421 	return (temp==0);
422 }
423 
424 #ifdef WII
access(const char * file,int type)425 int access (const char *file, int type)
426 {
427 struct stat stbuf;
428 gid_t gid;
429 uid_t uid;
430 
431 if (file == NULL || (type & ~(R_OK|W_OK|X_OK|F_OK)) != 0) {
432 	errno = EINVAL;
433 	return -1;
434 }
435 if(stat(file, &stbuf) == -1)
436 	return -1;
437 
438 // No getgid() and getuid()? Well, we are God!
439 
440 // gid = getgid();
441 // uid = getuid();
442 uid = stbuf.st_uid;
443 gid = stbuf.st_gid;
444 
445 if(uid == stbuf.st_uid) {
446 	if( ((type & R_OK) && !(stbuf.st_mode & S_IRUSR) ) ||
447 		((type & W_OK) && !(stbuf.st_mode & S_IWUSR) ) ||
448 		((type & X_OK) && !(stbuf.st_mode & S_IXUSR) ) ) {
449 		errno = EACCES;
450 		return -1;
451 	}
452 }
453 else if(gid == stbuf.st_gid) {
454 	if( ((type & R_OK) && !(stbuf.st_mode & S_IRGRP) ) ||
455 		((type & W_OK) && !(stbuf.st_mode & S_IWGRP) ) ||
456 		((type & X_OK) && !(stbuf.st_mode & S_IXGRP) ) ) {
457 		errno = EACCES;
458 		return -1;
459 	}
460 }
461 else {
462 	if( ((type & R_OK) && !(stbuf.st_mode & S_IROTH) ) ||
463 		((type & W_OK) && !(stbuf.st_mode & S_IWOTH) ) ||
464 		((type & X_OK) && !(stbuf.st_mode & S_IXOTH) ) ) {
465 		errno = EACCES;
466 		return -1;
467 	}
468 }
469 
470 return 0;
471 
472 }
473 #endif
474 
TestDir(char * dir)475 bool localDrive::TestDir(char * dir) {
476 	char newdir[CROSS_LEN];
477 	strcpy(newdir,basedir);
478 	strcat(newdir,dir);
479 	CROSS_FILENAME(newdir);
480 	dirCache.ExpandName(newdir);
481 	// Skip directory test, if "\"
482 	size_t len = strlen(newdir);
483 	if (len && (newdir[len-1]!='\\')) {
484 		// It has to be a directory !
485 		struct stat test;
486 		if (stat(newdir,&test))			return false;
487 		if ((test.st_mode & S_IFDIR)==0)	return false;
488 	};
489 	int temp=access(newdir,F_OK);
490 	return (temp==0);
491 }
492 
Rename(char * oldname,char * newname)493 bool localDrive::Rename(char * oldname,char * newname) {
494 	char newold[CROSS_LEN];
495 	strcpy(newold,basedir);
496 	strcat(newold,oldname);
497 	CROSS_FILENAME(newold);
498 	dirCache.ExpandName(newold);
499 
500 	char newnew[CROSS_LEN];
501 	strcpy(newnew,basedir);
502 	strcat(newnew,newname);
503 	CROSS_FILENAME(newnew);
504 #ifdef VITA
505    int temp=sceIoRename(newold,dirCache.GetExpandName(newnew));
506 #else
507    int temp=rename(newold,dirCache.GetExpandName(newnew));
508 #endif
509 	if (temp==0) dirCache.CacheOut(newnew);
510 	return (temp==0);
511 
512 }
513 
AllocationInfo(Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters,Bit16u * _free_clusters)514 bool localDrive::AllocationInfo(Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters,Bit16u * _free_clusters) {
515 	*_bytes_sector=allocation.bytes_sector;
516 	*_sectors_cluster=allocation.sectors_cluster;
517 	*_total_clusters=allocation.total_clusters;
518 	*_free_clusters=allocation.free_clusters;
519 	return true;
520 }
521 
FileExists(const char * name)522 bool localDrive::FileExists(const char* name) {
523 	char newname[CROSS_LEN];
524 	strcpy(newname,basedir);
525 	strcat(newname,name);
526 	CROSS_FILENAME(newname);
527 	dirCache.ExpandName(newname);
528 	struct stat temp_stat;
529 	if(stat(newname,&temp_stat)!=0) return false;
530 	if(temp_stat.st_mode & S_IFDIR) return false;
531 	return true;
532 }
533 
FileStat(const char * name,FileStat_Block * const stat_block)534 bool localDrive::FileStat(const char* name, FileStat_Block * const stat_block) {
535 	char newname[CROSS_LEN];
536 	strcpy(newname,basedir);
537 	strcat(newname,name);
538 	CROSS_FILENAME(newname);
539 	dirCache.ExpandName(newname);
540 	struct stat temp_stat;
541 	if(stat(newname,&temp_stat)!=0) return false;
542 	/* Convert the stat to a FileStat */
543 	struct tm *time;
544 	if((time=localtime((const time_t*)&temp_stat.st_mtime))!=0) {
545 		stat_block->time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec);
546 		stat_block->date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday);
547 	} else {
548 
549 	}
550 	stat_block->size=(Bit32u)temp_stat.st_size;
551 	return true;
552 }
553 
554 
GetMediaByte(void)555 Bit8u localDrive::GetMediaByte(void) {
556 	return allocation.mediaid;
557 }
558 
isRemote(void)559 bool localDrive::isRemote(void) {
560 	return false;
561 }
562 
isRemovable(void)563 bool localDrive::isRemovable(void) {
564 	return false;
565 }
566 
UnMount(void)567 Bits localDrive::UnMount(void) {
568 	delete this;
569 	return 0;
570 }
571 
localDrive(const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid)572 localDrive::localDrive(const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid) {
573 	strcpy(basedir,startdir);
574 	sprintf(info,"local directory %s",startdir);
575 	allocation.bytes_sector=_bytes_sector;
576 	allocation.sectors_cluster=_sectors_cluster;
577 	allocation.total_clusters=_total_clusters;
578 	allocation.free_clusters=_free_clusters;
579 	allocation.mediaid=_mediaid;
580 
581 	dirCache.SetBaseDir(basedir);
582 }
583 
584 
585 //TODO Maybe use fflush, but that seemed to fuck up in visual c
Read(Bit8u * data,Bit16u * size)586 bool localFile::Read(Bit8u * data,Bit16u * size) {
587 	if ((this->flags & 0xf) == OPEN_WRITE) {	// check if file opened in write-only mode
588 		DOS_SetError(DOSERR_ACCESS_DENIED);
589 		return false;
590 	}
591 	if (last_action==WRITE) fseek(fhandle,ftell(fhandle),SEEK_SET);
592 	last_action=READ;
593 	*size=(Bit16u)fread(data,1,*size,fhandle);
594 	/* Fake harddrive motion. Inspector Gadget with soundblaster compatible */
595 	/* Same for Igor */
596 	/* hardrive motion => unmask irq 2. Only do it when it's masked as unmasking is realitively heavy to emulate */
597 	Bit8u mask = IO_Read(0x21);
598 	if(mask & 0x4 ) IO_Write(0x21,mask&0xfb);
599 	return true;
600 }
601 
Write(Bit8u * data,Bit16u * size)602 bool localFile::Write(Bit8u * data,Bit16u * size) {
603 	if ((this->flags & 0xf) == OPEN_READ) {	// check if file opened in read-only mode
604 		DOS_SetError(DOSERR_ACCESS_DENIED);
605 		return false;
606 	}
607 	if (last_action==READ) fseek(fhandle,ftell(fhandle),SEEK_SET);
608 	last_action=WRITE;
609 	if(*size==0){
610 #ifdef VITA
611          return true;
612 #else
613          return (!ftruncate(fileno(fhandle),ftell(fhandle)));
614 #endif
615     }
616     else
617     {
618 		*size=(Bit16u)fwrite(data,1,*size,fhandle);
619 		return true;
620     }
621 }
622 
Seek(Bit32u * pos,Bit32u type)623 bool localFile::Seek(Bit32u * pos,Bit32u type) {
624 	int seektype;
625 	switch (type) {
626 	case DOS_SEEK_SET:seektype=SEEK_SET;break;
627 	case DOS_SEEK_CUR:seektype=SEEK_CUR;break;
628 	case DOS_SEEK_END:seektype=SEEK_END;break;
629 	default:
630 	//TODO Give some doserrorcode;
631 		return false;//ERROR
632 	}
633 	int ret=fseek(fhandle,*reinterpret_cast<Bit32s*>(pos),seektype);
634 	if (ret!=0) {
635 		// Out of file range, pretend everythings ok
636 		// and move file pointer top end of file... ?! (Black Thorne)
637 		fseek(fhandle,0,SEEK_END);
638 	};
639 #if 0
640 	fpos_t temppos;
641 	fgetpos(fhandle,&temppos);
642 	Bit32u * fake_pos=(Bit32u*)&temppos;
643 	*pos=*fake_pos;
644 #endif
645 	*pos=(Bit32u)ftell(fhandle);
646 	last_action=NONE;
647 	return true;
648 }
649 
Close()650 bool localFile::Close() {
651 	// only close if one reference left
652 	if (refCtr==1) {
653 		if(fhandle) fclose(fhandle);
654 		fhandle = 0;
655 		open = false;
656 	};
657 	return true;
658 }
659 
GetInformation(void)660 Bit16u localFile::GetInformation(void) {
661 	return read_only_medium?0x40:0;
662 }
663 
664 
localFile(const char * _name,FILE * handle)665 localFile::localFile(const char* _name, FILE * handle) {
666 	fhandle=handle;
667 	open=true;
668 	UpdateDateTimeFromHost();
669 
670 	attr=DOS_ATTR_ARCHIVE;
671 	last_action=NONE;
672 	read_only_medium=false;
673 
674 	name=0;
675 	SetName(_name);
676 }
677 
FlagReadOnlyMedium(void)678 void localFile::FlagReadOnlyMedium(void) {
679 	read_only_medium = true;
680 }
681 
UpdateDateTimeFromHost(void)682 bool localFile::UpdateDateTimeFromHost(void) {
683 	if(!open) return false;
684 	struct stat temp_stat;
685 	fstat(fileno(fhandle),&temp_stat);
686 	struct tm * ltime;
687 	if((ltime=localtime((const time_t*)&temp_stat.st_mtime))!=0) {
688 		time=DOS_PackTime((Bit16u)ltime->tm_hour,(Bit16u)ltime->tm_min,(Bit16u)ltime->tm_sec);
689 		date=DOS_PackDate((Bit16u)(ltime->tm_year+1900),(Bit16u)(ltime->tm_mon+1),(Bit16u)ltime->tm_mday);
690 	} else {
691 		time=1;date=1;
692 	}
693 	return true;
694 }
695 
Flush(void)696 void localFile::Flush(void) {
697 	if (last_action==WRITE) {
698 		fseek(fhandle,ftell(fhandle),SEEK_SET);
699 		last_action=NONE;
700 	}
701 }
702 
703 
704 // ********************************************
705 // CDROM DRIVE
706 // ********************************************
707 
708 int  MSCDEX_RemoveDrive(char driveLetter);
709 int  MSCDEX_AddDrive(char driveLetter, const char* physicalPath, Bit8u& subUnit);
710 bool MSCDEX_HasMediaChanged(Bit8u subUnit);
711 bool MSCDEX_GetVolumeName(Bit8u subUnit, char* name);
712 
713 
cdromDrive(const char driveLetter,const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid,int & error)714 cdromDrive::cdromDrive(const char driveLetter, const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid, int& error)
715 		   :localDrive(startdir,_bytes_sector,_sectors_cluster,_total_clusters,_free_clusters,_mediaid) {
716 	// Init mscdex
717 	error = MSCDEX_AddDrive(driveLetter,startdir,subUnit);
718 	strcpy(info, "CDRom ");
719 	strcat(info, startdir);
720 	this->driveLetter = driveLetter;
721 	// Get Volume Label
722 	char name[32];
723 	if (MSCDEX_GetVolumeName(subUnit,name)) dirCache.SetLabel(name,true,true);
724 }
725 
FileOpen(DOS_File ** file,char * name,Bit32u flags)726 bool cdromDrive::FileOpen(DOS_File * * file,char * name,Bit32u flags) {
727 	if ((flags&0xf)==OPEN_READWRITE) {
728 		flags &= ~OPEN_READWRITE;
729 	} else if ((flags&0xf)==OPEN_WRITE) {
730 		DOS_SetError(DOSERR_ACCESS_DENIED);
731 		return false;
732 	}
733 	bool retcode = localDrive::FileOpen(file,name,flags);
734 	if(retcode) (dynamic_cast<localFile*>(*file))->FlagReadOnlyMedium();
735 	return retcode;
736 }
737 
FileCreate(DOS_File **,char *,Bit16u)738 bool cdromDrive::FileCreate(DOS_File * * /*file*/,char * /*name*/,Bit16u /*attributes*/) {
739 	DOS_SetError(DOSERR_ACCESS_DENIED);
740 	return false;
741 }
742 
FileUnlink(char *)743 bool cdromDrive::FileUnlink(char * /*name*/) {
744 	DOS_SetError(DOSERR_ACCESS_DENIED);
745 	return false;
746 }
747 
RemoveDir(char *)748 bool cdromDrive::RemoveDir(char * /*dir*/) {
749 	DOS_SetError(DOSERR_ACCESS_DENIED);
750 	return false;
751 }
752 
MakeDir(char *)753 bool cdromDrive::MakeDir(char * /*dir*/) {
754 	DOS_SetError(DOSERR_ACCESS_DENIED);
755 	return false;
756 }
757 
Rename(char *,char *)758 bool cdromDrive::Rename(char * /*oldname*/,char * /*newname*/) {
759 	DOS_SetError(DOSERR_ACCESS_DENIED);
760 	return false;
761 }
762 
GetFileAttr(char * name,Bit16u * attr)763 bool cdromDrive::GetFileAttr(char * name,Bit16u * attr) {
764 	bool result = localDrive::GetFileAttr(name,attr);
765 	if (result) *attr |= DOS_ATTR_READ_ONLY;
766 	return result;
767 }
768 
GetFileAttrEx(char * name,struct stat * status)769 bool cdromDrive::GetFileAttrEx(char* name, struct stat *status) {
770 	return localDrive::GetFileAttrEx(name,status);
771 }
772 
GetCompressedSize(char * name)773 Bit32u cdromDrive::GetCompressedSize(char* name) {
774 	return localDrive::GetCompressedSize(name);
775 }
776 
CreateOpenFile(const char * name)777 void* cdromDrive::CreateOpenFile(const char* name) {
778 		return localDrive::CreateOpenFile(name);
779 }
780 
FindFirst(char * _dir,DOS_DTA & dta,bool)781 bool cdromDrive::FindFirst(char * _dir,DOS_DTA & dta,bool /*fcb_findfirst*/) {
782 	// If media has changed, reInit drivecache.
783 	if (MSCDEX_HasMediaChanged(subUnit)) {
784 		dirCache.EmptyCache();
785 		// Get Volume Label
786 		char name[32];
787 		if (MSCDEX_GetVolumeName(subUnit,name)) dirCache.SetLabel(name,true,true);
788 	}
789 	return localDrive::FindFirst(_dir,dta);
790 }
791 
SetDir(const char * path)792 void cdromDrive::SetDir(const char* path) {
793 	// If media has changed, reInit drivecache.
794 	if (MSCDEX_HasMediaChanged(subUnit)) {
795 		dirCache.EmptyCache();
796 		// Get Volume Label
797 		char name[32];
798 		if (MSCDEX_GetVolumeName(subUnit,name)) dirCache.SetLabel(name,true,true);
799 	}
800 	localDrive::SetDir(path);
801 }
802 
isRemote(void)803 bool cdromDrive::isRemote(void) {
804 	return true;
805 }
806 
isRemovable(void)807 bool cdromDrive::isRemovable(void) {
808 	return true;
809 }
810 
UnMount(void)811 Bits cdromDrive::UnMount(void) {
812 	if(MSCDEX_RemoveDrive(driveLetter)) {
813 		delete this;
814 		return 0;
815 	}
816 	return 2;
817 }
818