Homebrew Homebrew app Project New TinWoo Installer

  • Thread starter Thread starter impeeza
  • Start date Start date
  • Views Views 98,348
  • Replies Replies 134
  • Likes Likes 37
Probably better to do a feature request on the git. As far as I know the dev doesn't come around here.
Post automatically merged:

I take back anything good I ever said about Tinwoo. The dev is a fucking asshole just like duckbill and whoever runs the switchbrew git. DrDude is a raging bigot and duckbill has some weird Russian superiority complex. Not sure what the problem with Switchbrew is but they're no better than the other two losers.

This statement from the OP is not accurate. "An old temper worked on the code and make some tweaks correcting the problem, now you can batch install several files at the same time without crashing the app" It still happens.
What did switchbrew do?
 
It's sad to see drama around each one of the major title installers for the Switch. If only people could put aside their issues and use the time they spend arguing by fixing some of the issues users are experiencing, we would have some well-maintained projects.
 
What did switchbrew do?
Nothing, they just removed an issue he posted from their libnx github as they saw it as a non issue and then he thew a hissy fit and started complaining on other websites about how they ignored him. I suspect the people that code these programs for free are just sick and tired of people like him asking for stuff and then acting like spoiled children when they don't get their way.

It's no wonder devs stop coding stuff and sharing it as they probably can't be botherd with the drama. Awoo/Tinwoo/Tinleaf/AtmoXL all stopped becase of dramas, DBI now in Russian only because of dramas. There's a pattern of great apps being discontinued because frankly the devs are just sick of reading childish comments, especailly from people that can't even code a simple "hello world" program.
 
Nothing, they just removed an issue he posted from their libnx github as they saw it as a non issue and then he thew a hissy fit and started complaining on other websites about how they ignored him. I suspect the people that code these programs for free are just sick and tired of people like him asking for stuff and then acting like spoiled children when they don't get their way.

It's no wonder devs stop coding stuff and sharing it as they probably can't be botherd with the drama. Awoo/Tinwoo/Tinleaf/AtmoXL all stopped becase of dramas, DBI now in Russian only because of dramas. There's a pattern of great apps being discontinued because frankly the devs are just sick of reading childish comments, especailly from people that can't even code a simple "hello world" program.

Yeah. No. That's not exactly what happened, not the complete story, and you know it. Lying bitch.
 
  • Like
Reactions: SkullHex2
Can someone tell me what the glorious background music is that is playing on the installer menu?
 
Can someone tell me what the glorious background music is that is playing on the installer menu?
What installer menu? The one that plays in tinwoo?

It's a song called bluesong which was made back in the Amiga 500 days in a program called modtracker/soundtracker/protracker by karsten_obarski. All samples used in the song are from the Yamaha DX21 (if you like that kindof sound).

It's a mod file that the paula chip on amiga was able to play. It came out about the late 1980's I think. Any player such as vlc can play it, or sdl2 can play it on pretty much any hardware. You can download it or rip it from Tinwoo nro file as it's in ROMFS directory called bluesong.mod.

https://modarchive.org/index.php?request=view_by_moduleid&query=161745

There's a bit about trackers here:
https://xavier.borderie.net/blog/20...part-1-where-in-the-world-is-karsten-obarski/
 
Last edited by littlesmurf,
Can someone tell me what the glorious background music is that is playing on the installer menu?
It's referenced on the source as «Bluesong» sorry no more information.

What installer menu? The one that plays in tinwoo?

It's a song called bluesong which was made back in the Amiga 500 days in a program called modtracker/soundtracker/protracker by karsten_obarski. All samples used in the song are from the Yamaha DX21 (if you like that kindof sound).

It's a mod file that the paula chip on amiga was able to play. It came out about the late 1980's I think. Any player such as vlc can play it, or sdl2 can play it on pretty much any hardware. You can download it or rip it from Tinwoo nro file as it's in ROMFS directory called bluesong.mod.

https://modarchive.org/index.php?request=view_by_moduleid&query=161745

There's a bit about trackers here:
https://xavier.borderie.net/blog/20...part-1-where-in-the-world-is-karsten-obarski/
for me Media Player Classic which is part of K-Lite Codec Pack plays the file without problem
 
@impeeza, since you have the source code for tinwoo here's the fix for the usb3 transfers, it fixes the following:

Key Fixes in _usbCommsRead():
Ensures buffer alignment
Flushes the cache properly after the transfer
Fixes memory attribute issues
Handles Zero-Length Packet (ZLP) properly

Summary of Fixes
Memory Attribute Fix: svcSetMemoryAttribute is not needed since we don’t modify cache attributes.
Cache Flush Fix: svcFlushProcessDataCache ensures the CPU sees the latest received data.
Alignment Check: Ensures transfer_buffer is 0x1000-aligned before USB transfer.
Zero-Length Packet (ZLP) Handling: USB read should already handle ZLP correctly since it reads exactly what was sent.

Code:
#define MAX_TRANSFER_SIZE 0x400000 // 4MB max transfer size

static Result _usbCommsRead(usbCommsInterface* interface, void* buffer, size_t size, size_t* transferredSize, u64 timeout) {
    Result rc = 0;
    u32 urbId = 0;
    u8* bufptr = (u8*)buffer;
    u8* transfer_buffer = NULL;
    u32 chunksize = 0;
    u32 tmp_transferredSize = 0;
    size_t total_transferredSize = 0;
    UsbDsReportData reportdata;
    //const u32 wMaxPacketSize = 0x200; // Adjust based on your USB endpoint settings

    // Make sure endpoints are ready for data-transfer / wait for init if needed.
    rc = usbDsWaitReady(timeout);
    if (R_FAILED(rc)) return rc;

    while (size) {
        if (((u64)bufptr) & 0xfff) { // If bufptr isn't page-aligned, use a temp buffer
            transfer_buffer = interface->endpoint_out_buffer;
            memset(interface->endpoint_out_buffer, 0, 0x1000);

            chunksize = 0x1000;
            chunksize -= ((u64)bufptr) & 0xfff; // Align for next transfer
            if (size < chunksize) chunksize = size;
        }
        else {
            transfer_buffer = bufptr;
            chunksize = size;
        }

        // Ensure buffer alignment
        if (((u64)transfer_buffer) & 0xfff) {
            printf("Error: Transfer buffer is not 0x1000 aligned!\n");
            return -1;
        }

        // Start a host->device transfer.
        printf("Posting read buffer: addr=%p, size=%u\n", transfer_buffer, chunksize);
        rc = usbDsEndpoint_PostBufferAsync(interface->endpoint_out, transfer_buffer, chunksize, &urbId);
        if (R_FAILED(rc)) return rc;

        // Wait for the transfer to complete
        rc = eventWait(&interface->endpoint_out->CompletionEvent, timeout);
        if (R_FAILED(rc)) {
            printf("eventWait timeout: 0x%08X\n", rc);
            usbDsEndpoint_Cancel(interface->endpoint_out);
            eventWait(&interface->endpoint_out->CompletionEvent, UINT64_MAX);
            eventClear(&interface->endpoint_out->CompletionEvent);
            return rc;
        }
        eventClear(&interface->endpoint_out->CompletionEvent);

        // Get transfer status
        rc = usbDsEndpoint_GetReportData(interface->endpoint_out, &reportdata);
        if (R_FAILED(rc)) return rc;

        rc = usbDsParseReportData(&reportdata, urbId, NULL, &tmp_transferredSize);
        if (R_FAILED(rc)) return rc;

        if (tmp_transferredSize > chunksize) tmp_transferredSize = chunksize;

        total_transferredSize += (size_t)tmp_transferredSize;

        // Copy data from temp buffer if used
        if (((u64)bufptr) & 0xfff) {
            memcpy(bufptr, transfer_buffer, tmp_transferredSize);
        }

        bufptr += tmp_transferredSize;
        size -= tmp_transferredSize;

        // If transfer completes early, exit loop
        if (tmp_transferredSize < chunksize) break;
    }

    // Flush data cache after receiving
    svcFlushProcessDataCache(CUR_PROCESS_HANDLE, (u64)buffer, total_transferredSize);

    if (transferredSize) *transferredSize = total_transferredSize;

    return rc;
}

Code:
static Result _usbCommsWrite(usbCommsInterface* interface, const void* buffer, size_t size, size_t* transferredSize, u64 timeout) {
    Result rc = 0;
    u32 urbId = 0;
    u32 chunksize = 0;
    u8* bufptr = (u8*)buffer;
    u8* transfer_buffer = NULL;
    u32 tmp_transferredSize = 0;
    size_t total_transferredSize = 0;
    UsbDsReportData reportdata;
    const u32 wMaxPacketSize = 0x200; // Adjust based on your USB endpoint settings

    // Make sure endpoints are ready for data-transfer / wait for init if needed.
    rc = usbDsWaitReady(timeout);
    if (R_FAILED(rc)) return rc;

    while (size) {
        if (((u64)bufptr) & 0xfff) { // If bufptr isn't page-aligned, use a temp buffer
            transfer_buffer = interface->endpoint_in_buffer;
            memset(interface->endpoint_in_buffer, 0, 0x1000);

            chunksize = 0x1000;
            chunksize -= ((u64)bufptr) & 0xfff; // Align for next transfer
            if (size < chunksize) chunksize = size;

            memcpy(interface->endpoint_in_buffer, bufptr, chunksize);
        }
        else {
            transfer_buffer = bufptr;
            chunksize = size;
        }

        // Ensure buffer alignment
        if (((u64)transfer_buffer) & 0xfff) {
            printf("Error: Transfer buffer is not 0x1000 aligned!\n");
            return -1;
        }

        // Disable caching for this memory range
        /*
        rc = svcSetMemoryAttribute(transfer_buffer, chunksize, 0, 8);
        if (R_FAILED(rc)) {
            printf("Failed to set memory attribute: 0x%08X\n", rc);
            return rc;
        }
        */

        // Flush data cache before sending
        svcFlushProcessDataCache(CUR_PROCESS_HANDLE, (u64)transfer_buffer, chunksize);

        // Start a device->host transfer.
        printf("Posting buffer: addr=%p, size=%u\n", transfer_buffer, chunksize);
        rc = usbDsEndpoint_PostBufferAsync(interface->endpoint_in, transfer_buffer, chunksize, &urbId);
        if (R_FAILED(rc)) return rc;

        // Wait for transfer to complete
        rc = eventWait(&interface->endpoint_in->CompletionEvent, timeout);
        if (R_FAILED(rc)) {
            printf("eventWait timeout: 0x%08X\n", rc);
            usbDsEndpoint_Cancel(interface->endpoint_in);
            eventWait(&interface->endpoint_in->CompletionEvent, UINT64_MAX);
            eventClear(&interface->endpoint_in->CompletionEvent);
            return rc;
        }
        eventClear(&interface->endpoint_in->CompletionEvent);

        // Get transfer status
        rc = usbDsEndpoint_GetReportData(interface->endpoint_in, &reportdata);
        if (R_FAILED(rc)) return rc;

        rc = usbDsParseReportData(&reportdata, urbId, NULL, &tmp_transferredSize);
        if (R_FAILED(rc)) return rc;

        if (tmp_transferredSize > chunksize) tmp_transferredSize = chunksize;

        total_transferredSize += (size_t)tmp_transferredSize;
        bufptr += tmp_transferredSize;
        size -= tmp_transferredSize;

        // If transfer completes early, exit loop
        if (tmp_transferredSize < chunksize) break;
    }

    // Send Zero-Length Packet (ZLP) if needed
    if ((total_transferredSize % wMaxPacketSize) == 0 && total_transferredSize > 0) {
        printf("Sending Zero-Length Packet (ZLP)...\n");
        usbDsEndpoint_PostBufferAsync(interface->endpoint_in, NULL, 0, &urbId);

        // Wait for ZLP transfer to complete
        rc = eventWait(&interface->endpoint_in->CompletionEvent, timeout);
        if (R_FAILED(rc)) {
            printf("ZLP eventWait failed: 0x%08X\n", rc);
            usbDsEndpoint_Cancel(interface->endpoint_in);
            return rc;
        }
        eventClear(&interface->endpoint_in->CompletionEvent);
    }

    if (transferredSize) *transferredSize = total_transferredSize;

    return rc;
}

Someone that fixed this but wants to remain anonymous told me to give it to you as you would know what to do with it.

I tested this out and it works good for me, you can adjust this line here - const u32 wMaxPacketSize = 0x200; // Adjust based on your USB endpoint settings, this is set for packet size of 512 bytes (high speed usb), for super speed usb (3.1) you can change that value to 0x400 which will double the packet size and make usb transfers a little faster, but it's best to keep it at 0x200 for better compatability.

File to edit - usb_comms_tinleaf.c
 
Last edited by littlesmurf,
You do not need to flush dcache, because it is already done in postbufferasync. Also posting null buffer is a bad idea. Usbds has specific method for this. All you need is to call writezlt(true) before last chunk.
Post automatically merged:

Also. You do need to adjust packet size to 0x400 in case of usb3, because in interface setup it is set to 0x400
Post automatically merged:

Also. Page alignment code is wrong. You are doing two sequential read, first one is definelty not equal to full packet size. In this case remaining data from full packet will be dropped and not actually read in the second attempt. So, you will lose some data in the middle of transfer.
 
Last edited by duckbill007,
You do not need to flush dcache, because it is already done in postbufferasync. Also posting null buffer is a bad idea. Usbds has specific method for this. All you need is to call writezlt(true) before last chunk.
Post automatically merged:

Also. You do need to adjust packet size to 0x400 in case of usb3, because in interface setup it is set to 0x400
Post automatically merged:

Also. Page alignment code is wrong. You are doing two sequential read, first one is definelty not equal to full packet size. In this case remaining data from full packet will be dropped and not actually read in the second attempt. So, you will lose some data in the middle of transfer.
Thanks for your input, I've installed quite a few nsp with this and not faced any issues. However if you want to mod the code, feel free and post the updated code.
 
  • Love
  • Haha
Reactions: JK_ and impeeza
I'm assuming he means Monster Hunter Rise.
I don't play those type games so I don't have it. Like I said I've installed quite a few nsp and didn't get any problems, but the code is posted so anyone can mod it, and if it's just a couple of lines that need commented out or a line or two added, feel free.
 
  • Like
  • Haha
Reactions: JK_ and impeeza
Problem discussed here can be reprodused by installing big batches of games, not a few nsp.

Code above not solved it.
Post automatically merged:

And I have no idea how to fix it modifying line or two of code. For me solving that error takes total rewrite of usb comms and switching to different protocol.
 
Problem discussed here can be reprodused by installing big batches of games, not a few nsp.

Code above not solved it.
Post automatically merged:

And I have no idea how to fix it modifying line or two of code. For me solving that error takes total rewrite of usb comms and switching to different protocol.
I didn't get any problems doing batch installs, I just did 8 games in one go and everything was fine. If there's a problem with the usb comms this should be directed and fixed on libnx though as a pull request as the original usb_comms_tinleaf.c which is used in tinleaf/tinwoo and other installers uses the exact same code which libnx uses.

As libnx has not had this issue reported or it only affects a few people that use usb3.1, in the usb write command on this line (const u32 wMaxPacketSize = 0x200 forces the packet size to use 512 bytes instead of 1024 and overwrites the auto detected super speed you mentioned in a previous post, which seems so be why usb3.1 users were having issues. I didn't get any issues myself though and don't have usb3.1 on my laptop to test with. Also forcing packet size to 512 instead of 1024 bytes I don't see any difference in speed on writing to a microsd card.
 
did 8 games in one go
Man, I mentioned MHR because it has hundreds of files, not 8.
Post automatically merged:

in the usb write command on this line (const u32 wMaxPacketSize = 0x200 forces the packet size to use 512 bytes instead of 1024 and overwrites the auto detected super speed you mentioned in a previous post,
Did you read your code? wMaxPacketSize not used in sending process at all. It only affects if your code tries to send null buffer of not. (Also, sending null buffer is not the same as writing ZeroLength packet, which is done automaticaly if you configure endpoint to do that by using writeZlt(true) call). Just get wireshark and look what data and what packets actually sent.
Post automatically merged:

Also, all that packet thing in WRITE is useless, because tinwoo uses write only to send very small (less that 100 bytes) commands to PC.

To sumarize:

1. flushing cache is useles because it is done inside usbDsEndpoint_PostBufferAsync
2. buffer alignment has bugs in read and will drop bytes on read. It is a luck that buffers are prealigned in tinwoo. So, this alignment also useless.
3. sending zero-length packet done incorect and with wrong packetsize. Also it is useless because tinwoo never writes more that 100 bytes (OK, 200 bytes for very long filenames) command, which always les than single packet even in USB2 case (512 bytes)

So, all of your changes are useless and not affect behaviour of tinwoo at all.

Back to issue - it is caused by BIG install batches with dozens or hundreds of files. You did not tested that.
 
Last edited by duckbill007,
  • Like
Reactions: SkullHex2
Back to issue - it is caused by BIG install batches with dozens or hundreds of files. You did not tested that.
I never install hundreds of files at once or do big batch installs. But if this is the case, surely the issue is more down the the way that Tinwoo/Tinleaf etc., stores the filenames in a list that it wants to install. Maybe the buffer that holds all those filenames is becoming full so that's why the large batch install fails when it gets to the end of that buffer? I'll need to check usbInstall.cpp and some other files down to installing to check this.
 
Last edited by littlesmurf,
  • Like
Reactions: Blythe93
Not trying to get in the middle, but I just wanted to make mention that for myself it's very common practice for me to load/install batches with 100's of files selected at once. For example a newly created sd card getting populated, it's very common for me to do something like throwing the switch on a dock, loading up DBI (for example) and then selecting large batches of files at a time and letting it install.

Once a switch is setup, sure it's normally 5-10 files at a time. But for newly created partitions, very common to load 100's at a time.
 
  • Like
Reactions: Blythe93

Site & Scene News

Popular threads in this forum