The Ctrl + V Game

VinsCool

Persona Secretiva Felineus
Global Moderator
Joined
Jan 7, 2014
Messages
14,600
Trophies
4
Location
Another World
Website
www.gbatemp.net
XP
25,218
Country
Canada
IMG_20230712_150038.jpg
 
  • Haha
Reactions: SylverReZ

VinsCool

Persona Secretiva Felineus
Global Moderator
Joined
Jan 7, 2014
Messages
14,600
Trophies
4
Location
Another World
Website
www.gbatemp.net
XP
25,218
Country
Canada
C:
// SAPSPLIT v0.1 by VinsCool

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>

typedef unsigned char BYTE;

typedef enum
{
    FAILURE = -1,
    SUCCESSFUL,
    INPUT_ERROR,
    OUTPUT_ERROR,
    ARGUMENT_ERROR,
    PARAMETER_ERROR,
    HELP_SCREEN,
    INVALID_DATA,
    NOT_ENOUGH_MEMORY
} TStatusCode;

typedef enum
{
    UNDEFINED = -1,
    SPLIT,
    MERGE,
    CONCATENATE,
    ANALYSE
} TOptionMode;

typedef struct
{
    BYTE* buffer;
    int size;
    bool isDuplicate;
} TChunkSection;

TOptionMode optionMode = UNDEFINED;
TChunkSection** channelStream = NULL, ** sectionStream = NULL;
int frameCount = 0, streamCount = 0, sectionCount = 0, chunkCount = 0;
BYTE* fileBuffer = NULL;
char* inputName = NULL, * outputName = NULL;
FILE* in = NULL, * out = NULL;
bool isStereo = false;


void FreeMemory();


// Exit the program, an optional code may be provided for handling errors
void Quit(TStatusCode statusCode, const char* argument)
{
    // Clear all the allocated memory
    FreeMemory();
    
    switch (statusCode)
    {
    case SUCCESSFUL:
        fprintf(stderr, "%s\n\n", argument? argument : "Success!");
        break;
        
    case INPUT_ERROR:
        fprintf(stderr, "Could not open '%s'\n%s\n\n", argument ? argument : "", argument ?
        "No such file or directory" : "Missing argument: [-i] [filename]");
        break;
        
    case OUTPUT_ERROR:
        fprintf(stderr, "Could not write '%s'\n%s\n\n", argument ? argument : "", argument ?
        "No such file or directory" : "Missing argument: [-o] [filename]");
        break;
        
    case ARGUMENT_ERROR:
        fprintf(stderr, "Invalid argument: '%s'\n\n", argument);
        break;
        
    case PARAMETER_ERROR:
        fprintf(stderr, "Invalid parameter: '%s'\n\n", argument);
        break;
        
    case HELP_SCREEN:
        fprintf(stderr, "[SAPSPLIT v0.1 by VinsCool]\n\n"
        "Usage: '%s [-argument] [parameter]'\n\n"
        "Multiple arguments may be used at once, in no particular order\n"
        "Optional parameters may also be required for specific purposes\n\n"
        "[-i] [filename] Input file to be processed\n"
        "[-o] [filename] Output file(s) to be saved\n"
        "[-h] Display this screen and exit the program\n\n", argument);
        break;
        
    case INVALID_DATA:
        fprintf(stderr, "Invalid data: '%s'\n\n", argument);
        break;
        
    case NOT_ENOUGH_MEMORY:
        fprintf(stderr, "Error: Could not create new data due to insufficient memory\n\n");
        break;
        
    default:
        fprintf(stderr, "%s\n\n", argument ?
        argument : "Error: Unknown program status, something wrong occured");
    }
    
    fprintf(stderr, "The status code of '%i' was returned upon exit\n\n", statusCode);
    exit(statusCode);
}

void SplitBuffer(TChunkSection* pChunk, int offset)
{
    if (!pChunk)
        Quit(FAILURE, NULL);
        
    int frame = 0;
        
    while (offset < frameCount)
    {
        pChunk->buffer[frame++] = fileBuffer[offset];
        offset += streamCount;
    }
}

void FillChunk(TChunkSection* pChunkFrom, TChunkSection* pChunkTo, int offset)
{
    if (!pChunkFrom || !pChunkTo)
        Quit(FAILURE, NULL);

    int frame = 0;
        
    while (frame < pChunkTo->size)
    {
        pChunkTo->buffer[frame++] = pChunkFrom->buffer[offset++];
        
        if (offset > pChunkFrom->size)
        {
            printf("Warning: Copy from source chunk truncated, maximum size reached\n\n");
            break;
        }
    }       

}

bool FindDuplicateChunk(TChunkSection* pChunkFrom, TChunkSection* pChunkTo)
{
    if (!pChunkFrom || !pChunkTo)
        Quit(FAILURE, NULL);
        
    if (pChunkFrom->size != pChunkTo->size)
        return false;
        
    if (!(memcmp(pChunkFrom->buffer, pChunkTo->buffer, pChunkFrom->size)))
        return pChunkTo->isDuplicate = true;
    
    return false;
}

void WriteChunk(TChunkSection* pChunk, int channel, int section)
{
    if (!pChunk)
        Quit(FAILURE, NULL);

    // Create the output filename appended with additional infos as an extention
    char fileName[1024];
    sprintf(fileName, "%s.%X_%02X", outputName, channel, section);

    // Open the output file
    if (!(out = fopen(fileName, "wb")))
        Quit(OUTPUT_ERROR, fileName);
    
    fseek(out, 0, SEEK_SET);
    
    // Write the entire buffer to the file
    size_t writeCount = fwrite(pChunk->buffer, 1, pChunk->size, out);
    
    // Close the file once it is written
    fclose(out);
    out = NULL;

    printf("Chunk file '%s' written\n", fileName);
    
    // If there is a mismatch between the bytes written and expected, abort the procedure
    if (writeCount != pChunk->size)
        Quit(FAILURE, "Fatal error: The number of bytes written did not match the expected count");
}

void DeleteChunk(TChunkSection* pChunk)
{
    if (pChunk)
    {
        free(pChunk->buffer);
        free(pChunk);
        pChunk = NULL;
    }
}

TChunkSection* CreateChunk(int size)
{
    TChunkSection* pChunk;

    if (!(pChunk = (TChunkSection*)malloc(sizeof(TChunkSection))))
        Quit(NOT_ENOUGH_MEMORY, NULL);

    if (!(pChunk->buffer = (BYTE*)malloc(size * sizeof(BYTE))))
    {
        DeleteChunk(pChunk);
        Quit(NOT_ENOUGH_MEMORY, NULL);
    }

    pChunk->size = size;
    pChunk->isDuplicate = false;

    return pChunk;
}

int GetChunkSize(TChunkSection* pChunk)
{
    if (!pChunk)
        Quit(FAILURE, NULL);
        
    return pChunk->size;   
}

void ProcessArguments(int argc, char** argv)
{
    // If the program was executed with no argument, display the help screen by default
    if (argc <= 1)
        Quit(HELP_SCREEN, argv[0]);
        
    char* option = NULL;

    // Parse command line arguments and parameters
    for (int i = 1; i < argc; i++)
    {
        if (argv[i][0] == '-' && argv[i][2] == 0)
        {
            switch (argv[i][1])
            {
            case 'c':
                sectionCount = atoi(argv[++i]);
                continue;
            
            case 'i':
                inputName = argv[++i];
                continue;
                
            case 'o':
                outputName = argv[++i];
                continue;
                
            case 's':
                isStereo = true;
                continue;
                
            case 'm':
                option = argv[++i];
                    
                if (!strcmp(option, "split"))
                    optionMode = SPLIT;
                else if (!strcmp(option, "merge"))
                    optionMode = MERGE;
                else if (!strcmp(option, "concatenate"))
                    optionMode = CONCATENATE;
                else if (!strcmp(option, "analyse"))
                    optionMode = ANALYSE;
                else
                    break;
                    //optionMode = UNDEFINED;
                continue;
        
            case 'h':
                Quit(HELP_SCREEN, argv[0]);
            }
        }
        
        // Anything reaching this line is assumed to be an invalid argument
        Quit(ARGUMENT_ERROR, argv[i]);
    }

    // Set the number of channels to process, Mono uses 8+1 by default
    streamCount = (9 * (isStereo + 1));
    
    // Set the number of chunks to process, if defined
    chunkCount = streamCount * sectionCount;

    // Verify if the input and output filenames are valid before continuing further
    if (!inputName || strlen(inputName) < 1)
        Quit(INPUT_ERROR, inputName);

    if (!outputName || strlen(outputName) < 1)
        Quit(OUTPUT_ERROR, outputName);
}

void SplitChunks()
{   
    // Open the input file
    if (!(in = fopen(inputName, "rb")))
        Quit(INPUT_ERROR, inputName);
        
    fseek(in, 0, SEEK_END);
    
    // Identify the file size, and reject anything not matching a multiple of streamCount
    if ((frameCount = ftell(in)) % streamCount)
        Quit(INVALID_DATA, inputName);
    
    fseek(in, 0, SEEK_SET);
    
    // Allocate the file buffer memory using the framecount for reference
    if (!(fileBuffer = (BYTE*)malloc(frameCount * sizeof(BYTE))))
        Quit(NOT_ENOUGH_MEMORY, NULL);
    
    // Load the entire file into the buffer
    size_t readCount = fread(fileBuffer, sizeof(BYTE), frameCount, in);
    
    // Close the file once it is read
    fclose(in);
    in = NULL;
    
    // If there is a mismatch between the bytes read and identified, abort the procedure
    if (readCount != frameCount)
        Quit(FAILURE, "Fatal error: The number of bytes read did not match the expected count");
    
    // Allocate the channel stream memory using the streamcount for reference
    if (!(channelStream = (TChunkSection**)calloc(streamCount, sizeof(TChunkSection*))))
        Quit(NOT_ENOUGH_MEMORY, NULL);
    
    // Split each channel to individual byte stream
    for (int i = 0; i < streamCount; i++)
        SplitBuffer(channelStream[i] = CreateChunk(frameCount / streamCount), i);
}

void ProcessChunks()
{
    switch (optionMode)
    {
    case SPLIT:
        // Create new chunks and save them as new files
        if (sectionCount > 1 && chunkCount > 0)
        {
            // Allocate the section stream memory using the chunk count for reference
            if (!(sectionStream = (TChunkSection**)calloc(chunkCount, sizeof(TChunkSection*))))
                Quit(NOT_ENOUGH_MEMORY, NULL);
            
            int count = 0;
            
            for (int i = 0; i < streamCount; i++)
            {
                int chunkSize = GetChunkSize(channelStream[i]);
                int sectionSize = chunkSize / sectionCount;
                int offset = 0;
                
                if (!chunkSize)
                    Quit(FAILURE, "Fatal Error: A size of 0 was returned");
                
                while (offset < chunkSize && count < chunkCount)
                {
                    FillChunk(channelStream[i], sectionStream[count++] = CreateChunk(sectionSize), offset);
                    offset += sectionSize;
                }
                
            }
            
            //bool FindDuplicateChunk(TChunkSection* pChunkFrom, TChunkSection* pChunkTo)
            
            printf("Chunk count: %i\n\n", count);
            
            for (int k = 0; k < chunkCount; k++)
            {
                count = 0;

                for (int i = 0; i < streamCount; i++)
                {               
                    for (int j = 0; j < sectionCount; j++)
                    {
                        if (k != count && FindDuplicateChunk(sectionStream[k], sectionStream[count]))
                        {
                            printf("Chunk %i is a duplicate of chunk %i, used in Channel %i, section %i\n",
                            k, count, i, j);
                        }
                        
                        count++;
                    }
                }           
            }
            
            count = 0;
            
            for (int i = 0; i < streamCount; i++)
            {               
                for (int j = 0; j < sectionCount; j++)
                {
                    if (count > chunkCount)
                        break;
                    
                    WriteChunk(sectionStream[count++], i, j);
                }
            }
            
        }
        
        // Save data from channel streams directly
        else
        {
            for (int i = 0; i < streamCount; i++)
                WriteChunk(channelStream[i], i, 0);
        }
        break;
        
    case ANALYSE:
        // Analyse the framecount to find the optimal section size per channel stream
        for (int i = 0; i < streamCount; i++)
        {   
            int count = 0;
            int size = GetChunkSize(channelStream[i]);
            
            if (!size)
                Quit(FAILURE, "Fatal Error: A size of 0 was returned");
            
            printf("Integral divisions for Channel %i:\n", i);
            
            for (int j = 1; j < 256; j++)
            {
                if (size % j)
                    continue;
                
                printf("%i (%i bytes)\n", j, size / j);
                count++;
            }
            
            printf("For a total of %i possible numbers\n\n", count);
        }
        break;

    default:
        Quit(FAILURE, "Missing argument: [-m] [option]");
    }

}

void FreeMemory()
{
    // We no longer need the file buffer at this point
    if (fileBuffer)
    {
        free(fileBuffer);
        fileBuffer = NULL;
    }
    
    // We no longer need the channel buffer either
    if (channelStream)
    {
        // Delete the byte streams once they are no longer needed
        for (int i = 0; i < streamCount; i++)
            DeleteChunk(channelStream[i]);

        free(channelStream);
        channelStream = NULL;
    }
    
    // We no longer need the section buffer either
    if (sectionStream)
    {
        // Delete the byte streams once they are no longer needed
        for (int i = 0; i < chunkCount; i++)
            DeleteChunk(sectionStream[i]);

        free(sectionStream);
        sectionStream = NULL;
    }
    
    // Close files if they are still open for some reason
    if (in)
    {
        fclose(in);
        in = NULL;
    }
    
    if (out)
    {
        fclose(out);
        out = NULL;
    }
}

int main(int argc, char** argv)
{
    // Just so things look less cramped right off the bat
    printf("\n");

    // Parse the command line arguments
    ProcessArguments(argc, argv);
    
    // Load the input file and split the buffers into individual channel streams
    SplitChunks();
    
    // Run the procedure using all the necessary parameters
    ProcessChunks();
        
    // Finished without error
    Quit(SUCCESSFUL, NULL);
}
 
  • Like
Reactions: plasturion

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
    K3Nv2 @ K3Nv2: https://www.washingtonpost.com/business/2024/04/08/walmart-settlement-meat-seafood-do-you-qualify/