// Contract: PunkBlocks
// Target function: function svgFromPunkID(uint256 _tokenID) external view returns (string memory)
4e530040e6d8660000002f4944415478da62c00bea181818fe333030fe6f60603ed8c0c0dc00c1ec0c58302312666e606067079b00080000ffffd1030b1b1e3ca1240000000049454e44ae426082";
        index[nextId] = bytes32(hex"8c0e60b85ff0f8be1a87b28ae066a63dcc3c02589a213b0856321a73882515f9");
        nextId++;
        b = blocks[bytes32(hex"047228ad95cec16eb926f7cd21ac9cc9a3288d911a6c2917a24555eac7a2c0e2")];
        b.layer = Layer(1);
        b.dataFemale = hex"89504e470d0a1a0a0000000d4948445200000018000000180806000000e0773df80000002f4944415478da6262a03118b560d482510b462d18b5806e164c916250036142620302462379d482216001200000ffff8e55037f0295ca130000000049454e44ae426082";
        index[nextId] = bytes32(hex"047228ad95cec16eb926f7cd21ac9cc9a3288d911a6c2917a24555eac7a2c0e2");
        nextId++;
        b = blocks[bytes32(hex"e651be5dd43261e6e9c1098ec114ab5c44e7cb07377dc674336f1b3d34428fe4")];
        b.layer = Layer(3);
        b.dataFemale = hex"89504e470d0a1a0a0000000d4948445200000018000000180103000000dab9afbb00000006504c5445000000000000a567b9cf0000000174524e530040e6d866000000364944415478da62c00a0a1818187f303030bf6060607fc0c0c07e808181bd8181811f861920980f19373030c83320301400020000ffff746d06dbf514328c0000000049454e44ae426082";
        index[nextId] = bytes32(hex"e651be5dd43261e6e9c1098ec114ab5c44e7cb07377dc674336f1b3d34428fe4");
        nextId++;
        b = blocks[bytes32(hex"a71068a671b554f75b7cc31ce4f8d63c377f276333d11989e77bc4a9205b5e42")];
        b.layer = Layer(3);
        b.dataFemale = hex"89504e470d0a1a0a0000000d4948445200000018000000180103000000dab9afbb00000006504c5445000000000000a567b9cf0000000174524e530040e6d866000000294944415478da9cc0310d00300800b01ecbae65125042f02f8b9b971a2e3e0285c4c3b1d5010000ffff337800bd4717cafb0000000049454e44ae426082";
        index[nextId] = bytes32(hex"a71068a671b554f75b7cc31ce4f8d63c377f276333d11989e77bc4a9205b5e42");
        nextId++;
        b = blocks[bytes32(hex"1cd064e6db4e7c5180ccf5f2afe1370c6539b525fe3bea9c358f24a7cbdb50ad")];
        b.layer = Layer(8);
        b.dataFemale = hex"89504e470d0a1a0a0000000d4948445200000018000000180103000000dab9afbb00000006504c5445000000000000a567b9cf0000000174524e530040e6d866000000124944415478da62a01a90c12a0a080000ffff02c8001d72b777ad0000000049454e44ae426082";
        index[nextId] = bytes32(hex"1cd064e6db4e7c5180ccf5f2afe1370c6539b525fe3bea9c358f24a7cbdb50ad");
        nextId++;
        b = blocks[bytes32(hex"d5de5c20969a9e22f93842ca4d65bac0c0387225cee45a944a14f03f9221fd4a")];
        b.layer = Layer(6);
        b.dataFemale = hex"89504e470d0a1a0a0000000d4948445200000018000000180806000000e0773df8000000484944415478da6262a03118b560d482510b8683052c04e4ff13690e23b916309c7ef6b710c6369562eec725466e103162e13312eb7abac401c368321db560d402da5b00080000ffffbe8c09370dfcbb5e0000000049454e44ae426082";
        index[nextId] = bytes32(hex"d5de5c20969a9e22f93842ca4d65bac0c0387225cee45a944a14f03f9221fd4a");
        nextId++;
        b = blocks[bytes32(hex"2c382a7f1f32a6a2d0e9b0d378cb95e3dad70fe6909ff13888fe2a250bd10bb0")];
        b.layer = Layer(12);
        b.dataFemale = hex"89504e470d0a1a0a0000000d4948445200000018000000180103000000dab9afbb00000006504c5445000000dfdfdf337edd570000000174524e530040e6d866000000124944415478da62a01928009380000000ffff0300007135fa2b640000000049454e44ae426082";
        index[nextId] = bytes32(hex"2c382a7f1f32a6a2d0e9b0d378cb95e3dad70fe6909ff13888fe2a250bd10bb0");
        nextId++;
    }

    /**
    * @dev registerBlock allows anybody to add a new block to the contract.
    *   Either _dataMale or _dataFemale, or both, must contain a byte stream of a png file.
    *   It's best if the png is using an 'index palette' and the lowest bit depth possible,
    *   while keeping the highest compression setting.
    * @param _dataMale png data for the male version, 24x24
    * @param _dataFemale png data for the female version, 24x24
    * @param _layer 0 to 12, corresponding to the Layer enum type.
    */
    function registerBlock(
        bytes calldata _dataMale,
        bytes calldata _dataFemale,
        uint8 _layer,
        string memory _name) external
    {
        bytes32 key = keccak256(abi.encodePacked(_name));
        Block storage b = blocks[key];
        require (_layer < 13, "invalid _layer");
        unchecked{
            require (_dataMale.length + _dataFemale.length > 0, "no data");
            require (b.dataMale.length + b.dataFemale.length == 0, "slot taken");
        }
        if (_dataMale.length > 0) {
            require (_validatePng(_dataMale), "invalid m png");
            b.dataMale = _dataMale;
        }
        if (_dataFemale.length > 0) {
            require (_validatePng(_dataFemale), "invalid f png");
            b.dataFemale = _dataFemale;
        }
        b.layer = Layer(_layer);
        index[nextId] = key;
        emit NewBlock(msg.sender, nextId);
        unchecked{nextId++;}
    }

    /**
    * @dev Just a limited png validation. Only verifies that the png is 24x24 and has a png structure,
    *   but doesn't validate any checksums or any other validation
    */
    function _validatePng(bytes calldata _data) pure internal returns (bool) {
        unchecked {
            if (_data.length < 8) {
                return false;
            }
            bytes memory pngHeader = bytes("\x89PNG\r\n\x1a\n"); // first 8 bytes
            uint pos;
            while (pos < 8) {
                if (_data[pos] != pngHeader[pos]) {
                    return false;
                }
                pos++;
            }
            int32 chunkLen;
            while (true) {
                 // next 4 bytes represent a big-endian int32, the chunk length
                chunkLen = int32(uint32(uint8(_data[pos+3]))
                | uint32(uint8(_data[pos+2]))<<8
                | uint32(uint8(_data[pos+1]))<<16
                    | uint32(uint8(_data[pos]))<<24);
                pos += 4;
                if (
                    _data[pos] == bytes1("I") &&
                    _data[pos+1] == bytes1("H") &&
                    _data[pos+2] == bytes1("D") &&
                    _data[pos+3] == bytes1("R")) { // IHDR
                    if (24 != int32(uint32(uint8(_data[pos+7]))
                        | uint32(uint8(_data[pos+6]))<<8
                        | uint32(uint8(_data[pos+5]))<<16
                        | uint32(uint8(_data[pos+4]))<<24)) { // width needs to be 24
                        return false;
                    }
                    if (24 != int32(uint32(uint8(_data[pos+11]))
                    | uint32(uint8(_data[pos+10]))<<8
                    | uint32(uint8(_data[pos+9]))<<16
                        | uint32(uint8(_data[pos+8]))<<24)) { // height needs to be 24
                        return false;
                    }
                } else if (
                    _data[pos] == bytes1("P") &&
                    _data[pos+1] == bytes1("L") &&
                    _data[pos+2] == bytes1("T") &&
                    _data[pos+3] == bytes1("E")) {  // PLTE
                } else if (
                    _data[pos] == bytes1("t") &&
                    _data[pos+1] == bytes1("R") &&
                    _data[pos+2] == bytes1("N") &&
                    _data[pos+3] == bytes1("S")) {  // tRNS
                } else if (
                    _data[pos] == bytes1("I") &&
                    _data[pos+1] == bytes1("D") &&
                    _data[pos+2] == bytes1("A") &&
                    _data[pos+3] == bytes1("T")) {  // IDAT
                } else if (
                    _data[pos] == bytes1("I") &&
                    _data[pos+1] == bytes1("E") &&
                    _data[pos+2] == bytes1("N") &&
                    _data[pos+3] == bytes1("D")) {  // IEND
                    return true;                    // png is valid (without checking the CRC)
                } else {
                    return false;
                }
                pos += 4 + uint(int(chunkLen)) + 4; // skip the payload, ignore CRC
            }
        } //unchecked
        return true;
    }

    /**
    * @dev svgFromNames returns the svg data as a string
    * @param _attributeNames a list of attribute names, eg "Male 1", "Goat"
    *    must have at least 1 layer 0 attribute (eg. Male, Female, Alien, Ape, Zombie)
    */
    function svgFromNames(string[] calldata _attributeNames) external view returns (string memory){
        bytes32[] memory attributeKeys = new bytes32[](_attributeNames.length);
        for (uint i = 0; i < _attributeNames.length; i++) {
            attributeKeys[i] = keccak256(
                abi.encodePacked(_attributeNames[i])
            );
        }
        return _svg(attributeKeys);
    }

    /**
    * @dev svgFromKeys returns the svg data as a string
    * @param _attributeKeys a list of attribute names that have been hashed,
    *    eg keccak256("Male 1"), keccak256("Goat")
    *    must have at least 1 layer 0 attribute (eg. keccak256("Male"))
    */
    function svgFromKeys(bytes32[] calldata _attributeKeys) external view returns (string memory) {
        return _svg(_attributeKeys);
    }

    /**
    * @dev svgFromIDs returns the svg data as a string
    * @param _ids uint256 ids of an attribute, by it's index of creation
    */
    function svgFromIDs(uint256[] calldata _ids) external view returns (string memory) {
        bytes32[] memory attributeKeys = new bytes32[](_ids.length);
        for (uint i = 0; i < _ids.length; i++) {
            attributeKeys[i] = index[_ids[i]];
        }
        return _svg(attributeKeys);
    }