1

Does anyone know what tools should I use to extract the backgrounds and sprites of this very rare old game: Armorik the Viking?

The files are in .BIN format. No idea how to open them. I tried Game Extractor and QuickBMS tools but they could not read the files.

What type of BIN are these? The game is from 1988 and was released by InfoGrames.

This is the game:

https://archive.org/download/msdos_Armorik_the_Viking_-_The_Eight_Conquests_1988/Armorik_the_Viking_-_The_Eight_Conquests_1988.zip

Many thanks in advance!

enter image description here

1
  • Unless you want to do some work to 100% confirm (as it's just a .COM file), screenshots of the game look like it's CGA. As per moddingwiki.shikadi.net/wiki/Raw_CGA_Data, it's a pretty simple format with every pair of bits representing a color. To confirm it's CGA with reverse-engineering, though, you'd scan for "INT 10h" in the disassembly to identify all possible screen mode changes, and I'm pretty certain that they're using "INT 21h" to read the contents of those filenames. Commented Jun 28, 2023 at 15:26

2 Answers 2

0

BACKS.BIN - backgrounds. You'll have to write a program to decode those images using your preferred programming language. Some info:

  • All images are 320x200, 2 bits per pixel.

  • all images contain header - 8 bytes (4 values - 2 bytes per value, all header contain values 0,0-319,199)

  • Images are interlaced - first part of image contains only even rows, second - odd rows.

  • All images are packed with RLE compression, pseudocode:

     while(!needExit) {
    
         controlByte = getNextByte(data[position]);
         position++;
    
         if (controlByte == 0) {
             needExit = true;
         } else if (controlByte == 0x80) {
             // change CGA plane
             buffer = oddRows;
         } else if ((controlByte & 0x80) == 0) {
             for (int i=0; i<controlByte; i++) {
                 word = getNextTwoBytes(data, position + i*2);
                 buffer.write(word);
             }
             position += controlByte * 2;
         } else {
             controlByte = controlByte & 0x7F;
             word = getNextTwoBytes(data, position);
             for (int i=0; i<controlByte; i++) {
                 buffer.write(word);
             }
             position += 2;
         }
     }
    

MOTIFS.BIN - sprites. This file is not packed, so you can see/extract sprites with programs like "Texture/Tiles finder" (if they support CGA/2 bits per pixel modes).

  • 256 sprites of size 32x32 pixels

  • 3 sprites of size 48x48 pixels

  • 3 sprites of size 64x64 pixels

  • 2 bits per pixel, CGA pal.

  • After each sprite lies its transparency mask, also 2 bits per pixel. If 2 bits in mask both set then corresponding pixel in sprite is transparent.

Example - https://imgur.com/a/UEf75nF

0

I tried to read the file BACKS.BIN but I can only view one image from the output_images.bin file generated by the following C++ code. It looks like the output file does not contain all the content from the input file BACKS.BIN; I'm not sure how to fix it. Any ideas on how to improve the below code? Many thanks! My viewer is GBS as well.

here it is: https://ibb.co/MhvhZZq

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>

const int IMAGE_WIDTH = 320;
const int IMAGE_HEIGHT = 200;
const int HEADER_SIZE = 8;

struct ImageHeader {
    int width;
    int height;
};

bool getNextTwoBytes(const std::vector<char>& data, int position, short& word) {
    if (position + 1 >= data.size())
        return false;

    word = (static_cast<uint8_t>(data[position + 1]) << 8) | static_cast<uint8_t>(data[position]);
    return true;
}

bool extractAndDecompressImages(const std::string& inputFilePath, const std::string& outputFilePath) {
    std::ifstream inputFile(inputFilePath, std::ios::binary);
    if (!inputFile) {
        std::cerr << "Error: Could not open input file.\n";
        return false;
    }

    inputFile.seekg(0, std::ios::end);
    std::streampos fileSize = inputFile.tellg();
    inputFile.seekg(0, std::ios::beg);

    int imageCount = static_cast<int>(fileSize / (HEADER_SIZE + IMAGE_WIDTH * IMAGE_HEIGHT));
    if (imageCount == 0) {
        std::cerr << "Error: Invalid input file format.\n";
        return false;
    }

    std::ofstream outputFile(outputFilePath, std::ios::binary);
    if (!outputFile) {
        std::cerr << "Error: Could not create output file " << outputFilePath << "\n";
        return false;
    }

    for (int i = 0; i < imageCount; ++i) {
        ImageHeader header;
        inputFile.read(reinterpret_cast<char*>(&header), sizeof(ImageHeader));

        std::vector<char> compressedData(IMAGE_WIDTH * IMAGE_HEIGHT);
        inputFile.read(compressedData.data(), compressedData.size());

        std::vector<char> decompressedData;
        decompressedData.reserve(IMAGE_WIDTH * IMAGE_HEIGHT * 2); // Reserve enough space to avoid reallocations

        int position = 0;

        while (position < compressedData.size()) {
            unsigned char controlByte = static_cast<unsigned char>(compressedData[position++]);
            if (controlByte == 0) {
                break;
            } else if (controlByte == 0x80) {
                // change CGA plane (not relevant for decompression)
                // Assuming CGA plane change is already handled outside of decompression.
            } else if ((controlByte & 0x80) == 0) {
                for (int i = 0; i < controlByte; i++) {
                    short word;
                    if (!getNextTwoBytes(compressedData, position + i * 2, word)) {
                        std::cerr << "Error: Invalid input file format.\n";
                        return false;
                    }
                    decompressedData.insert(decompressedData.end(), {static_cast<char>(word & 0xFF), static_cast<char>(word >> 8)});
                }
                position += controlByte * 2;
            } else {
                controlByte = controlByte & 0x7F;
                short word;
                if (!getNextTwoBytes(compressedData, position, word)) {
                    std::cerr << "Error: Invalid input file format.\n";
                    return false;
                }
                for (int i = 0; i < controlByte; i++) {
                    decompressedData.insert(decompressedData.end(), {static_cast<char>(word & 0xFF), static_cast<char>(word >> 8)});
                }
                position += 2;
            }
        }

        // Write the decompressed data to the output file using modern C++ iterators
        std::copy(decompressedData.begin(), decompressedData.end(), std::ostreambuf_iterator<char>(outputFile));
    }

    return true;
}

int main() {
    const std::string inputFilePath = "BACKS.bin";
    const std::string outputFilePath = "output_images.bin";

    if (extractAndDecompressImages(inputFilePath, outputFilePath)) {
        std::cout << "Extraction and decompression successful!\n";
    } else {
        std::cout << "Extraction and decompression failed.n";
    }

    return 0;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.