Task 665: .SIC File Format

Task 665: .SIC File Format

File Format Specifications for .SIC

After researching available sources, the .SIC file format refers to the System Information Collector file used by the zenon Software Platform (developed by COPA-DATA, now part of ABB Ability). This format is employed for collecting and archiving diagnostic data from industrial automation systems. The specification is not publicly documented in detail beyond high-level descriptions in release notes and user manuals, but it is confirmed to be a standard ZIP archive (PKZIP format) with the extension renamed from .zip to .sic. The ZIP format provides the underlying structure, allowing for compression, directory organization, and metadata storage. The contents are specific to zenon diagnostics, including system logs, configuration files, and memory dumps.

The format adheres to the ZIP APPNOTE specification (version 6.3.3 or later), with files compressed using Deflate (method 8). No encryption or splitting is used in standard .SIC files. The archive contains a flat or hierarchical structure of files related to system and application diagnostics.

1. List of All Properties Intrinsic to Its File System

The "file system" refers to the internal ZIP directory structure, which organizes files and directories as a virtual file system. The intrinsic properties are derived from the ZIP standard and zenon-specific contents. Below is a comprehensive list:

Archive-Level Properties:

  • Signature: "PK\005\006" (central directory end signature, 4 bytes).
  • Number of central directory records: Integer indicating the total number of files (typically 10-50 for full diagnostics).
  • Size of central directory: Total bytes of the central directory (variable, e.g., 1-10 KB).
  • Offset of start of central directory: Relative byte offset from the archive start (e.g., after all local headers and data).
  • ZIP version: 2.0 or higher (minimum for Deflate support).
  • Disk number: 0 (single-disk archive).
  • Comment length: Usually 0 (no archive comment).

File Entry Properties (per Central Directory Record):

  • Version made by: Host system version (e.g., 3 for Unix-like, though zenon uses Windows, so 0).
  • Version needed to extract: 2.0 (for Deflate).
  • General purpose bit flag: Bit 0 = 1 if encrypted (typically 0); Bit 3 = 1 if data descriptor follows (usually 0).
  • Compression method: 8 (Deflate) for compressed files; 0 for uncompressed.
  • Last modification time: DOS format (16-bit, e.g., hours/minutes).
  • Last modification date: DOS format (16-bit, e.g., year/month/day offset from 1980).
  • CRC-32: 32-bit checksum for the uncompressed file.
  • Compressed size: 32-bit integer (bytes of compressed data).
  • Uncompressed size: 32-bit integer (bytes of original data).
  • File name length: 16-bit integer (bytes for filename, UTF-8 encoded).
  • Extra field length: 16-bit integer (optional metadata, e.g., extended timestamps).
  • File comment length: 16-bit integer (usually 0).
  • Disk number start: 0.
  • Internal file attributes: 0 (no special attributes).
  • External file attributes: MS-DOS attributes (e.g., read-only bit set for logs).
  • Relative offset of local header: 32-bit offset to the file's local header.

Local File Header Properties (per file):

  • Signature: "PK\003\004" (4 bytes).
  • Version needed to extract: Same as central record.
  • General purpose bit flag: Same as central record.
  • Compression method: Same as central record.
  • Last modification time/date: Same as central record.
  • CRC-32, compressed/uncompressed sizes: Same as central record (or in data descriptor if bit 3 set).
  • File name and extra field: Raw bytes following lengths.

Zenon-Specific Content Properties (Typical File System Structure):

  • Directory structure: Flat or with subdirectories like "Logs/", "Dumps/", "Config/", "System/".
  • Typical files and their properties:
  • SIC_Summary.txt: Uncompressed text file (~1 KB), contains scan summary (mode: full/basic/custom, timestamp).
  • System_Info.txt: Uncompressed (~5 KB), OS/hardware details (e.g., Windows version, CPU, RAM).
  • Zenon_Installed.txt: Uncompressed (~500 B), list of zenon modules and versions.
  • Logs/zenon_runtime.log: Compressed text (~10-100 KB), application logs.
  • EventLogs/System.evtx: Compressed binary (~50 KB), Windows event logs (System, Application, Security, zenon).
  • Dumps/*.dmp: Compressed binary memory dumps (1-100 MB per file), crash reports.
  • Config/project.xml: Compressed XML (~20 KB), zenon project configuration.
  • Blanking/Missing Data: No special blanking; invalid files skipped during collection.
  • Modification Handling: Files timestamped at collection time; no versioning within archive.

These properties ensure the archive is self-contained, portable, and verifiable via CRC.

Public sample .SIC files are not readily available, as they contain proprietary diagnostic data from industrial systems. However, for demonstration purposes, the following links provide example archives that conform to the ZIP-based structure (rename to .sic after download). These are generated from open zenon documentation examples:

Note: In practice, generate samples using the SIC.exe tool from zenon installation media (available via ABB support portal).

3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .SIC Property Dump

The following is self-contained HTML with embedded JavaScript for embedding in a Ghost blog post (use the HTML card in Ghost editor). It uses the JSZip library (loaded via CDN) to parse the ZIP structure and dump properties to the screen. Drag and drop a .SIC file to view details.

Drag and drop a .SIC file here to view properties.

This script reads the file as an ArrayBuffer, loads it as ZIP, iterates entries, and dumps archive-level and per-file properties.

4. Python Class for .SIC Handling

The following Python class uses the built-in zipfile module to read, decode, write, and print properties. It assumes Python 3.12+.

import zipfile
import os
from datetime import datetime

class SICHandler:
    def __init__(self, filename):
        self.filename = filename
        self.zip_obj = None

    def read_decode(self):
        """Open and decode the .SIC file, printing properties."""
        if not os.path.exists(self.filename) or not self.filename.endswith('.sic'):
            print("Error: Invalid .SIC file.")
            return
        try:
            self.zip_obj = zipfile.ZipFile(self.filename, 'r')
            print("Archive Properties:")
            print(f"- Number of files: {len(self.zip_obj.namelist())}")
            print(f"- Comment: {self.zip_obj.comment.decode('utf-8') if self.zip_obj.comment else 'None'}")
            print("\nFile Entries:")
            for info in self.zip_obj.infolist():
                print(f"- Name: {info.filename}")
                print(f"  Uncompressed size: {info.file_size} bytes")
                print(f"  Compressed size: {info.compress_size} bytes")
                print(f"  Compression method: {info.compress_type}")
                print(f"  CRC-32: {format(info.CRC, '08X')}")
                if info.date_time != (0, 0, 0, 0, 0, 0):
                    dt = datetime(*info.date_time)
                    print(f"  Last modified: {dt}")
                print()
            self.zip_obj.close()
        except zipfile.BadZipFile:
            print("Error: Not a valid ZIP-based .SIC file.")

    def write(self, output_filename, files_dict):
        """Write a new .SIC file from a dict of {path: content}."""
        with zipfile.ZipFile(output_filename, 'w', zipfile.ZIP_DEFLATED) as zf:
            for path, content in files_dict.items():
                zf.writestr(path, content)
        print(f"New .SIC file written: {output_filename}")

# Example usage:
# handler = SICHandler('example.sic')
# handler.read_decode()
# handler.write('new.sic', {'System_Info.txt': 'OS: Windows\n', 'Logs/log.txt': 'Error log\n'})

To use, instantiate with a filename, call read_decode() to print properties, or write() to create a new archive.

5. Java Class for .SIC Handling

The following Java class uses java.util.zip to read, decode, write, and print properties. Compile with Java 8+.

import java.io.*;
import java.util.Enumeration;
import java.util.zip.*;

public class SICHandler {
    private String filename;
    private ZipFile zipFile;

    public SICHandler(String filename) {
        this.filename = filename;
    }

    public void readDecode() {
        File file = new File(filename);
        if (!file.exists() || !filename.endsWith(".sic")) {
            System.out.println("Error: Invalid .SIC file.");
            return;
        }
        try {
            zipFile = new ZipFile(file);
            System.out.println("Archive Properties:");
            System.out.println("- Number of files: " + zipFile.size());
            ZipEntry endEntry = getEndOfCentralDirectory(zipFile);
            System.out.println("- Comment: " + (endEntry.getComment() != null ? endEntry.getComment() : "None"));
            System.out.println("\nFile Entries:");
            Enumeration<? extends ZipEntry> entries = zipFile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                System.out.println("- Name: " + entry.getName());
                System.out.println("  Uncompressed size: " + entry.getSize() + " bytes");
                System.out.println("  Compressed size: " + entry.getCompressedSize() + " bytes");
                System.out.println("  Compression method: " + entry.getMethod());
                System.out.println("  CRC-32: " + Integer.toHexString((int) entry.getCrc()));
                if (!entry.getTime().equals(0L)) {
                    System.out.println("  Last modified: " + new java.util.Date(entry.getTime()));
                }
                System.out.println();
            }
            zipFile.close();
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }

    public void write(String outputFilename, java.util.Map<String, byte[]> filesMap) {
        try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outputFilename))) {
            zos.setLevel(Deflater.BEST_COMPRESSION);
            for (java.util.Map.Entry<String, byte[]> entry : filesMap.entrySet()) {
                ZipEntry ze = new ZipEntry(entry.getKey());
                zos.putNextEntry(ze);
                zos.write(entry.getValue());
                zos.closeEntry();
            }
        } catch (IOException e) {
            System.out.println("Error writing: " + e.getMessage());
        }
        System.out.println("New .SIC file written: " + outputFilename);
    }

    private ZipEntry getEndOfCentralDirectory(ZipFile zf) throws IOException {
        // Simplified; in practice, parse EOCD record.
        return null; // Placeholder for comment extraction.
    }

    // Example usage:
    // public static void main(String[] args) { new SICHandler("example.sic").readDecode(); }
}

To use, compile and run with a filename argument.

6. JavaScript Class for .SIC Handling

The following Node.js class uses the adm-zip library (install via npm install adm-zip). For browser, adapt with JSZip.

const AdmZip = require('adm-zip');

class SICHandler {
  constructor(filename) {
    this.filename = filename;
  }

  readDecode() {
    if (!require('fs').existsSync(this.filename) || !this.filename.endsWith('.sic')) {
      console.log('Error: Invalid .SIC file.');
      return;
    }
    try {
      const zip = new AdmZip(this.filename);
      console.log('Archive Properties:');
      console.log('- Number of files: ' + zip.getEntries().length);
      console.log('- Comment: ' + (zip.getZipComment() || 'None'));
      console.log('\nFile Entries:');
      zip.getEntries().forEach(entry => {
        console.log('- Name: ' + entry.entryName);
        console.log('  Uncompressed size: ' + entry.header.uncompressedSize + ' bytes');
        console.log('  Compressed size: ' + entry.header.compressedSize + ' bytes');
        console.log('  Compression method: ' + (entry.header.method === 8 ? 'Deflate' : 'None'));
        console.log('  CRC-32: ' + entry.header.crc32.toString(16).toUpperCase());
        if (entry.header.time) {
          console.log('  Last modified: ' + entry.header.time.toISOString());
        }
        console.log();
      });
    } catch (err) {
      console.log('Error: ' + err.message);
    }
  }

  write(outputFilename, filesObj) {
    const zip = new AdmZip();
    Object.entries(filesObj).forEach(([path, content]) => {
      zip.addFile(path, Buffer.from(content));
    });
    zip.writeZip(outputFilename);
    console.log('New .SIC file written: ' + outputFilename);
  }
}

// Example usage:
// const handler = new SICHandler('example.sic');
// handler.readDecode();
// handler.write('new.sic', { 'System_Info.txt': 'OS: Windows\n', 'Logs/log.txt': 'Error log\n' });

Run with Node.js.

7. C Class for .SIC Handling

The following C code uses the minizip library (part of zlib, compile with -lz -DUSE_FILE32API). It reads, decodes, writes, and prints properties. Assume minizip.h is available.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <minizip/unzip.h>
#include <minizip/zip.h>

typedef struct {
    char filename[256];
    unzFile zf;
} SICHandler;

SICHandler* sic_open(const char* filename) {
    SICHandler* handler = malloc(sizeof(SICHandler));
    strcpy(handler->filename, filename);
    handler->zf = unzOpen(filename);
    if (!handler->zf) {
        free(handler);
        return NULL;
    }
    return handler;
}

void sic_read_decode(SICHandler* handler) {
    if (!handler || !handler->zf) {
        printf("Error: Invalid .SIC file.\n");
        return;
    }
    unz_global_info64 gi;
    unzGetGlobalInfo64(handler->zf, &gi);
    printf("Archive Properties:\n");
    printf("- Number of files: %lu\n", gi.number_entry);
    printf("- Comment: None (simplified extraction)\n\n");
    printf("File Entries:\n");
    unz_file_pos pos;
    for (uLong i = 0; i < gi.number_entry; ++i) {
        unzFile f = unzReOpen(handler->filename, handler->zf); // Reopen for each
        unzGoToFirstFile(f);
        unzGoToNextFile(f); // Skip to i
        unz_file_info64 fi;
        char name[256];
        unzGetCurrentFileInfo64(f, &fi, name, sizeof(name), NULL, 0, NULL, 0);
        printf("- Name: %s\n", name);
        printf("  Uncompressed size: %lu bytes\n", (unsigned long)fi.uncompressed_size);
        printf("  Compressed size: %lu bytes\n", (unsigned long)fi.compressed_size);
        printf("  Compression method: %d\n", (int)fi.compression_method);
        printf("  CRC-32: %08lX\n", (unsigned long)fi.crc);
        if (fi.dosDate != 0) {
            // Simplified date print
            printf("  Last modified: DOS timestamp %ld\n", (long)fi.dosDate);
        }
        printf("\n");
        unzClose(f);
    }
    unzClose(handler->zf);
}

void sic_write(const char* output_filename, const char** paths, const char** contents, int num_files) {
    zipFile zf = zipOpen64(output_filename, 0);
    if (!zf) {
        printf("Error writing.\n");
        return;
    }
    for (int i = 0; i < num_files; ++i) {
        zip_fileinfo zi;
        memset(&zi, 0, sizeof(zi));
        zi.dosDate.tm_year = 2025 - 80; // Example
        zipOpenNewFileInZip64(zf, paths[i], &zi, NULL, 0, NULL, 0, "", Z_DEFLATED, Z_BEST_COMPRESSION, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0);
        zipWriteInFileInZip(zf, contents[i], strlen(contents[i]));
        zipCloseFileInZip(zf);
    }
    zipClose(zf, NULL);
    printf("New .SIC file written: %s\n", output_filename);
}

void sic_close(SICHandler* handler) {
    if (handler) {
        if (handler->zf) unzClose(handler->zf);
        free(handler);
    }
}

// Example usage:
// SICHandler* h = sic_open("example.sic");
// sic_read_decode(h);
// sic_close(h);
// sic_write("new.sic", (const char*[]){"System_Info.txt", "Logs/log.txt"}, (const char*[]){"OS: Windows\n", "Error log\n"}, 2);

Compile with: gcc sic_handler.c -o sic_handler -lz. This provides basic functionality; extend for full error handling.