// File: @nftculture/nftc-contracts-private/contracts/access/v2/OwnableDeferral.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

/**
 * @title OwnableDeferral
 * @author @NiftyMike | @NFTCulture
 * @dev Implements checks for contract admin operations. Will be Backed by
 * OZ Ownable.
 *
 * This contract is helpful when a contract tree gets complicated,
 * and multiple contracts need to leverage Ownable.
 *
 * Sample Implementation:
 *
 * modifier isOwner() override(...) {
 *     _isOwner();
 *     _;
 * }
 *
 * function _isOwner() internal view override(...) {
 *     _checkOwner();
 * }
 */
abstract contract OwnableDeferral {
    modifier isOwner() virtual;

    function _isOwner() internal view virtual;
}

// File: @nftculture/nftc-contracts-private/contracts/access/v2/OwnableDeferralResolution.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// OZ Libraries
import '@openzeppelin/contracts/access/Ownable.sol';

// Local References
import './OwnableDeferral.sol';

// Error Codes
error CallerIsNotOwner();

/**
 * @title OwnableDeferralResolution
 * @author @NiftyMike | @NFTCulture
 * @dev Implements checks for contract admin (Owner) operations. Backed by OZ Ownable.
 *
 * Ownership is assigned to contract deployer wallet by default.
 *
 * NOTE: IMPORTANT - This resolution will work great in a simple inheritance situation,
 * however, if multiple inheritance is involved, it might not adequately satisfy
 * override (...) conditions. In those scenarios, this code should be used as a
 * starting point and then adjusted appropriately.
 */
contract OwnableDeferralResolution is Ownable, OwnableDeferral {
    modifier isOwner() override {
        _isOwner();
        _;
    }

    function _isOwner() internal view override {
        // Same as _checkOwner() but using error code instead of a require statement.
        if (owner() != _msgSender()) revert CallerIsNotOwner();
    }
}

// File: @nftculture/nftc-contracts-private/contracts/metadata/v1/ArtDatastoreManager.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// Local References
import '../../access/v2/OwnableDeferral.sol';
import './interfaces/IDynamicArt.sol';

// Error Codes
error ArtPieceCountExceeded();
error ArtPieceDoesNotExist();
error ArtPieceAlreadyCreated();
error NullTokenType();

/**
 * @title ArtDatastoreManager
 * @author @NiftyMike | @NFTCulture
 * @dev A class for managing the datastore that holds Art pieces as either strings or bytes.
 */
abstract contract ArtDatastoreManager is IDynamicArtV1, OwnableDeferral {
    // Storage for Token Attribute Definitions
    mapping(uint256 => DynamicArtV1) private _artObjects;
    uint64[] private _artPieceTokenTypes;

    uint256 private immutable _maxNumberOfArtPieces;

    constructor(uint256 __maxNumberOfArtPieces) {
        _maxNumberOfArtPieces = __maxNumberOfArtPieces;
    }

    function createArtPieces(
        uint256[] calldata tokenTypes,
        string[] calldata stringAssets,
        string[] calldata backgrounds,
        bytes[] calldata bytesAssets
    ) external isOwner {
        require(
            tokenTypes.length == stringAssets.length &&
                tokenTypes.length == backgrounds.length &&
                tokenTypes.length == bytesAssets.length,
            'Invalid number of assets'
        );

        uint256 idx;
        for (idx = 0; idx < tokenTypes.length; idx++) {
            _createArtPiece(tokenTypes[idx], stringAssets[idx], backgrounds[idx], bytesAssets[idx]);
        }
    }

    function createArtPiece(
        uint256 tokenType,
        string calldata stringAsset,
        string calldata backgroundColor,
        bytes calldata bytesAsset
    ) external isOwner {
        _createArtPiece(tokenType, stringAsset, backgroundColor, bytesAsset);
    }

    function updateArtPiece(
        uint256 tokenType,
        string calldata stringAsset,
        string calldata backgroundColor,
        bytes calldata bytesAsset
    ) external isOwner {
        if (tokenType == 0) revert NullTokenType();
        DynamicArtV1 memory current = _artObjects[tokenType];
        if (current.tokenType == 0) revert ArtPieceDoesNotExist();

        current.tokenType = tokenType;

        if (bytes(stringAsset).length > 0) {
            current.encodedArt = stringAsset;
        }

        if (bytes(backgroundColor).length > 0) {
            current.backgroundColor = backgroundColor;
        }

        if (bytesAsset.length > 0) {
            current.encodedArtBytes = bytesAsset;
        }

        _artObjects[tokenType] = current;
    }

    function _createArtPiece(
        uint256 tokenType,
        string calldata stringAsset,
        string calldata backgroundColor,
        bytes calldata bytesAsset
    ) internal {
        if (tokenType == 0) revert NullTokenType();
        if (_maxNumberOfArtPieces > 0 && _artPieceTokenTypes.length + 1 > _maxNumberOfArtPieces)
            revert ArtPieceCountExceeded();
        if (_artObjects[tokenType].tokenType > 0) revert ArtPieceAlreadyCreated();

        DynamicArtV1 memory current;
        current.tokenType = tokenType;
        current.encodedArt = stringAsset;
        current.backgroundColor = backgroundColor;
        current.encodedArtBytes = bytesAsset;

        _artObjects[tokenType] = current;
        _artPieceTokenTypes.push(uint64(tokenType));
    }

    function getArtObject(uint256 tokenType) external view returns (DynamicArtV1 memory) {
        return _getArtObject(tokenType);
    }

    function _getArtObject(uint256 tokenType) internal view returns (DynamicArtV1 memory) {
        return _artObjects[tokenType];
    }

    function getArtPieceTokenTypes() external view returns (uint64[] memory) {
        return _getArtPieceTokenTypes();
    }

    function _getArtPieceTokenTypes() internal view returns (uint64[] memory) {
        return _artPieceTokenTypes;
    }
}

// File: @nftculture/nftc-contracts-private/contracts/metadata/v1/interfaces/IChainNativeArtProducer.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

/**
 * @title IChainNativeArtProducer
 * @author @NiftyMike | @NFTCulture
 * @dev Super thin interface definition for a contract that
 * produces art in a chain native way.
 */
interface IChainNativeArtProducer {
    /**
     * Given a token type, return a string that can be directly inserted into an
     * NFT metadata attribute such as image.
     *
     * @param tokenType type of the art piece
     */
    function getArtAsString(uint256 tokenType) external view returns (string memory);

    /**
     * Given a token type, return a string that can be directly inserted into an
     * NFT metadata attribute such as animation_url.
     *
     * @param tokenType type of the art piece
     */
    function getAnimationAsString(uint256 tokenType) external view returns (string memory);
}

// File: @nftculture/nftc-contracts-private/contracts/metadata/v1/interfaces/IDynamicArt.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

/**
 * @title IDynamicArtV1
 * @author @NFTMike | @NFTCulture
 * @dev Interface that defines the datastructure of on-chain artwork.
 */
interface IDynamicArtV1 {
    struct DynamicArtV1 {
        uint256 tokenType;
        string encodedArt;
        string backgroundColor;
        bytes encodedArtBytes;
    }
}

// File: @nftculture/nftc-contracts-private/contracts/metadata/v1/interfaces/IScriptyStorageProvider.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

///////////////////////////////////////////////////////////
// ░██████╗░█████╗░██████╗░██╗██████╗░████████╗██╗░░░██╗ //
// ██╔════╝██╔══██╗██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗░██╔╝ //
// ╚█████╗░██║░░╚═╝██████╔╝██║██████╔╝░░░██║░░░░╚████╔╝░ //
// ░╚═══██╗██║░░██╗██╔══██╗██║██╔═══╝░░░░██║░░░░░╚██╔╝░░ //
// ██████╔╝╚█████╔╝██║░░██║██║██║░░░░░░░░██║░░░░░░██║░░░ //
// ╚═════╝░░╚════╝░╚═╝░░╚═╝╚═╝╚═╝░░░░░░░░╚═╝░░░░░░╚═╝░░░ //
///////////////////////////////////////////////////////////

/**
 * @title IScriptyStorageProvider
 * @author @NiftyMike | @NFTCulture
 * @dev An interface wrapper for a ScriptStorage contract, when we only need to read data from it.
 * @dev Special thanks to @0xthedude and @xtremetom for providing this library.
 * See: https://github.com/intartnft/scripty.sol
 */
interface IScriptyStorageProvider {
    /**
     * @notice Get the full script
     * @param name - Name given to the script. Eg: threejs.min.js_r148
     * @param data - Arbitrary data. Not used by this contract.
     * @return script - Full script from merged chunks
     */
    function getScript(string memory name, bytes memory data) external view returns (bytes memory script);

    /**
     * @notice Get script's chunk pointer list
     * @param name - Name given to the script. Eg: threejs.min.js_r148
     * @return pointers - List of pointers
     */
    function getScriptChunkPointers(string memory name) external view returns (address[] memory pointers);
}

// File: @nftculture/nftc-contracts-private/contracts/metadata/v1/interfaces/ISVGConstructor.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/**
 * @title ISVGConstructor
 * @author @NiftyMike | @NFTCulture
 * @dev A simple interface for rendering an SVG.
 */
interface ISVGConstructor {
    function constructFromPNG(
        bool hasBackground,
        string memory backgroundColor,
        string memory pngDataURI
    ) external view returns (string memory);
}

// File: @nftculture/nftc-contracts-private/contracts/metadata/v1/PNGBackedSVGArt.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// OZ Libraries
import '@openzeppelin/contracts/utils/Base64.sol';

// Local References
import './ArtDatastoreManager.sol';
import './interfaces/ISVGConstructor.sol';

/**
 * @title PNGBackedSVGArt
 * @author @NiftyMike | @NFTCulture
 * @dev An on-chain contract that constructs a PNG backed SVG object.
 */
abstract contract PNGBackedSVGArt is ArtDatastoreManager {
    function _getSvgConstructor(uint256 tokenType) internal view virtual returns (ISVGConstructor svgConstructor);

    function _getSvgDataURI(uint256 tokenType) internal view returns (string memory) {
        DynamicArtV1 memory artObject = _getArtObject(tokenType);
        bool hasBackground = bytes(artObject.backgroundColor).length > 0;
        string memory onChainEncodedArt = _getArtObject(tokenType).encodedArt;
        string memory svg = _constructSvg(
            tokenType,
            hasBackground,
            artObject.backgroundColor,
            _convertBase64PngToDataURI(onChainEncodedArt)
        );

        return _convertSvgToDataURI(svg);
    }

    function _getPngDataUri(uint256 tokenType) internal view returns (string memory) {
        string memory onChainEncodedArt = _getArtObject(tokenType).encodedArt;
        return _convertBase64PngToDataURI(onChainEncodedArt);
    }

    function _convertBase64PngToDataURI(string memory base64Png) internal pure returns (string memory) {
        return string.concat('data:image/png;base64,', base64Png);
    }

    function _convertSvgToDataURI(string memory svg) internal pure returns (string memory) {
        return string.concat('data:image/svg+xml;base64,', Base64.encode(bytes(svg)));
    }

    function _convertBytesToDataURI(
        bytes memory artBytes,
        string memory mimeType
    ) internal pure returns (string memory) {
        return string.concat('data:', mimeType, ';base64,', Base64.encode(artBytes));
    }

    function _constructSvg(
        uint256 tokenType,
        bool hasBackground,
        string memory backgroundColor,
        string memory pngDataURI
    ) internal view virtual returns (string memory) {
        return _getSvgConstructor(tokenType).constructFromPNG(hasBackground, backgroundColor, pngDataURI);
    }
}

// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @openzeppelin/contracts/utils/Base64.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides a set of functions to operate with Base64 strings.
 *
 * _Available since v4.5._
 */
library Base64 {
    /**
     * @dev Base64 Encoding/Decoding Table
     */
    string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /**
     * @dev Converts a `bytes` to its Bytes64 `string` representation.
     */
    function encode(bytes memory data) internal pure returns (string memory) {
        /**
         * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
         * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
         */
        if (data.length == 0) return "";

        // Loads the table into memory
        string memory table = _TABLE;

        // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
        // and split into 4 numbers of 6 bits.
        // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
        // - `data.length + 2`  -> Round up
        // - `/ 3`              -> Number of 3-bytes chunks
        // - `4 *`              -> 4 characters for each chunk
        string memory result = new string(4 * ((data.length + 2) / 3));

        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the lookup table (skip the first "length" byte)
            let tablePtr := add(table, 1)

            // Prepare result pointer, jump over length
            let resultPtr := add(result, 32)

            // Run over the input, 3 bytes at a time
            for {
                let dataPtr := data
                let endPtr := add(data, mload(data))
            } lt(dataPtr, endPtr) {

            } {
                // Advance 3 bytes
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)

                // To write each character, shift the 3 bytes (18 bits) chunk
                // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                // and apply logical AND with 0x3F which is the number of
                // the previous character in the ASCII table prior to the Base64 Table
                // The result is then added to the table to get the character to write,
                // and finally write it in the result pointer but with a left shift
                // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits

                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance
            }

            // When data `bytes` is not exactly 3 bytes long
            // it is padded with `=` characters at the end
            switch mod(mload(data), 3)
            case 1 {
                mstore8(sub(resultPtr, 1), 0x3d)
                mstore8(sub(resultPtr, 2), 0x3d)
            }
            case 2 {
                mstore8(sub(resultPtr, 1), 0x3d)
            }
        }

        return result;
    }
}

// File: @openzeppelin/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// File: contracts/KH87A/PixelPioneerArtwork.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

// Local References
import './PixelPioneerArtworkBase.sol';

/**
 * @title PixelPioneerArtwork .
 *                            .+.
 *             -:       .:    :+.
 *    --      :=.       :+    -=                                         .     ..                     .:-:
 *    .+.    --         :+    =-   .:.                                 :++-.  .++=: ..              :==:-++==:
 *     =-  .=:          :=    +.  -=::.:.               -.        .==..+=-+=  =+:+-.=====:         -+:  =+:.-+.
 *     =-:=-       :.   ==----+  :+   =+:.-+-   ..                 ++..++++:  =++=-+:   -+:      .++=--=++=-++.
 *     -=--==-.    :.   +-::.=-  +- .=-+:=::+. -+=     .:-.        =+.  .+=   :+=.=+:   -+:       +=:::=+..-+:
 *     --    .--       .+.  .+:  ==-=: -+:  ===:.======-::+-       =+.  :+:   =+:-+-   :+=       .+=  .+--+-.
 *     .-              .+.  :+.   :.                      :+.      .-    .    .-=-.    =+.        :==-=+=:.
 *                      +:  .+                            :-                           ..            ..
 *
 *                                                On-Chain Artwork
 */
contract PixelPioneerArtwork is PixelPioneerArtworkBase {
    uint256 private constant maxNumberOfArtPieces = 5; // Capped at 5 total pieces.

    constructor() ArtDatastoreManager(maxNumberOfArtPieces) {
        // Implementation version: v1.0.0
    }
}

// File: contracts/KH87A/PixelPioneerArtworkBase.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// NFTC Prerelease Contracts
import '@nftculture/nftc-contracts-private/contracts/access/v2/OwnableDeferralResolution.sol';
import '@nftculture/nftc-contracts-private/contracts/metadata/v1/interfaces/IChainNativeArtProducer.sol';
import '@nftculture/nftc-contracts-private/contracts/metadata/v1/interfaces/IScriptyStorageProvider.sol';
import '@nftculture/nftc-contracts-private/contracts/metadata/v1/PNGBackedSVGArt.sol';

// Error Codes
error UnrecognizedExtension();

/**
 * @title PixelPioneerArtworkBase
 * @author @NiftyMike | @NFTCulture
 * @dev This contract implements the NFTC IChainNativeArtProducer API, which means
 * it contains logic to return the contents of an NFT image / animation_url field
 * for a given NFT token type.
 *
 * In this particular implementation, the image approach is fully on-chain. The returned
 * value represents a SVG that is base64 encoded. The string representation can be copy
 * and pasted into a new browser tab to display the image.
 *
 * The SVGs constructed by this contract are backed by bitmaps stored as base64 encoded
 * PNG files. The PNG files are via transactions that are sent after the contract is
 * deployed. The maximum base64 string size is about 34KB, and is limited by the max block
 * size of the block chain, which is around 30M gas.
 *
 * In theory, this same exact approach could be used with larger backing PNGs (ignoring
 * cost implications), if the base64 string was chunked across multiple transactions, but
 * we have no need to do that with this project.
 */
abstract contract PixelPioneerArtworkBase is PNGBackedSVGArt, IChainNativeArtProducer, OwnableDeferralResolution {
    string private constant extension_iff = 'iff';
    string private constant extension_pict = 'pct';

    address private _svgRenderer;

    struct ArchivalMetadata {
        address scriptyStorageProvider;
        uint256 tokenType;
        string scriptName;
        string extension;
    }

    // File extension -> tokenType (but zero indexed)
    mapping(string => ArchivalMetadata[]) private _metadata;

    function getArtAsString(uint256 tokenType) external view returns (string memory) {
        // This project returns the SVG data as the art string.
        return _getSvgDataURI(tokenType);
    }

    function getAnimationAsString(uint256) external pure returns (string memory) {
        // This art is not animated.
        return '';
    }

    /**
     * @notice Get the Art Asset for a TokenType as an SVG encoded into a data URI.
     *
     * @param tokenType the token type of the art piece.
     */
    function getSvgArt(uint256 tokenType) external view returns (string memory) {
        return _getSvgDataURI(tokenType);
    }

    /**
     * @notice Get the Art Asset for a TokenType as a PNG encoded into a data URI.
     *
     * @param tokenType the token type of the art piece.
     */
    function getPngArt(uint256 tokenType) external view returns (string memory) {
        return _getPngDataUri(tokenType);
    }

    /**
     * @notice Get the Art Asset for a TokenType as a PICT data URI.
     *
     * Important: This will only function if the PICT files have been recovered.
     *
     * @param tokenType the token type of the art piece.
     */
    function getPICTArt(uint256 tokenType) external view returns (string memory) {
        return _getPICTDataUri(tokenType);
    }

    /**
     * @notice Get the Art Asset for a TokenType as an IFF data URI.
     *
     * Important: This will only function if the IFF files have been recovered.
     *
     * @param tokenType the token type of the art piece.
     */
    function getIFFArt(uint256 tokenType) external view returns (string memory) {
        return _getIFFDataUri(tokenType);
    }

    /**
     * @notice Get the closest source archival asset for a token.
     *
     * @param extension the file extension of the archival asset.
     * @param tokenType the token type of the art piece.
     */
    function getRawArchivalArt(string calldata extension, uint256 tokenType) external view returns (bytes memory) {
        if (keccak256(abi.encodePacked(extension)) == keccak256(abi.encodePacked(extension_iff))) {
            return _getIFFBytes(tokenType);
        }

        if (keccak256(abi.encodePacked(extension)) == keccak256(abi.encodePacked(extension_pict))) {
            return _getPICTBytes(tokenType);
        }

        revert UnrecognizedExtension();
    }

    /**
     * @notice Get the metadata of the source archival asset for a token.
     *
     * @param extension the file extension of the archival asset.
     * @param tokenType the token type of the art piece.
     */
    function getArchivalMetadata(
        string calldata extension,
        uint256 tokenType
    ) external view returns (ArchivalMetadata memory) {
        return _metadata[extension][tokenType - 1];
    }

    /**
     * @notice Admin function to configure the archival metadata for all tokens for a particular file extension.
     *
     * @param provider address of the archival data store.
     * @param scriptNames the filenames that will be saved in the data store.
     * @param extension the native os extension of the files that will be saved.
     */
    function setArchivalMetadata(
        address provider,
        string[] calldata scriptNames,
        string calldata extension
    ) external isOwner {
        uint256 idx; // note, zero indexed.
        for (idx; idx < scriptNames.length; idx++) {
            // this is implicitly assuming tokenTypes are 1-5.
            ArchivalMetadata memory metadata = ArchivalMetadata(provider, idx + 1, scriptNames[idx], extension);
            _metadata[extension].push(metadata);
        }
    }

    /**
     * @notice Admin function to update the archival metadata details for a single file.
     *
     * @param provider address of the archival data store.
     * @param tokenType the tokenType of the file.
     * @param scriptName the filename that will be saved in the data store.
     * @param extension the native os extension of the files that will be saved.
     */
    function updateArchivalMetadata(
        address provider,
        uint256 tokenType,
        string calldata scriptName,
        string calldata extension
    ) external isOwner {
        uint256 idx = tokenType - 1;
        ArchivalMetadata memory metadata = _metadata[extension][idx];
        require(metadata.tokenType == tokenType, 'invalid addressing');

        _metadata[extension][idx].scriptyStorageProvider = provider;
        _metadata[extension][idx].scriptName = scriptName;
        _metadata[extension][idx].extension = extension;
    }

    /**
     * @notice Admin function to clear out the entire Metadata Archive for a file extension.
     *
     * Important: This has no impact on the underlying datastore.
     *
     * @param extension the native os extension of the files that will be saved.
     */
    function clearArchivalMetadata(string calldata extension) external isOwner {
        ArchivalMetadata[] memory allMetadataForExtension = _metadata[extension];
        require(allMetadataForExtension.length > 0, 'array is empty');
        delete _metadata[extension];
    }

    function setSvgRenderer(address svgRenderer) external isOwner {
        _svgRenderer = svgRenderer;
    }

    function getSvgRenderer() external view returns (address) {
        return _svgRenderer;
    }

    function _getSvgConstructor(uint256) internal view override returns (ISVGConstructor svgConstructor) {
        return ISVGConstructor(_svgRenderer);
    }

    function _getIFFDataUri(uint256 tokenType) internal view returns (string memory) {
        bytes memory artBytes = _getIFFBytes(tokenType);

        return _convertBytesToDataURI(artBytes, 'image/x-iff');
    }

    function _getIFFBytes(uint256 tokenType) internal view returns (bytes memory) {
        uint256 idx = tokenType - 1;
        ArchivalMetadata memory metadata = _metadata[extension_iff][idx];
        require(metadata.tokenType == tokenType, 'IFF not configured');

        IScriptyStorageProvider provider = IScriptyStorageProvider(metadata.scriptyStorageProvider);
        return provider.getScript(metadata.scriptName, '');
    }

    function _getPICTDataUri(uint256 tokenType) internal view returns (string memory) {
        bytes memory artBytes = _getPICTBytes(tokenType);

        return _convertBytesToDataURI(artBytes, 'image/x-pict');
    }

    function _getPICTBytes(uint256 tokenType) internal view returns (bytes memory) {
        uint256 idx = tokenType - 1;
        ArchivalMetadata memory metadata = _metadata[extension_pict][idx];
        require(metadata.tokenType == tokenType, 'PICT not configured');

        IScriptyStorageProvider provider = IScriptyStorageProvider(metadata.scriptyStorageProvider);
        return provider.getScript(metadata.scriptName, '');
    }
}