TinWoo
TinWoo copied to clipboard
Feature request: set archive bit
It would be really helpful if there was a feature to set the archive bit on a directory. This would be extra useful when using "install over USB HDD" because nxshell is the only other tool I know of that has the feature to set the archive bit, but it doesn't allow you to browse USB devices.
That's only for MAC owners right?
@mrdude2478 Not necessarily. Any nsp directory may or may not have the archive bit set. IE: it's possible in windows to unset the archive bit on a directory. FWIW I use linux and encountered this, and even though there is a solution available for linux, it's not as easy as having that solution available in the app.
User story
As a TinWoo user, if I find myself in a circumstance where I am trying to install an nsp directory that does not have the archive bit set, I would like to be able to successfully install that nsp without leaving TinWoo. Further, I would like to be able to successfully complete this install without needing an internet connection to download more Switch Homebrew apps, and without needing an additional computer, both of which may have been present when copying the files but may not be present when installing them, EG: on an airplane.
I'll maybe have a look when I get some time, in the meantime I have updated the source code on the git, if you have a look at nx-shell, or just look here:
https://forums.codeguru.com/showthread.php?483201-Clearing-the-archive-bit https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileattributesa
It shouldn't be difficult to implement this, I am busy just now - but Ill keep it in mind.
Here's a small c++ bit of code I tested in windows that can remove all the archive bit properties:
#include <FileAPI.h>
using namespace std;
int main() {
SetFileAttributes("text.txt",0);
}
Devkitpro doesn't have FileAPI.h, but you can get the switch equivalent from the headers and once of the source files in nxshell - probably in includes /f.h, and one of the source .cpp files.
Also as you download and install via the net or from hdd - it would be better to just automatically get the bit state - store that as a variable, then set the bit on the file to zero just before it gets installed - once installed set it back what it was. That way there would be no need for you to do anything as everything would be done automatically. (Not sure about split xci/nsp's - but would probably work).
Update, I had a look at nxshell code and Archive bit only does this: fsFsSetConcatenationFileAttribute()
https://switchbrew.github.io/libnx/fs_8h.html#aa2d20aa70309fe1e1806e2ebf05c0830
I have written some test code to call that and set it on a folder/file and to be honest I never noticed what difference it made? When you use nxshell, what difference does it make to your files?
https://switchbrew.github.io/libnx/fs_8h.html#aa2d20aa70309fe1e1806e2ebf05c0830
This will cause HOS to treat the directory as if it were a file containing the directory's concatenated contents.
The effect in apps is that you cannot browse into directory.nsp
and see 00, 01, 02. You see just directory.nsp
and operate on it as if it were cat directory.nsp/* >> concatenated_file.nsp
OK, I created a folder in the switch directory called test.nsp and then I created this code to set the archive bit on it: (ignore the file creation and rename stuff - I was using that for debugging).
#include <switch.h>
#include <stdio.h>
#include <dirent.h>
#include <string>
#include <iostream>
#include <sys/types.h>
#include <filesystem>
FsFileSystem *fs;
FsFileSystem devices[4];
Result RenameFile(const char* path, const char* new_path) {
devices[0] = *fsdevGetDeviceFileSystem("sdmc");
fs = &devices[0];
Result ret = 0;
std::string switchpath = ("/switch/"); //path to put our files reside in
std::string a = switchpath + path; //create a string path of the file we want to rename
std::string b = switchpath + new_path; //create a string path for the renamed file
const char *C = a.c_str(); //convert string path into a const char
const char *D = b.c_str(); //convert string path into a const char
printf("\nFile 1:%s File 2:%s", C, D); //print file into the the console screen
//remove dude2.txt if it already exists - we can't rename something if there's already a file with the same name.
ret = (fsFsDeleteFile(fs, D) && fsFsRenameFile(fs, C, D));
if (R_FAILED(ret)) {
consoleClear();
printf("Unable to rename the file");
return 0;
}
else {
consoleClear();
printf("File renamed");
return ret;
}
}
Result Archive(const char* folder) {
devices[0] = *fsdevGetDeviceFileSystem("sdmc");
fs = &devices[0];
Result ret = 0;
std::string a = ("/switch/");
std::string b = a + folder;
const char *mypath = b.c_str();
ret = fsFsSetConcatenationFileAttribute(fs, mypath);
if (R_FAILED(ret)) {
consoleClear();
printf("Unable to set archive bit on %s", mypath);
return 0;
}
else {
consoleClear();
printf("Archive bit set on %s", mypath);
return ret;
}
}
int main(int argc, char* argv[])
{
consoleInit(NULL);
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
PadState pad;
padInitializeDefault(&pad);
printf("Press A to quit and B to set the archive bit\n\n");
//read sd card
DIR* dir;
struct dirent* ent;
dir = opendir("");//Open current-working-directory.
if(dir==NULL)
{
printf("Failed to open dir.\n");
}
else
{
// read dir to make sure we can
while ((ent = readdir(dir)))
{
printf("d_name: %s\n", ent->d_name);
}
}
///
while(appletMainLoop())
{
//scan for button changes
padUpdate(&pad);
u64 kDown = padGetButtonsDown(&pad);
//quit the loop if A is pressed
if (kDown & HidNpadButton_A){
break;
}
if (kDown & HidNpadButton_Y){
consoleClear();
}
if (kDown & HidNpadButton_B){
consoleClear();
//create test file
FILE * fp;
fp = fopen ("dude.txt", "w+");
fprintf(fp, "%s %s %s %d", "We", "are", "in", 2022);
fclose(fp);
//call to rename file.
RenameFile("dude.txt", "dude2.txt");
//call to set archive bit on a folder called test.nsp the resides inside sdmc:/switch/
Archive("test.nsp");
//
printf("\n\nDone");
}
consoleUpdate(NULL);
}
closedir(dir);
consoleExit(NULL);
return 0;
}
The archive bit gets set on the folder - however........ when trying to ftp into the switch folder, the ftp client can't access the folder properly if test.nsp was an empty folder (or folder that doesn't contain 00, 01 etc) before the bit was set, this is kind of a bummer as if someone set the bit on a an empty folder - they are kind of screwed unless they connect to a pc to remove the file or use a file browser app to remove it.
Also if you set this bit on a folder such as the switch folder by accident, you wouldn't be able to browse that folder again as the switch would think it was a file. This seems kind of dangerous for most people.
this is kind of a bummer as if someone set the bit on a an empty folder - they are kind of screwed unless they connect to a pc to remove the file or use a file browser app to remove it.
FWIW this is the normal behavior :( I think that is itself another bug. I have encountered this while accidentally setting some random folder to +a and then I have to go correct it on a PC. definitely not a good UX.
Another place you might look for clues on how to do this is the "Fix Archive Bit" feature in Hekate. https://github.com/CTCaer/hekate/search?q=archive
Yes I see the code is looping through all folders and looking for a file called 00, if it finds it the bit is changed on the folder by this:
f_chmod(path, AM_ARC, AM_ARC);
If you want to remove the bit from a file, so it goes back to a folder then this is issued:
f_chmod(path, 0, AM_ARC);
Regards.
Nyx has this feature now, which solves all the use cases I had. It's less convenient than in-Tinwoo but I think good enough.
In Nyx, you can find it in Tools (top menu) -> Arch Bit (bottom left)