// File copy tool // Currently not that efficient and reads entire file in memory // instead of doing it in chunks /* [x] check BDOS args for src and dst [x] check src file is valid [x] create dst file [x] open src [x] set cursor to start [x] read chunk into mem [x] close src [x] open dst [x] set cursor to start [x] write mem into dst [x] close dst */ #define word char #include "LIB/MATH.C" #include "LIB/SYS.C" #include "LIB/STDLIB.C" #include "LIB/FS.C" char infilename[96]; // input filename char outfilename[96]; // output filename #define EOF -1 #define FOPEN_FILENAME_LIMIT 32 #define STDIO_FBUF_ADDR 0x420000 #define STDIO_MEMBUF_ADDR 0x440000 #define FOPEN_MAX_FILESIZE 0x200000 // 5 MiB // Buffers for reading // Length of buffer always should be less than 65536, since this is the maximum FS_readFile can do in a single call #define STDIO_FBUF_LEN 32768 char *inputBuffer = (char*) STDIO_FBUF_ADDR; //inputBuffer[STDIO_FBUF_LEN]; word inbufStartPos = 0; // where in the file the buffer starts word inbufCursor = 0; // where in the buffer we currently are working word fileSize = 0; // size of input file char *memBuf = (char*) STDIO_MEMBUF_ADDR; // where the entire input file is written to // Opens file for reading // requires full paths // returns 1 on success word fopenRead(const char* fname) { if (strlen(fname) >= FOPEN_FILENAME_LIMIT) { BDOS_PrintConsole("E: Path too large\n"); return 0; } if (fname[0] != '/') { BDOS_PrintConsole("E: Filename should be a full path\n"); return 0; } FS_close(); // to be sure // convert to uppercase strToUpper(fname); // init read buffer inbufStartPos = 0; // start at 0 inbufCursor = 0; // start at 0 // if the resulting path is correct (can be file or directory) if (FS_sendFullPath(fname) == FS_ANSW_USB_INT_SUCCESS) { // if we can successfully open the file (not directory) if (FS_open() == FS_ANSW_USB_INT_SUCCESS) { FS_setCursor(0); // set cursor to start // get filesize fileSize = FS_getFileSize(); word maxFileSize = FOPEN_MAX_FILESIZE; if (fileSize > maxFileSize) { BDOS_PrintConsole("E: File too large for memory\n"); return 0; } return 1; } else { return 0; } } else { return 0; } return 0; } // Opens file for writing // requires full paths // returns 1 on success word fopenWrite(const char* fname) { if (strlen(fname) >= FOPEN_FILENAME_LIMIT) { BDOS_PrintConsole("E: Path too large\n"); return 0; } if (fname[0] != '/') { BDOS_PrintConsole("E: Filename should be a full path\n"); return 0; } FS_close(); // to be sure // convert to uppercase strToUpper(fname); // if current path is correct (can be file or directory) if (FS_sendFullPath(fname) == FS_ANSW_USB_INT_SUCCESS) { // create the file if (FS_createFile() == FS_ANSW_USB_INT_SUCCESS) { //BDOS_PrintConsole("File created\n"); // open again and start at 0 FS_sendFullPath(fname); FS_open(); FS_setCursor(0); // set cursor to start return 1; } else { BDOS_PrintConsole("E: Could not create file\n"); return 0; } } else { BDOS_PrintConsole("E: Invalid path\n"); return 0; } return 0; } // closes currently opened file void fclose() { FS_close(); } // returns the current char at cursor (EOF if end of file) // increments the cursor word fgetc() { // workaround for missing compiler check for ALU constants > 11 bit word stdioBufLen = STDIO_FBUF_LEN; if (inbufCursor >= STDIO_FBUF_LEN || inbufCursor == 0) // we are at the end of the buffer (or it is not initialized yet) { // get filesize word sizeOfFile = FS_getFileSize(); // if we cannot completely fill the buffer: if (inbufStartPos + stdioBufLen > sizeOfFile) { // fill the buffer, and append with EOF token FS_readFile(inputBuffer, sizeOfFile - inbufStartPos, 0); inputBuffer[sizeOfFile - inbufStartPos] = EOF; } else { // Fill buffer again FS_readFile(inputBuffer, STDIO_FBUF_LEN, 0); } inbufStartPos += stdioBufLen; // for the next fill inbufCursor = 0; // start at the beginning of the buffer again } // return from the buffer, and increment char gotchar = inputBuffer[inbufCursor]; inbufCursor++; // BDOS_PrintcConsole(gotchar); // useful for debugging return gotchar; } word fputData(char* outbufAddr, word lenOfData) { word bytesWritten = 0; // loop until all bytes are sent while (bytesWritten != lenOfData) { word partToSend = lenOfData - bytesWritten; // send in parts of 0xFFFF if (partToSend > 0xFFFF) partToSend = 0xFFFF; // write away if (FS_writeFile((outbufAddr +bytesWritten), partToSend) != FS_ANSW_USB_INT_SUCCESS) BDOS_PrintConsole("write error\n"); // Update the amount of bytes sent bytesWritten += partToSend; } } // reads all data from input file into memory void readInFileToMem() { word outputi = 0; char c = fgetc(); // stop on EOF while (c != EOF) { memBuf[outputi] = c; outputi++; c = fgetc(); } memBuf[outputi] = 0; // terminate } int main() { // output file BDOS_GetArgN(2, outfilename); if (outfilename[0] == 0) { BDOS_PrintConsole("E: Missing arguments\n"); return 2; } // make full path if it is not already if (outfilename[0] != '/') { char bothPath[96]; bothPath[0] = 0; strcat(bothPath, BDOS_GetPath()); if (bothPath[strlen(bothPath)-1] != '/') { strcat(bothPath, "/"); } strcat(bothPath, outfilename); strcpy(outfilename, bothPath); } // create output file, test if it can be created/opened if (!fopenWrite(outfilename)) { BDOS_PrintConsole("Could not open outfile\n"); return 0; } fclose(); // input file BDOS_GetArgN(1, infilename); // Default to out.asm if (infilename[0] == 0) { BDOS_PrintConsole("E: Missing arguments\n"); return 2; } // Make full path if it is not already if (infilename[0] != '/') { char bothPath[96]; bothPath[0] = 0; strcat(bothPath, BDOS_GetPath()); if (bothPath[strlen(bothPath)-1] != '/') { strcat(bothPath, "/"); } strcat(bothPath, infilename); strcpy(infilename, bothPath); } // Open the input file if (!fopenRead(infilename)) { BDOS_PrintConsole("Cannot open input file\n"); return 1; } // read into memory readInFileToMem(); // write into file // write binary to output file if (!fopenWrite(outfilename)) { BDOS_PrintConsole("Could not open outfile\n"); return 0; } fputData(memBuf, fileSize); fclose(); return 'q'; } void interrupt() { // handle all interrupts word i = getIntID(); switch(i) { case INTID_TIMER1: timer1Value = 1; // notify ending of timer1 break; case INTID_TIMER2: break; case INTID_UART0: break; case INTID_GPU: break; case INTID_TIMER3: break; case INTID_PS2: break; case INTID_UART1: break; case INTID_UART2: break; } }