<commit_before>/*
 * Copyright (c) 2016 Dariusz Stojaczyk. All Rights Reserved.
 * The following source code is released under an MIT-style license,
 * that can be found in the LICENSE file.
 */

#include "Application.h"
#include "util/os.h"
#include "util/log.h"
#include "window/LoadingScreen.h"

Application::Application()
    : m_context(*this),
      m_window(std::make_unique<LoadingScreen>(&m_context))
#ifndef SIMULATION
      , m_renderer(m_context, m_window.get())
#endif
{
    m_timer.delta(); //if not called right now, first call in game loop would return a very huge value
}

void Application::reinit() {
#ifndef SIMULATION
    m_renderer.reload();
#endif
    m_timer.delta(); //if not called right now, first call in game loop would return a very huge value
    m_inputManager.reload();
}

void Application::update() {
    if (m_newWindow) {
        switchWindow();
        m_newWindow = nullptr;
    }

    m_window->tick(m_timer.delta());

#ifndef SIMULATION
    this->m_renderer.render();
#endif

    this->handleEvents();
    m_inputManager.tick(*m_window);
}

void Application::resize(uint32_t width, uint32_t height) {
#ifndef SIMULATION
    this->m_renderer.resize(width, height);
    this->m_renderer.reload();
    m_window->reload();
#endif
}

void Application::handleClick(int button, Input::TouchPoint::State state, float x, float y) {
    m_inputManager.handleClick(button, state, x, y);
}

void Application::handleEvents() {
#ifdef USES_SDL
    SDL_Event event;
    while (SDL_PollEvent(&event) != 0) {
        switch (event.type) {
            case SDL_QUIT:
                this->m_running = false;
                break;
            case SDL_WINDOWEVENT:
                if (event.window.event == SDL_WINDOWEVENT_RESIZED || event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
                    this->resize((unsigned int) event.window.data1, (unsigned int) event.window.data2);
                }
                break;
            case SDL_KEYDOWN:
            case SDL_KEYUP:
                m_inputManager.handleKeypress(&event);
                break;
            case SDL_MOUSEBUTTONDOWN:
            case SDL_MOUSEBUTTONUP:
            Log::debug("%s button with id %d\n", event.type == SDL_MOUSEBUTTONDOWN ? "Pressed" : "Unpressed", event.button.button);
            case SDL_MOUSEMOTION: {
                int button = (int) round(log((double) event.button.button) / log(2.0)) + 1;
                if (button < 0 || button >= 5) break;
                int x, y;
                SDL_GetMouseState(&x, &y);
                this->handleClick(button, event.type == SDL_MOUSEBUTTONDOWN ? Input::TouchPoint::State::PRESS : (event.type == SDL_MOUSEBUTTONUP ? Input::TouchPoint::State::RELEASE : Input::TouchPoint::State::REPEAT), x, y);
                break;
            }
        }
    }
#endif // USES_SDL
}

bool Application::running() const {
    return m_running;
}

void Application::switchWindow() {
    if (m_newWindow == nullptr) {
        m_running = false;
    } else {
        m_window.swap(m_newWindow);
        m_newWindow.reset();
#ifndef SIMULATION
        m_renderer.switchWindow(*m_window);
        m_window->reload();
#endif
    }
}

Application::~Application() {}

#ifdef DEF_ANDROID

#include <jni.h>

std::unique_ptr<Application> application;
bool initialized = false;

extern "C" {
    JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_init(JNIEnv *env, jobject obj);
    JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_resize(JNIEnv *env, jobject obj, jint width, jint height);
    JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_tick(JNIEnv *env, jobject obj);
    JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_handleTouch(JNIEnv *env, jobject obj, jint i, jint action, jfloat x, jfloat y);
};

JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_init(JNIEnv *env, jobject obj) {
    if (!application) {
        application = std::make_unique<Application>();
    } else {
        application->reinit();
    }
}

JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_resize(JNIEnv *env, jobject obj, jint width, jint height) {
    application->resize(width, height);
}

JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_tick(JNIEnv *env, jobject obj) {
    application->update();
    if (!application->running()) {
        jclass cls = env->GetObjectClass(obj);
        jmethodID mid = env->GetMethodID(cls, "exit", "()V");
        if (mid != 0) {
            env->CallVoidMethod(obj, mid);
        }
    }
}

JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_handleTouch(JNIEnv *env, jobject obj, jint i,  jint action, jfloat x, jfloat y) {
    application->handleClick(i, static_cast<Input::TouchPoint::State>(action), x, y);
}

#endif // DEF_ANDROID<commit_msg>Reinit window & the render on application reinit.<commit_after>/*
 * Copyright (c) 2016 Dariusz Stojaczyk. All Rights Reserved.
 * The following source code is released under an MIT-style license,
 * that can be found in the LICENSE file.
 */

#include "Application.h"
#include "util/os.h"
#include "util/log.h"
#include "window/LoadingScreen.h"

Application::Application()
    : m_context(*this),
      m_window(std::make_unique<LoadingScreen>(&m_context))
#ifndef SIMULATION
      , m_renderer(m_context, m_window.get())
#endif
{
    m_timer.delta(); //if not called right now, first call in game loop would return a very huge value
}

void Application::reinit() {
#ifndef SIMULATION
    m_renderer.switchWindow(*m_window);
#endif
    m_window->reload();
    m_timer.delta(); //if not called right now, first call in game loop would return a very huge value
    m_inputManager.reload();
}

void Application::update() {
    if (m_newWindow) {
        switchWindow();
        m_newWindow = nullptr;
    }

    m_window->tick(m_timer.delta());

#ifndef SIMULATION
    this->m_renderer.render();
#endif

    this->handleEvents();
    m_inputManager.tick(*m_window);
}

void Application::resize(uint32_t width, uint32_t height) {
#ifndef SIMULATION
    this->m_renderer.resize(width, height);
    this->m_renderer.reload();
    m_window->reload();
#endif
}

void Application::handleClick(int button, Input::TouchPoint::State state, float x, float y) {
    m_inputManager.handleClick(button, state, x, y);
}

void Application::handleEvents() {
#ifdef USES_SDL
    SDL_Event event;
    while (SDL_PollEvent(&event) != 0) {
        switch (event.type) {
            case SDL_QUIT:
                this->m_running = false;
                break;
            case SDL_WINDOWEVENT:
                if (event.window.event == SDL_WINDOWEVENT_RESIZED || event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
                    this->resize((unsigned int) event.window.data1, (unsigned int) event.window.data2);
                }
                break;
            case SDL_KEYDOWN:
            case SDL_KEYUP:
                m_inputManager.handleKeypress(&event);
                break;
            case SDL_MOUSEBUTTONDOWN:
            case SDL_MOUSEBUTTONUP:
            Log::debug("%s button with id %d\n", event.type == SDL_MOUSEBUTTONDOWN ? "Pressed" : "Unpressed", event.button.button);
            case SDL_MOUSEMOTION: {
                int button = (int) round(log((double) event.button.button) / log(2.0)) + 1;
                if (button < 0 || button >= 5) break;
                int x, y;
                SDL_GetMouseState(&x, &y);
                this->handleClick(button, event.type == SDL_MOUSEBUTTONDOWN ? Input::TouchPoint::State::PRESS : (event.type == SDL_MOUSEBUTTONUP ? Input::TouchPoint::State::RELEASE : Input::TouchPoint::State::REPEAT), x, y);
                break;
            }
        }
    }
#endif // USES_SDL
}

bool Application::running() const {
    return m_running;
}

void Application::switchWindow() {
    if (m_newWindow == nullptr) {
        m_running = false;
    } else {
        m_window.swap(m_newWindow);
        m_newWindow.reset();
#ifndef SIMULATION
        m_renderer.switchWindow(*m_window);
        m_window->reload();
#endif
    }
}

Application::~Application() {}

#ifdef DEF_ANDROID

#include <jni.h>

std::unique_ptr<Application> application;
bool initialized = false;

extern "C" {
    JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_init(JNIEnv *env, jobject obj);
    JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_resize(JNIEnv *env, jobject obj, jint width, jint height);
    JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_tick(JNIEnv *env, jobject obj);
    JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_handleTouch(JNIEnv *env, jobject obj, jint i, jint action, jfloat x, jfloat y);
};

JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_init(JNIEnv *env, jobject obj) {
    if (!application) {
        application = std::make_unique<Application>();
    } else {
        application->reinit();
    }
}

JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_resize(JNIEnv *env, jobject obj, jint width, jint height) {
    application->resize(width, height);
}

JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_tick(JNIEnv *env, jobject obj) {
    application->update();
    if (!application->running()) {
        jclass cls = env->GetObjectClass(obj);
        jmethodID mid = env->GetMethodID(cls, "exit", "()V");
        if (mid != 0) {
            env->CallVoidMethod(obj, mid);
        }
    }
}

JNIEXPORT void JNICALL Java_darsto_spooky_JniBridge_handleTouch(JNIEnv *env, jobject obj, jint i,  jint action, jfloat x, jfloat y) {
    application->handleClick(i, static_cast<Input::TouchPoint::State>(action), x, y);
}

#endif // DEF_ANDROID<|endoftext|>


<commit_before>#pragma once

#include <string>
#include <boost/atomic/atomic.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/lockfree/queue.hpp>
#include <boost/lockfree/spsc_queue.hpp>

using std::string;

#include "types.hpp"

class CConnection
{
public: //constructor / deconstructor
	CConnection(
		const string &host, const string &user, const string &passw, const string &db,
		const COptions *options);
	~CConnection();
	CConnection(const CConnection &rhs) = delete;

private: //variables
	MYSQL *m_Connection = nullptr;

	boost::mutex m_Mutex; //protect every MySQL C API call

public: //functions
	inline bool IsConnected() const
	{
		return m_Connection != nullptr;
	}
	bool EscapeString(const char *src, string &dest);
	bool SetCharset(string charset);
	bool GetCharset(string &charset);
	bool Execute(Query_t query);
	bool GetError(unsigned int &id, string &msg);
	bool GetStatus(string &stat);

};

class CThreadedConnection
{
public:
	CThreadedConnection(
		const string &host, const string &user, const string &passw, const string &db,
		const COptions *options);
	~CThreadedConnection();
	CThreadedConnection(const CThreadedConnection &rhs) = delete;

private:
	CConnection m_Connection;

	boost::thread m_WorkerThread;
	boost::atomic<bool> m_WorkerThreadActive;

	boost::lockfree::spsc_queue < Query_t,
		boost::lockfree::fixed_sized < true >,
		boost::lockfree::capacity < 32768 >> m_Queue;

private:
	void WorkerFunc();

public:
	inline bool Queue(Query_t query)
	{
		return m_Queue.push(query);
	}
	inline bool SetCharset(string charset)
	{
		return m_Connection.SetCharset(charset);
	}

};

class CConnectionPool
{
public:
	CConnectionPool(
		const size_t size, const string &host, const string &user, const string &passw, const string &db,
		const COptions *options);
	~CConnectionPool();
	CConnectionPool(const CConnectionPool &rhs) = delete;

private:
	struct SConnectionNode
	{
		CThreadedConnection *Connection;
		SConnectionNode *Next;
	};

	SConnectionNode *m_CurrentNode;
	boost::mutex m_PoolMutex;

public:
	bool Queue(Query_t query);
	bool SetCharset(string charset);

};
<commit_msg>double query limit per connection<commit_after>#pragma once

#include <string>
#include <boost/atomic/atomic.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/lockfree/queue.hpp>
#include <boost/lockfree/spsc_queue.hpp>

using std::string;

#include "types.hpp"

class CConnection
{
public: //constructor / deconstructor
	CConnection(
		const string &host, const string &user, const string &passw, const string &db,
		const COptions *options);
	~CConnection();
	CConnection(const CConnection &rhs) = delete;

private: //variables
	MYSQL *m_Connection = nullptr;

	boost::mutex m_Mutex; //protect every MySQL C API call

public: //functions
	inline bool IsConnected() const
	{
		return m_Connection != nullptr;
	}
	bool EscapeString(const char *src, string &dest);
	bool SetCharset(string charset);
	bool GetCharset(string &charset);
	bool Execute(Query_t query);
	bool GetError(unsigned int &id, string &msg);
	bool GetStatus(string &stat);

};

class CThreadedConnection
{
public:
	CThreadedConnection(
		const string &host, const string &user, const string &passw, const string &db,
		const COptions *options);
	~CThreadedConnection();
	CThreadedConnection(const CThreadedConnection &rhs) = delete;

private:
	CConnection m_Connection;

	boost::thread m_WorkerThread;
	boost::atomic<bool> m_WorkerThreadActive;

	boost::lockfree::spsc_queue < Query_t,
		boost::lockfree::fixed_sized < true >,
		boost::lockfree::capacity < 65536 >> m_Queue;

private:
	void WorkerFunc();

public:
	inline bool Queue(Query_t query)
	{
		return m_Queue.push(query);
	}
	inline bool SetCharset(string charset)
	{
		return m_Connection.SetCharset(charset);
	}

};

class CConnectionPool
{
public:
	CConnectionPool(
		const size_t size, const string &host, const string &user, const string &passw, const string &db,
		const COptions *options);
	~CConnectionPool();
	CConnectionPool(const CConnectionPool &rhs) = delete;

private:
	struct SConnectionNode
	{
		CThreadedConnection *Connection;
		SConnectionNode *Next;
	};

	SConnectionNode *m_CurrentNode;
	boost::mutex m_PoolMutex;

public:
	bool Queue(Query_t query);
	bool SetCharset(string charset);

};
<|endoftext|>


<commit_before>/// @brief provides CORDIC for cos function
/// @ref see H. Dawid, H. Meyr, "CORDIC Algorithms and Architectures"

namespace std {
    /// @brief computes square root by CORDIC-algorithm
    /// @ref page 11
    template<typename T, size_t n, size_t f, class op, class up>
    typename core::fixed_point<T, n, f, op, up>::sqrt_type sqrt(core::fixed_point<T, n, f, op, up> const val)
    {
        typedef core::fixed_point<T, n, f, op, up> fp;

        assert(("sqrt parameter is negative", val >= fp(0)));
        if (val < fp(0)) {
            throw std::exception("sqrt: arg is negative");
        }

        if (val == fp(0.0)) {
            return fp::sqrt_type(0.0);
        }

        // Chosen fixed-point format must have several bits to represent
        // lut. Also format must enable argument translating to interval [1.0, 2.0].
        // So format must reserve two bits at least for integer part.
        typedef core::fixed_point<boost::int_t<1u+f+2u>::least, f+2u, f, op, up> work_type;
        typedef core::cordic::lut<f, work_type> lut;

        using boost::mpl::if_;
        struct chooser
        {
            enum { value = (fp::total - fp::fractionals) >= 2 };
        };
        typedef if_<chooser, fp, work_type>::type reduce_type;

        int power(0);
        reduce_type arg(val);
        if (arg == reduce_type(1.0)) {
            return fp::sqrt_type(1.0);
        }

        while (arg >= work_type(2.0)) {
            as_native(arg) >>= 1u;
            power--;
        }
        while (arg < work_type(1.0)) {
            as_native(arg) <<= 1u;
            power++;
        }

        // CORDIC vectoring mode:
        lut const angles = lut::hyperbolic_wo_repeated_iterations();
        typename core::U_fixed_point<f, f>::type const norm(lut::hyperbolic_scale_with_repeated_iterations(n));
        work_type x(work_type(arg) + 0.25), y(work_type(arg) - 0.25), z(arg);
        {
            size_t repeated(4u);
            size_t num(0);

            for (size_t i(1u); i < f + 1u; ++i)
            {
                int const sign = ((x.value() < 0)? -1 : +1) * ((y.value() < 0)? -1 : +1);
                work_type::word_type const store(x.value());
                x = x - work_type::wrap(sign * (y.value() >> (num + 1u)));
                y = y - work_type::wrap(sign * (store >> (num + 1u)));
                z = (sign > 0) ? z + angles[num] : z - angles[num];

                // do repetition to receive convergence
                if (i == repeated && i != n - 1) {
                    int const sign = ((x.value() < 0)? -1 : +1) * ((y.value() < 0)? -1 : +1);
                    work_type::word_type const store(x.value());
                    x = x - work_type::wrap(sign * (y.value() >> (num + 1u)));
                    y = y - work_type::wrap(sign * (store >> (num + 1u)));
                    z = (sign > 0) ? z + angles[num] : z - angles[num];

                    i += 1u;
                    repeated = 3u * repeated  + 1u;
                }

                num += 1u;
            }
        }

        work_type result(x / norm);
        if (power > 0) {
            as_native(result) >>= (power >> 1u);
            if (power & 1u) {
                result = result * work_type::CONST_SQRT1_2;
            }
        }
        else {
            size_t const p(-power);
            as_native(result) <<= (p >> 1u);
            if (p & 1u) {
                result = result * work_type::CONST_SQRT2;
            }
        }

        return fp::sqrt_type(result);
    }
}
<commit_msg>sqrt.inl: lack of precision bottlenecks are removed<commit_after>/// @brief provides CORDIC for cos function
/// @ref see H. Dawid, H. Meyr, "CORDIC Algorithms and Architectures"

namespace std {
    /// @brief computes square root by CORDIC-algorithm
    /// @ref page 11
    template<typename T, size_t n, size_t f, class op, class up>
    typename core::fixed_point<T, n, f, op, up>::sqrt_type sqrt(core::fixed_point<T, n, f, op, up> const val)
    {
        typedef core::fixed_point<T, n, f, op, up> fp;

        assert(("sqrt parameter is negative", val >= fp(0)));
        if (val < fp(0)) {
            throw std::exception("sqrt: arg is negative");
        }

        if (val == fp(0.0)) {
            return fp::sqrt_type(0.0);
        }

        // Chosen fixed-point format must have several bits to represent
        // lut. Also format must enable argument translating to interval [1.0, 2.0].
        // So format must reserve two bits at least for integer part.
        typedef core::fixed_point<boost::int_t<1u+f+2u>::least, f+2u, f, op, up> work_type;
        typedef core::cordic::lut<f, work_type> lut;

        using boost::mpl::if_;
        struct chooser
        {
            enum { value = (fp::total - fp::fractionals) >= 2 };
        };
        typedef if_<chooser, fp, work_type>::type reduce_type;

        int power(0);
        reduce_type arg(val);
        if (arg == reduce_type(1.0)) {
            return fp::sqrt_type(1.0);
        }

        while (arg >= work_type(2.0)) {
            as_native(arg) >>= 1u;
            power--;
        }
        while (arg < work_type(1.0)) {
            as_native(arg) <<= 1u;
            power++;
        }

        // CORDIC vectoring mode:
        lut const angles = lut::hyperbolic_wo_repeated_iterations();
        typename core::U_fixed_point<f, f>::type const norm(lut::hyperbolic_scale_with_repeated_iterations(n));
        work_type x(work_type(arg) + 0.25), y(work_type(arg) - 0.25), z(arg);
        {
            size_t repeated(4u);
            size_t num(0);

            for (size_t i(1u); i < f + 1u; ++i)
            {
                int const sign = ((x.value() < 0)? -1 : +1) * ((y.value() < 0)? -1 : +1);
                work_type::word_type const store(x.value());
                x = x - work_type::wrap(sign * (y.value() >> (num + 1u)));
                y = y - work_type::wrap(sign * (store >> (num + 1u)));
                z = (sign > 0) ? z + angles[num] : z - angles[num];

                // do repetition to receive convergence
                if (i == repeated && i != n - 1) {
                    int const sign = ((x.value() < 0)? -1 : +1) * ((y.value() < 0)? -1 : +1);
                    work_type::word_type const store(x.value());
                    x = x - work_type::wrap(sign * (y.value() >> (num + 1u)));
                    y = y - work_type::wrap(sign * (store >> (num + 1u)));
                    z = (sign > 0) ? z + angles[num] : z - angles[num];

                    i += 1u;
                    repeated = 3u * repeated  + 1u;
                }

                num += 1u;
            }
        }

        reduce_type result(x / norm);
        if (power > 0) {
            as_native(result) >>= (power >> 1u);
            if (power & 1u) {
                result = result / reduce_type::CONST_SQRT2;
            }
        }
        else {
            size_t const p(-power);
            as_native(result) <<= (p >> 1u);
            if (p & 1u) {
                result = result * work_type::CONST_SQRT2;
            }
        }

        return fp::sqrt_type(result);
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: lode.vandevenne@gmail.com (Lode Vandevenne)
// Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)

// Command line tool to recompress and optimize PNG images, using zopflipng_lib.

/*Modified by Felix Hanau*/

#include <cstdio>
#include <set>
#include <vector>
#include <string>

#include "lodepng/lodepng_util.h"
#include "zopfli/deflate.h"
#include "main.h"
#include "lodepng/lodepng.h"

struct ZopfliPNGOptions {
  ZopfliPNGOptions();

  unsigned Mode;

  // Allow altering hidden colors of fully transparent pixels
  bool lossy_transparent;

  // Convert 16-bit per channel images to 8-bit per channel
  bool lossy_8bit;

  // remove PNG chunks
  bool strip;

  //Use per block multithreading
  unsigned multithreading;

};

ZopfliPNGOptions::ZopfliPNGOptions()
: lossy_transparent(true)
, lossy_8bit(false)
, strip(false)
{
}

// Deflate compressor passed as fuction pointer to LodePNG to have it use Zopfli
// as its compression backend.
static unsigned CustomPNGDeflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) {
  const ZopfliPNGOptions* png_options = static_cast<const ZopfliPNGOptions*>(settings->custom_context);
  unsigned char bp = 0;
  ZopfliOptions options;
  ZopfliInitOptions(&options, png_options->Mode, png_options->multithreading, 1);
  ZopfliDeflate(&options, 1, in, insize, &bp, out, outsize);
  return 0;
}

// Returns 32-bit integer value for RGBA color.
static unsigned ColorIndex(const unsigned char* color) {
  return color[0] + (color[1] << 8) + (color[2] << 16) + (color[3] << 24);
}

// Counts amount of colors in the image, up to 257. If transparent_counts_as_one
// is enabled, any color with alpha channel 0 is treated as a single color with
// index 0.
static void CountColors(std::set<unsigned>* unique, const unsigned char* image, unsigned w, unsigned h, bool transparent_counts_as_one) {
  unique->clear();
  for (size_t i = 0; i < w * h; i++) {
    unsigned index = ColorIndex(&image[i * 4]);
    if (transparent_counts_as_one && image[i * 4 + 3] == 0) index = 0;
    unique->insert(index);
    if (i>256){
      if (unique->size() > 256) break;
    }
  }
}

// Remove RGB information from pixels with alpha=0
static void LossyOptimizeTransparent(lodepng::State* inputstate, unsigned char* image,
                                     unsigned w, unsigned h, int filter) {
  //TODO: Only set "palette" when palette is actually used. Optimize for the filter actually used in the row. (Move into Lodepng?)
  //      There are big improvements possible here.

  std::set<unsigned> count;  // Color count, up to 257.

  // If true, means palette is possible so avoid using different RGB values for
  // the transparent color.
  bool palette;
  if(inputstate->info_png.color.colortype == LCT_PALETTE) {
    palette = w * h < inputstate->info_png.color.palettesize * 2;
  }
  else{
    CountColors(&count, image, w, h, true);
    unsigned long colors = count.size();
    palette = colors <= 256 && w * h < colors * 2;
  }

  if (!filter && !palette) {
    for (size_t i = 0; i < w * h; i++) {
      // if alpha is 0, alter the RGB values to 0.
      if (image[i * 4 + 3] == 0) {
        image[i * 4] = 0;
        image[i * 4 + 1] = 0;
        image[i * 4 + 2] = 0;
      }
    }
  }
  else {
    // First check if we want to preserve potential color-key background color,
    // or instead use the last encountered RGB value all the time to save bytes.
    bool key = true;
    // Makes no difference if palette
    if (!palette){
      for (size_t i = 0; i < w * h; i++) {
        if (image[i * 4 + 3] > 0 && image[i * 4 + 3] < 255) {
          key = false;
          break;
        }
      }
    }
    unsigned char r = 0, g = 0, b = 0;
    if (palette && !filter){
      // Use RGB value of first encountered pixel. This can be
      // used as a valid color key, or in case of palette ensures a color
      // existing in the input image palette is used.
      r = image[0];
      g = image[1];
      b = image[2];
    }
    else if (key || palette){
      for (size_t i = 0; i < w * h; i++) {
        if (image[i * 4 + 3] == 0) {
          // Use RGB value of first encountered transparent pixel. This can be
          // used as a valid color key, or in case of palette ensures a color
          // existing in the input image palette is used.
          r = image[i * 4];
          g = image[i * 4 + 1];
          b = image[i * 4 + 2];
          break;
        }
      }
    }
    for (size_t i = 0; i < w * h; i++) {
      // if alpha is 0, alter the RGB value to a possibly more efficient one.
      if (!palette && i % w == 0){
        r = 0;
        g = 0;
        b = 0;
      }
      if (image[i * 4 + 3] == 0) {
        image[i * 4] = r;
        image[i * 4 + 1] = g;
        image[i * 4 + 2] = b;
      }
      else {
        if (!key && !palette){
          // Use the last encountered RGB value if no key or palette is used: that
          // way more values can be 0 thanks to the PNG filter types.
          r = image[i * 4];
          g = image[i * 4 + 1];
          b = image[i * 4 + 2];
        }
      }
    }
  }

  // If there are now less colors, update palette of input image to match this.
  if (palette && inputstate->info_png.color.palettesize > 0) {
    CountColors(&count, image, w, h, false);
    if (count.size() < inputstate->info_png.color.palettesize) {
      std::vector<unsigned char> palette_out;
      unsigned char* palette_in = inputstate->info_png.color.palette;
      for (size_t i = 0; i < inputstate->info_png.color.palettesize; i++) {
        if (count.count(ColorIndex(&palette_in[i * 4])) != 0) {
          palette_out.push_back(palette_in[i * 4]);
          palette_out.push_back(palette_in[i * 4 + 1]);
          palette_out.push_back(palette_in[i * 4 + 2]);
          palette_out.push_back(palette_in[i * 4 + 3]);
        }
      }
      inputstate->info_png.color.palettesize = palette_out.size() / 4;
      for (size_t i = 0; i < palette_out.size(); i++) {
        palette_in[i] = palette_out[i];
      }
    }
  }
}

// Tries to optimize given a single PNG filter strategy.
// Returns 0 if ok, other value for error
static unsigned TryOptimize(std::vector<unsigned char>& image, unsigned w, unsigned h, bool bit16, const lodepng::State& inputstate,
                            const ZopfliPNGOptions* png_options, std::vector<unsigned char>* out, int best_filter, std::vector<unsigned char> filters) {
  lodepng::State state;
  state.encoder.zlibsettings.custom_deflate = CustomPNGDeflate;
  state.encoder.zlibsettings.custom_context = png_options;
  state.encoder.clean_alpha = png_options->lossy_transparent;

  ZopfliOptions dummyoptions;
  ZopfliInitOptions(&dummyoptions, png_options->Mode, 0, 0);
  state.encoder.filter_style = dummyoptions.filter_style;
  state.encoder.text_compression = 0;
  if (bit16) {
    state.info_raw.bitdepth = 16;
  }

  state.encoder.filter_strategy = (LodePNGFilterStrategy)best_filter;
  if (best_filter == 6)
  {
    state.encoder.predefined_filters = &filters[0];
    state.encoder.auto_convert = 0;
    lodepng_color_mode_copy(&state.info_png.color, &inputstate.info_png.color);
  }

  unsigned error = lodepng::encode(*out, image, w, h, state);
  // For very small output, also try without palette, it may be smaller thanks
  // to no palette storage overhead.
  unsigned long testboth = out->size();
  if (!error && testboth < 4096 && w * h < 45000 && best_filter != 6) {
    lodepng::State teststate;
    std::vector<unsigned char> temp;
    lodepng::decode(temp, w, h, teststate, *out);
    LodePNGColorMode& color = teststate.info_png.color;
    if (color.colortype == LCT_PALETTE && (testboth < 2048 || color.palettesize>192) && (testboth < 1024 || color.palettesize>64) && (testboth < 512 || color.palettesize>40) && (testboth < 300 || color.palettesize>9))
    {
      std::vector<unsigned char> out2;
      state.encoder.auto_convert = 0;
      bool grey = true;
      unsigned has_alpha=lodepng_has_palette_alpha(&color);
      for (size_t i = 0; i < color.palettesize; i++) {
        if (color.palette[i * 4] != color.palette[i * 4 + 2]
            || color.palette[i * 4 + 1] != color.palette[i * 4 + 2]) {
          grey = false;
          break;
        }
      }
      if (grey){
        if (has_alpha){state.info_png.color.colortype = LCT_GREY_ALPHA;}
        else{state.info_png.color.colortype = LCT_GREY;}
      }

      else{if (has_alpha){state.info_png.color.colortype = LCT_RGBA;}
      else{state.info_png.color.colortype = LCT_RGB;}
      }
      error = lodepng::encode(out2, image, w, h, state);
      if (out2.size() < out->size()){
        out->swap(out2);
      }

    }
  }
  if (error) {
    printf("Encoding error %u: %s\n", error, lodepng_error_text(error));
    return error;
  }
  return 0;
}

static unsigned ZopfliPNGOptimize(const std::vector<unsigned char>& origpng, const ZopfliPNGOptions& png_options, std::vector<unsigned char>* resultpng, int best_filter, std::vector<unsigned char> filters) {
  std::vector<unsigned char> image;
  unsigned w, h;
  lodepng::State inputstate;

  unsigned error = lodepng::decode(image, w, h, inputstate, origpng);

  if (error) {
    printf("Decoding error %i: %s\n", error, lodepng_error_text(error));

    return error;
  }

  bool bit16 = false;  // Using 16-bit per channel raw image
  if (inputstate.info_png.color.bitdepth == 16 && !png_options.lossy_8bit) {
    // Decode as 16-bit
    image.clear();
    error = lodepng::decode(image, w, h, origpng, LCT_RGBA, 16);
    bit16 = true;
  }
  if (!error) {
    // If lossy_transparent, remove RGB information from pixels with alpha=0
    if (png_options.lossy_transparent && !bit16) {
      LossyOptimizeTransparent(&inputstate, &image[0], w, h, best_filter);
    }
  }
  std::vector<unsigned char> temp;
  error = TryOptimize(image, w, h, bit16, inputstate, &png_options, &temp, best_filter, filters);
  if (!error) {
    (*resultpng).swap(temp);  // Store best result so far in the output.
  }
  if (!png_options.strip) {
    std::vector<std::string> names[3];
    std::vector<std::vector<unsigned char> > chunks[3];
    lodepng::getChunks(names, chunks, origpng);
    lodepng::insertChunks(*resultpng, chunks);
  }
  return error;
}

int Zopflipng(bool strip, const char * Infile, bool strict, unsigned Mode, int filter, unsigned multithreading) {
  ZopfliPNGOptions png_options;
  png_options.Mode = Mode;
  png_options.multithreading = multithreading;
  png_options.lossy_transparent = !strict && filter != 6;
  png_options.strip = strip;
  std::vector<unsigned char> origpng;

  std::vector<unsigned char> filters;
  lodepng::load_file(origpng, Infile);
  if (filter == 6){
    lodepng::getFilterTypes(filters, origpng);
    assert(filters.size());
  }
  std::vector<unsigned char> resultpng;
  if (ZopfliPNGOptimize(origpng, png_options, &resultpng, filter, filters)) {return -1;}
  if (resultpng.size() >= origpng.size()) {return 1;}
  lodepng::save_file(resultpng, Infile);
  return 0;
}
<commit_msg>Fix compilation (again)<commit_after>// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: lode.vandevenne@gmail.com (Lode Vandevenne)
// Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)

// Command line tool to recompress and optimize PNG images, using zopflipng_lib.

/*Modified by Felix Hanau*/

#include <cstdio>
#include <cassert>
#include <set>
#include <vector>
#include <string>

#include "lodepng/lodepng_util.h"
#include "zopfli/deflate.h"
#include "main.h"
#include "lodepng/lodepng.h"

struct ZopfliPNGOptions {
  ZopfliPNGOptions();

  unsigned Mode;

  // Allow altering hidden colors of fully transparent pixels
  bool lossy_transparent;

  // Convert 16-bit per channel images to 8-bit per channel
  bool lossy_8bit;

  // remove PNG chunks
  bool strip;

  //Use per block multithreading
  unsigned multithreading;

};

ZopfliPNGOptions::ZopfliPNGOptions()
: lossy_transparent(true)
, lossy_8bit(false)
, strip(false)
{
}

// Deflate compressor passed as fuction pointer to LodePNG to have it use Zopfli
// as its compression backend.
static unsigned CustomPNGDeflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) {
  const ZopfliPNGOptions* png_options = static_cast<const ZopfliPNGOptions*>(settings->custom_context);
  unsigned char bp = 0;
  ZopfliOptions options;
  ZopfliInitOptions(&options, png_options->Mode, png_options->multithreading, 1);
  ZopfliDeflate(&options, 1, in, insize, &bp, out, outsize);
  return 0;
}

// Returns 32-bit integer value for RGBA color.
static unsigned ColorIndex(const unsigned char* color) {
  return color[0] + (color[1] << 8) + (color[2] << 16) + (color[3] << 24);
}

// Counts amount of colors in the image, up to 257. If transparent_counts_as_one
// is enabled, any color with alpha channel 0 is treated as a single color with
// index 0.
static void CountColors(std::set<unsigned>* unique, const unsigned char* image, unsigned w, unsigned h, bool transparent_counts_as_one) {
  unique->clear();
  for (size_t i = 0; i < w * h; i++) {
    unsigned index = ColorIndex(&image[i * 4]);
    if (transparent_counts_as_one && image[i * 4 + 3] == 0) index = 0;
    unique->insert(index);
    if (i>256){
      if (unique->size() > 256) break;
    }
  }
}

// Remove RGB information from pixels with alpha=0
static void LossyOptimizeTransparent(lodepng::State* inputstate, unsigned char* image,
                                     unsigned w, unsigned h, int filter) {
  //TODO: Only set "palette" when palette is actually used. Optimize for the filter actually used in the row. (Move into Lodepng?)
  //      There are big improvements possible here.

  std::set<unsigned> count;  // Color count, up to 257.

  // If true, means palette is possible so avoid using different RGB values for
  // the transparent color.
  bool palette;
  if(inputstate->info_png.color.colortype == LCT_PALETTE) {
    palette = w * h < inputstate->info_png.color.palettesize * 2;
  }
  else{
    CountColors(&count, image, w, h, true);
    unsigned long colors = count.size();
    palette = colors <= 256 && w * h < colors * 2;
  }

  if (!filter && !palette) {
    for (size_t i = 0; i < w * h; i++) {
      // if alpha is 0, alter the RGB values to 0.
      if (image[i * 4 + 3] == 0) {
        image[i * 4] = 0;
        image[i * 4 + 1] = 0;
        image[i * 4 + 2] = 0;
      }
    }
  }
  else {
    // First check if we want to preserve potential color-key background color,
    // or instead use the last encountered RGB value all the time to save bytes.
    bool key = true;
    // Makes no difference if palette
    if (!palette){
      for (size_t i = 0; i < w * h; i++) {
        if (image[i * 4 + 3] > 0 && image[i * 4 + 3] < 255) {
          key = false;
          break;
        }
      }
    }
    unsigned char r = 0, g = 0, b = 0;
    if (palette && !filter){
      // Use RGB value of first encountered pixel. This can be
      // used as a valid color key, or in case of palette ensures a color
      // existing in the input image palette is used.
      r = image[0];
      g = image[1];
      b = image[2];
    }
    else if (key || palette){
      for (size_t i = 0; i < w * h; i++) {
        if (image[i * 4 + 3] == 0) {
          // Use RGB value of first encountered transparent pixel. This can be
          // used as a valid color key, or in case of palette ensures a color
          // existing in the input image palette is used.
          r = image[i * 4];
          g = image[i * 4 + 1];
          b = image[i * 4 + 2];
          break;
        }
      }
    }
    for (size_t i = 0; i < w * h; i++) {
      // if alpha is 0, alter the RGB value to a possibly more efficient one.
      if (!palette && i % w == 0){
        r = 0;
        g = 0;
        b = 0;
      }
      if (image[i * 4 + 3] == 0) {
        image[i * 4] = r;
        image[i * 4 + 1] = g;
        image[i * 4 + 2] = b;
      }
      else {
        if (!key && !palette){
          // Use the last encountered RGB value if no key or palette is used: that
          // way more values can be 0 thanks to the PNG filter types.
          r = image[i * 4];
          g = image[i * 4 + 1];
          b = image[i * 4 + 2];
        }
      }
    }
  }

  // If there are now less colors, update palette of input image to match this.
  if (palette && inputstate->info_png.color.palettesize > 0) {
    CountColors(&count, image, w, h, false);
    if (count.size() < inputstate->info_png.color.palettesize) {
      std::vector<unsigned char> palette_out;
      unsigned char* palette_in = inputstate->info_png.color.palette;
      for (size_t i = 0; i < inputstate->info_png.color.palettesize; i++) {
        if (count.count(ColorIndex(&palette_in[i * 4])) != 0) {
          palette_out.push_back(palette_in[i * 4]);
          palette_out.push_back(palette_in[i * 4 + 1]);
          palette_out.push_back(palette_in[i * 4 + 2]);
          palette_out.push_back(palette_in[i * 4 + 3]);
        }
      }
      inputstate->info_png.color.palettesize = palette_out.size() / 4;
      for (size_t i = 0; i < palette_out.size(); i++) {
        palette_in[i] = palette_out[i];
      }
    }
  }
}

// Tries to optimize given a single PNG filter strategy.
// Returns 0 if ok, other value for error
static unsigned TryOptimize(std::vector<unsigned char>& image, unsigned w, unsigned h, bool bit16, const lodepng::State& inputstate,
                            const ZopfliPNGOptions* png_options, std::vector<unsigned char>* out, int best_filter, std::vector<unsigned char> filters) {
  lodepng::State state;
  state.encoder.zlibsettings.custom_deflate = CustomPNGDeflate;
  state.encoder.zlibsettings.custom_context = png_options;
  state.encoder.clean_alpha = png_options->lossy_transparent;

  ZopfliOptions dummyoptions;
  ZopfliInitOptions(&dummyoptions, png_options->Mode, 0, 0);
  state.encoder.filter_style = dummyoptions.filter_style;
  state.encoder.text_compression = 0;
  if (bit16) {
    state.info_raw.bitdepth = 16;
  }

  state.encoder.filter_strategy = (LodePNGFilterStrategy)best_filter;
  if (best_filter == 6)
  {
    state.encoder.predefined_filters = &filters[0];
    state.encoder.auto_convert = 0;
    lodepng_color_mode_copy(&state.info_png.color, &inputstate.info_png.color);
  }

  unsigned error = lodepng::encode(*out, image, w, h, state);
  // For very small output, also try without palette, it may be smaller thanks
  // to no palette storage overhead.
  unsigned long testboth = out->size();
  if (!error && testboth < 4096 && w * h < 45000 && best_filter != 6) {
    lodepng::State teststate;
    std::vector<unsigned char> temp;
    lodepng::decode(temp, w, h, teststate, *out);
    LodePNGColorMode& color = teststate.info_png.color;
    if (color.colortype == LCT_PALETTE && (testboth < 2048 || color.palettesize>192) && (testboth < 1024 || color.palettesize>64) && (testboth < 512 || color.palettesize>40) && (testboth < 300 || color.palettesize>9))
    {
      std::vector<unsigned char> out2;
      state.encoder.auto_convert = 0;
      bool grey = true;
      unsigned has_alpha=lodepng_has_palette_alpha(&color);
      for (size_t i = 0; i < color.palettesize; i++) {
        if (color.palette[i * 4] != color.palette[i * 4 + 2]
            || color.palette[i * 4 + 1] != color.palette[i * 4 + 2]) {
          grey = false;
          break;
        }
      }
      if (grey){
        if (has_alpha){state.info_png.color.colortype = LCT_GREY_ALPHA;}
        else{state.info_png.color.colortype = LCT_GREY;}
      }

      else{if (has_alpha){state.info_png.color.colortype = LCT_RGBA;}
      else{state.info_png.color.colortype = LCT_RGB;}
      }
      error = lodepng::encode(out2, image, w, h, state);
      if (out2.size() < out->size()){
        out->swap(out2);
      }

    }
  }
  if (error) {
    printf("Encoding error %u: %s\n", error, lodepng_error_text(error));
    return error;
  }
  return 0;
}

static unsigned ZopfliPNGOptimize(const std::vector<unsigned char>& origpng, const ZopfliPNGOptions& png_options, std::vector<unsigned char>* resultpng, int best_filter, std::vector<unsigned char> filters) {
  std::vector<unsigned char> image;
  unsigned w, h;
  lodepng::State inputstate;

  unsigned error = lodepng::decode(image, w, h, inputstate, origpng);

  if (error) {
    printf("Decoding error %i: %s\n", error, lodepng_error_text(error));

    return error;
  }

  bool bit16 = false;  // Using 16-bit per channel raw image
  if (inputstate.info_png.color.bitdepth == 16 && !png_options.lossy_8bit) {
    // Decode as 16-bit
    image.clear();
    error = lodepng::decode(image, w, h, origpng, LCT_RGBA, 16);
    bit16 = true;
  }
  if (!error) {
    // If lossy_transparent, remove RGB information from pixels with alpha=0
    if (png_options.lossy_transparent && !bit16) {
      LossyOptimizeTransparent(&inputstate, &image[0], w, h, best_filter);
    }
  }
  std::vector<unsigned char> temp;
  error = TryOptimize(image, w, h, bit16, inputstate, &png_options, &temp, best_filter, filters);
  if (!error) {
    (*resultpng).swap(temp);  // Store best result so far in the output.
  }
  if (!png_options.strip) {
    std::vector<std::string> names[3];
    std::vector<std::vector<unsigned char> > chunks[3];
    lodepng::getChunks(names, chunks, origpng);
    lodepng::insertChunks(*resultpng, chunks);
  }
  return error;
}

int Zopflipng(bool strip, const char * Infile, bool strict, unsigned Mode, int filter, unsigned multithreading) {
  ZopfliPNGOptions png_options;
  png_options.Mode = Mode;
  png_options.multithreading = multithreading;
  png_options.lossy_transparent = !strict && filter != 6;
  png_options.strip = strip;
  std::vector<unsigned char> origpng;

  std::vector<unsigned char> filters;
  lodepng::load_file(origpng, Infile);
  if (filter == 6){
    lodepng::getFilterTypes(filters, origpng);
    assert(filters.size());
  }
  std::vector<unsigned char> resultpng;
  if (ZopfliPNGOptimize(origpng, png_options, &resultpng, filter, filters)) {return -1;}
  if (resultpng.size() >= origpng.size()) {return 1;}
  lodepng::save_file(resultpng, Infile);
  return 0;
}
<|endoftext|>


<commit_before>#include <efsw/platform/win/FileSystemImpl.hpp>

#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32

#ifndef WIN32_LEAN_AND_MEAN
	#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>

#ifndef EFSW_COMPILER_MSVC
#include <dirent.h>
#endif

namespace efsw { namespace Platform {

FileInfoMap FileSystem::filesInfoFromPath( const std::string& path )
{
	FileInfoMap files;

	String tpath( path );

	if ( tpath[ tpath.size() - 1 ] == '/' || tpath[ tpath.size() - 1 ] == '\\' )
	{
		tpath += "*";
	}
	else
	{
		tpath += "\\*";
	}

	WIN32_FIND_DATAW findFileData;
	HANDLE hFind = FindFirstFileW( (LPCWSTR)tpath.toWideString().c_str(), &findFileData );

	if( hFind != INVALID_HANDLE_VALUE )
	{
		std::string name( String( findFileData.cFileName ).toUtf8() );
		std::string fpath( path + name );

		if ( name != "." && name != ".." )
		{
			files[ name ] = FileInfo( fpath );
		}

		while( FindNextFileW( hFind, &findFileData ) )
		{
			name = String( findFileData.cFileName ).toUtf8();
			fpath = path + name;

			if ( name != "." && name != ".." )
			{
				files[ name ] = FileInfo( fpath );
			}
		}

		FindClose( hFind );
	}

	return files;
}

char FileSystem::getOSSlash()
{
	return '\\';
}

bool FileSystem::isDirectory( const std::string& path )
{
	return GetFileAttributesW( String( path ).toWideString().c_str() ) == FILE_ATTRIBUTE_DIRECTORY;
}

bool FileSystem::isRemoteFS( const std::string& directory )
{
	if ((directory[0] == '\\' || directory[0] == '/')  &&
		(directory[1] == '\\' || directory[1] == '/'))
	{
		return true;
	}

	if ( directory.size() >= 3 )
	{
		return 4 == GetDriveTypeA( directory.substr( 0, 3 ).c_str() );
	}

	return false;
}

}}

#endif
<commit_msg>Fixed FileSystem::isDirectory.<commit_after>#include <efsw/platform/win/FileSystemImpl.hpp>

#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32

#ifndef WIN32_LEAN_AND_MEAN
	#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>

#ifndef EFSW_COMPILER_MSVC
#include <dirent.h>
#endif

namespace efsw { namespace Platform {

FileInfoMap FileSystem::filesInfoFromPath( const std::string& path )
{
	FileInfoMap files;

	String tpath( path );

	if ( tpath[ tpath.size() - 1 ] == '/' || tpath[ tpath.size() - 1 ] == '\\' )
	{
		tpath += "*";
	}
	else
	{
		tpath += "\\*";
	}

	WIN32_FIND_DATAW findFileData;
	HANDLE hFind = FindFirstFileW( (LPCWSTR)tpath.toWideString().c_str(), &findFileData );

	if( hFind != INVALID_HANDLE_VALUE )
	{
		std::string name( String( findFileData.cFileName ).toUtf8() );
		std::string fpath( path + name );

		if ( name != "." && name != ".." )
		{
			files[ name ] = FileInfo( fpath );
		}

		while( FindNextFileW( hFind, &findFileData ) )
		{
			name = String( findFileData.cFileName ).toUtf8();
			fpath = path + name;

			if ( name != "." && name != ".." )
			{
				files[ name ] = FileInfo( fpath );
			}
		}

		FindClose( hFind );
	}

	return files;
}

char FileSystem::getOSSlash()
{
	return '\\';
}

bool FileSystem::isDirectory( const std::string& path )
{
	return 0 != ( GetFileAttributesW( String( path ).toWideString().c_str() ) & FILE_ATTRIBUTE_DIRECTORY );
}

bool FileSystem::isRemoteFS( const std::string& directory )
{
	if ((directory[0] == '\\' || directory[0] == '/')  &&
		(directory[1] == '\\' || directory[1] == '/'))
	{
		return true;
	}

	if ( directory.size() >= 3 )
	{
		return 4 == GetDriveTypeA( directory.substr( 0, 3 ).c_str() );
	}

	return false;
}

}}

#endif
<|endoftext|>


<commit_before>#include "clay.hpp"
#include "invokeutil2.hpp"

//
// ArgList
//

ArgList::ArgList(const vector<ExprPtr> &exprs, EnvPtr env)
    : Object(DONT_CARE), exprs(exprs), env(env), allStatic(true),
      recursionError(false)
{
    for (unsigned i = 0; i < exprs.size(); ++i) {
        PValuePtr pv = partialEval(exprs[i], env);
        if (!pv)
            recursionError = true;
        else if (!pv->isStatic)
            allStatic = false;
        this->_pvalues.push_back(pv);
    }
    this->_values.resize(exprs.size());
}

TypePtr ArgList::type(int i)
{
    return this->_pvalues[i]->type;
}

bool ArgList::isTemp(int i )
{
    return this->_pvalues[i]->isTemp;
}

PValuePtr ArgList::pvalue(int i)
{
    return this->_pvalues[i];
}

ValuePtr ArgList::value(int i)
{
    if (!this->_values[i])
        this->_values[i] = evaluateNonVoid(this->exprs[i], this->env);
    return this->_values[i];
}

TypePtr ArgList::typeValue(int i)
{
    return valueToType(this->value(i));
}

void ArgList::ensureArity(int n)
{
    if ((int)this->exprs.size() != n)
        error("incorrect no. of arguments");
}

bool ArgList::unifyFormalArg(int i, FormalArgPtr farg, EnvPtr fenv)
{
    switch (farg->objKind) {
    case VALUE_ARG : {
        ValueArg *x = (ValueArg *)farg.ptr();
        if (!x->type)
            return true;
        PatternPtr pattern = evaluatePattern(x->type, fenv);
        return unifyType(pattern, type(i));
    }
    case STATIC_ARG : {
        StaticArg *x = (StaticArg *)farg.ptr();
        PatternPtr pattern = evaluatePattern(x->pattern, fenv);
        return unify(pattern, this->value(i));
    }
    default :
        assert(false);
        return false;
    }
}

bool
ArgList::unifyFormalArgs(const vector<FormalArgPtr> &fargs, EnvPtr fenv)
{
    if (this->size() != fargs.size())
        return false;
    for (unsigned i = 0; i < fargs.size(); ++i) {
        if (!this->unifyFormalArg(i, fargs[i], fenv))
            return false;
    }
    return true;
}

void
ArgList::ensureUnifyFormalArgs(const vector<FormalArgPtr> &fargs,
                               EnvPtr fenv)
{
    ensureArity(fargs.size());
    for (unsigned i = 0; i < fargs.size(); ++i) {
        if (!this->unifyFormalArg(i, fargs[i], fenv))
            error(this->exprs[i], "non-matching argment");
    }
}

ArgListPtr ArgList::removeStaticArgs(const vector<FormalArgPtr> &fargs)
{
    assert(fargs.size() == this->size());
    ensureArity(fargs.size());

    ArgListPtr args = new ArgList(vector<ExprPtr>(), this->env);
    args->allStatic = this->allStatic;
    args->recursionError = this->recursionError;

    for (unsigned i = 0; i < fargs.size(); ++i) {
        if (fargs[i]->objKind != STATIC_ARG) {
            args->exprs.push_back(this->exprs[i]);
            args->_pvalues.push_back(this->_pvalues[i]);
            args->_values.push_back(this->_values[i]);
        }
    }

    return args;
}

//
// invoke table procs
//

int hashArgs(ArgListPtr args, const vector<bool> &isStaticFlags)
{
    int h = 0;
    for (unsigned i = 0; i < isStaticFlags.size(); ++i) {
        if (!isStaticFlags[i])
            h += toCOIndex(args->type(i).ptr());
        else
            h += valueHash(args->value(i));
    }
    return h;
}

bool matchingArgs(ArgListPtr args, InvokeTableEntryPtr entry)
{
    const vector<bool> &isStaticFlags = entry->table->isStaticFlags;
    const vector<ObjectPtr> &argsInfo = entry->argsInfo;
    for (unsigned i = 0; i < isStaticFlags.size(); ++i) {
        if (!isStaticFlags[i]) {
            TypePtr t = (Type *)argsInfo[i].ptr();
            if (args->type(i) != t)
                return false;
        }
        else {
            ValuePtr v = args->value(i);
            if (!valueEquals(v, (Value *)argsInfo[i].ptr()))
                return false;
        }
    }
    return true;
}

void initArgsInfo(InvokeTableEntryPtr entry, ArgListPtr args)
{
    const vector<bool> &isStaticFlags = entry->table->isStaticFlags;
    vector<ObjectPtr> &argsInfo = entry->argsInfo;
    for (unsigned i = 0; i < isStaticFlags.size(); ++i) {
        if (!isStaticFlags[i])
            argsInfo.push_back(args->type(i).ptr());
        else
            argsInfo.push_back(cloneValue(args->value(i)).ptr());
    }
}

InvokeTableEntryPtr findMatchingEntry(InvokeTablePtr table, ArgListPtr args)
{
    int h = hashArgs(args, table->isStaticFlags);
    h &= (table->data.size() - 1);
    vector<InvokeTableEntryPtr> &entries = table->data[h];
    for (unsigned i = 0; i < entries.size(); ++i) {
        if (matchingArgs(args, entries[i]))
            return entries[i];
    }
    InvokeTableEntryPtr entry = new InvokeTableEntry();
    entry->table = table;
    entries.push_back(entry);
    initArgsInfo(entry, args);
    return entry;
}

InvokeTablePtr newInvokeTable(const vector<FormalArgPtr> &formalArgs)
{
    InvokeTablePtr table = new InvokeTable();
    for (unsigned i = 0; i < formalArgs.size(); ++i) {
        bool isStatic = formalArgs[i]->objKind == STATIC_ARG;
        table->isStaticFlags.push_back(isStatic);
    }
    table->data.resize(64);
    return table;
}

//
// procedure invoke table
//

void initProcedureInvokeTable(ProcedurePtr x)
{
    x->invokeTable = newInvokeTable(x->code->formalArgs);
}

InvokeTableEntryPtr
lookupProcedureInvoke(ProcedurePtr x, ArgListPtr args)
{
    InvokeTablePtr table = x->invokeTable;
    if (!table) {
        initProcedureInvokeTable(x);
        table = x->invokeTable;
    }
    args->ensureArity(x->code->formalArgs.size());
    return findMatchingEntry(table, args);
}

void
initProcedureEnv(ProcedurePtr x,
                 ArgListPtr args,
                 InvokeTableEntryPtr entry)
{
    CodePtr code = x->code;
    vector<PatternCellPtr> cells;
    EnvPtr patternEnv = initPatternVars(x->env, code->patternVars, cells);
    args->ensureUnifyFormalArgs(code->formalArgs, patternEnv);
    EnvPtr env = bindPatternVars(x->env, code->patternVars, cells);
    if (code->predicate.ptr()) {
        bool result = evaluateToBool(code->predicate, env);
        if (!result)
            error(code->predicate, "procedure predicate failed");
    }
    entry->env = env;
    entry->code = code;
}

//
// overloadable invoke table
//

void initOverloadableInvokeTables(OverloadablePtr x)
{
    for (unsigned i = 0; i < x->overloads.size(); ++i) {
        const vector<FormalArgPtr> &formalArgs =
            x->overloads[i]->code->formalArgs;
        unsigned nArgs = formalArgs.size();
        if (x->invokeTables.size() <= nArgs)
            x->invokeTables.resize(nArgs + 1);
        if (!x->invokeTables[nArgs]) {
            x->invokeTables[nArgs] = newInvokeTable(formalArgs);
        }
        else {
            InvokeTablePtr table = x->invokeTables[nArgs];
            for (unsigned j = 0; j < formalArgs.size(); ++j) {
                if (table->isStaticFlags[j]) {
                    if (formalArgs[j]->objKind != STATIC_ARG)
                        error(formalArgs[j], "expecting static argument");
                }
                else {
                    if (formalArgs[j]->objKind == STATIC_ARG)
                        error(formalArgs[j], "expecting non static argument");
                }
            }
        }
    }
}

InvokeTableEntryPtr
lookupOverloadableInvoke(OverloadablePtr x, ArgListPtr args)
{
    if (x->invokeTables.empty())
        initOverloadableInvokeTables(x);
    if (x->invokeTables.size() <= args->size())
        error("no matching overload");
    InvokeTablePtr table = x->invokeTables[args->size()];
    if (!table)
        error("no matching overload");
    return findMatchingEntry(table, args);
}

void
initOverloadableEnv(OverloadablePtr x,
                    ArgListPtr args,
                    InvokeTableEntryPtr entry)
{
    for (unsigned i = 0; i < x->overloads.size(); ++i) {
        OverloadPtr y = x->overloads[i];
        CodePtr code = y->code;
        vector<PatternCellPtr> cells;
        EnvPtr patternEnv = initPatternVars(y->env, code->patternVars, cells);
        if (!args->unifyFormalArgs(code->formalArgs, patternEnv))
            continue;
        EnvPtr env = bindPatternVars(y->env, code->patternVars, cells);
        if (code->predicate.ptr()) {
            bool result = evaluateToBool(code->predicate, env);
            if (!result)
                continue;
        }
        entry->env = env;
        entry->code = code;
        return;
    }
    error("no matching overload");
}
<commit_msg>minor fix in overloadable invoke table initialization.<commit_after>#include "clay.hpp"
#include "invokeutil2.hpp"

//
// ArgList
//

ArgList::ArgList(const vector<ExprPtr> &exprs, EnvPtr env)
    : Object(DONT_CARE), exprs(exprs), env(env), allStatic(true),
      recursionError(false)
{
    for (unsigned i = 0; i < exprs.size(); ++i) {
        PValuePtr pv = partialEval(exprs[i], env);
        if (!pv)
            recursionError = true;
        else if (!pv->isStatic)
            allStatic = false;
        this->_pvalues.push_back(pv);
    }
    this->_values.resize(exprs.size());
}

TypePtr ArgList::type(int i)
{
    return this->_pvalues[i]->type;
}

bool ArgList::isTemp(int i )
{
    return this->_pvalues[i]->isTemp;
}

PValuePtr ArgList::pvalue(int i)
{
    return this->_pvalues[i];
}

ValuePtr ArgList::value(int i)
{
    if (!this->_values[i])
        this->_values[i] = evaluateNonVoid(this->exprs[i], this->env);
    return this->_values[i];
}

TypePtr ArgList::typeValue(int i)
{
    return valueToType(this->value(i));
}

void ArgList::ensureArity(int n)
{
    if ((int)this->exprs.size() != n)
        error("incorrect no. of arguments");
}

bool ArgList::unifyFormalArg(int i, FormalArgPtr farg, EnvPtr fenv)
{
    switch (farg->objKind) {
    case VALUE_ARG : {
        ValueArg *x = (ValueArg *)farg.ptr();
        if (!x->type)
            return true;
        PatternPtr pattern = evaluatePattern(x->type, fenv);
        return unifyType(pattern, type(i));
    }
    case STATIC_ARG : {
        StaticArg *x = (StaticArg *)farg.ptr();
        PatternPtr pattern = evaluatePattern(x->pattern, fenv);
        return unify(pattern, this->value(i));
    }
    default :
        assert(false);
        return false;
    }
}

bool
ArgList::unifyFormalArgs(const vector<FormalArgPtr> &fargs, EnvPtr fenv)
{
    if (this->size() != fargs.size())
        return false;
    for (unsigned i = 0; i < fargs.size(); ++i) {
        if (!this->unifyFormalArg(i, fargs[i], fenv))
            return false;
    }
    return true;
}

void
ArgList::ensureUnifyFormalArgs(const vector<FormalArgPtr> &fargs,
                               EnvPtr fenv)
{
    ensureArity(fargs.size());
    for (unsigned i = 0; i < fargs.size(); ++i) {
        if (!this->unifyFormalArg(i, fargs[i], fenv))
            error(this->exprs[i], "non-matching argment");
    }
}

ArgListPtr ArgList::removeStaticArgs(const vector<FormalArgPtr> &fargs)
{
    assert(fargs.size() == this->size());
    ensureArity(fargs.size());

    ArgListPtr args = new ArgList(vector<ExprPtr>(), this->env);
    args->allStatic = this->allStatic;
    args->recursionError = this->recursionError;

    for (unsigned i = 0; i < fargs.size(); ++i) {
        if (fargs[i]->objKind != STATIC_ARG) {
            args->exprs.push_back(this->exprs[i]);
            args->_pvalues.push_back(this->_pvalues[i]);
            args->_values.push_back(this->_values[i]);
        }
    }

    return args;
}

//
// invoke table procs
//

int hashArgs(ArgListPtr args, const vector<bool> &isStaticFlags)
{
    int h = 0;
    for (unsigned i = 0; i < isStaticFlags.size(); ++i) {
        if (!isStaticFlags[i])
            h += toCOIndex(args->type(i).ptr());
        else
            h += valueHash(args->value(i));
    }
    return h;
}

bool matchingArgs(ArgListPtr args, InvokeTableEntryPtr entry)
{
    const vector<bool> &isStaticFlags = entry->table->isStaticFlags;
    const vector<ObjectPtr> &argsInfo = entry->argsInfo;
    for (unsigned i = 0; i < isStaticFlags.size(); ++i) {
        if (!isStaticFlags[i]) {
            TypePtr t = (Type *)argsInfo[i].ptr();
            if (args->type(i) != t)
                return false;
        }
        else {
            ValuePtr v = args->value(i);
            if (!valueEquals(v, (Value *)argsInfo[i].ptr()))
                return false;
        }
    }
    return true;
}

void initArgsInfo(InvokeTableEntryPtr entry, ArgListPtr args)
{
    const vector<bool> &isStaticFlags = entry->table->isStaticFlags;
    vector<ObjectPtr> &argsInfo = entry->argsInfo;
    for (unsigned i = 0; i < isStaticFlags.size(); ++i) {
        if (!isStaticFlags[i])
            argsInfo.push_back(args->type(i).ptr());
        else
            argsInfo.push_back(cloneValue(args->value(i)).ptr());
    }
}

InvokeTableEntryPtr findMatchingEntry(InvokeTablePtr table, ArgListPtr args)
{
    int h = hashArgs(args, table->isStaticFlags);
    h &= (table->data.size() - 1);
    vector<InvokeTableEntryPtr> &entries = table->data[h];
    for (unsigned i = 0; i < entries.size(); ++i) {
        if (matchingArgs(args, entries[i]))
            return entries[i];
    }
    InvokeTableEntryPtr entry = new InvokeTableEntry();
    entry->table = table;
    entries.push_back(entry);
    initArgsInfo(entry, args);
    return entry;
}

InvokeTablePtr newInvokeTable(const vector<FormalArgPtr> &formalArgs)
{
    InvokeTablePtr table = new InvokeTable();
    for (unsigned i = 0; i < formalArgs.size(); ++i) {
        bool isStatic = formalArgs[i]->objKind == STATIC_ARG;
        table->isStaticFlags.push_back(isStatic);
    }
    table->data.resize(64);
    return table;
}

//
// procedure invoke table
//

void initProcedureInvokeTable(ProcedurePtr x)
{
    x->invokeTable = newInvokeTable(x->code->formalArgs);
}

InvokeTableEntryPtr
lookupProcedureInvoke(ProcedurePtr x, ArgListPtr args)
{
    InvokeTablePtr table = x->invokeTable;
    if (!table) {
        initProcedureInvokeTable(x);
        table = x->invokeTable;
    }
    args->ensureArity(x->code->formalArgs.size());
    return findMatchingEntry(table, args);
}

void
initProcedureEnv(ProcedurePtr x,
                 ArgListPtr args,
                 InvokeTableEntryPtr entry)
{
    CodePtr code = x->code;
    vector<PatternCellPtr> cells;
    EnvPtr patternEnv = initPatternVars(x->env, code->patternVars, cells);
    args->ensureUnifyFormalArgs(code->formalArgs, patternEnv);
    EnvPtr env = bindPatternVars(x->env, code->patternVars, cells);
    if (code->predicate.ptr()) {
        bool result = evaluateToBool(code->predicate, env);
        if (!result)
            error(code->predicate, "procedure predicate failed");
    }
    entry->env = env;
    entry->code = code;
}

//
// overloadable invoke table
//

void initOverloadableInvokeTables(OverloadablePtr x)
{
    for (unsigned i = x->overloads.size(); i > 0; --i) {
        const vector<FormalArgPtr> &formalArgs =
            x->overloads[i-1]->code->formalArgs;
        unsigned nArgs = formalArgs.size();
        if (x->invokeTables.size() <= nArgs)
            x->invokeTables.resize(nArgs + 1);
        if (!x->invokeTables[nArgs]) {
            x->invokeTables[nArgs] = newInvokeTable(formalArgs);
        }
        else {
            InvokeTablePtr table = x->invokeTables[nArgs];
            for (unsigned j = 0; j < formalArgs.size(); ++j) {
                if (table->isStaticFlags[j]) {
                    if (formalArgs[j]->objKind != STATIC_ARG)
                        error(formalArgs[j], "expecting static argument");
                }
                else {
                    if (formalArgs[j]->objKind == STATIC_ARG)
                        error(formalArgs[j], "expecting non static argument");
                }
            }
        }
    }
}

InvokeTableEntryPtr
lookupOverloadableInvoke(OverloadablePtr x, ArgListPtr args)
{
    if (x->invokeTables.empty())
        initOverloadableInvokeTables(x);
    if (x->invokeTables.size() <= args->size())
        error("no matching overload");
    InvokeTablePtr table = x->invokeTables[args->size()];
    if (!table)
        error("no matching overload");
    return findMatchingEntry(table, args);
}

void
initOverloadableEnv(OverloadablePtr x,
                    ArgListPtr args,
                    InvokeTableEntryPtr entry)
{
    for (unsigned i = 0; i < x->overloads.size(); ++i) {
        OverloadPtr y = x->overloads[i];
        CodePtr code = y->code;
        vector<PatternCellPtr> cells;
        EnvPtr patternEnv = initPatternVars(y->env, code->patternVars, cells);
        if (!args->unifyFormalArgs(code->formalArgs, patternEnv))
            continue;
        EnvPtr env = bindPatternVars(y->env, code->patternVars, cells);
        if (code->predicate.ptr()) {
            bool result = evaluateToBool(code->predicate, env);
            if (!result)
                continue;
        }
        entry->env = env;
        entry->code = code;
        return;
    }
    error("no matching overload");
}
<|endoftext|>


<commit_before>#include <Esp.h>
#ifdef ESP32
#include <ESPmDNS.h>
#include <SPIFFS.h>
#include <esp_log.h>
#include <rom/rtc.h>
#else
#include <ESP8266mDNS.h>
#include <user_interface.h>
#endif
#include <FS.h>

#include "iotsa.h"
#include "iotsaConfigFile.h"
#include "iotsaConfig.h"

//
// Global variables, because other modules need them too.
//
IotsaConfig iotsaConfig = {
  false,
  IOTSA_MODE_NORMAL,
  0,
  IOTSA_MODE_NORMAL,
  0,
  ""
};

String& hostName(iotsaConfig.hostName);

static void wifiDefaultHostName() {
  iotsaConfig.hostName = "iotsa";
#ifdef ESP32
  iotsaConfig.hostName += String(uint32_t(ESP.getEfuseMac()), HEX);
#else
  iotsaConfig.hostName += String(ESP.getChipId(), HEX);
#endif
}

static const char* getBootReason() {
  static const char *reason = NULL;
  if (reason == NULL) {
    reason = "unknown";
#ifndef ESP32
    rst_info *rip = ESP.getResetInfoPtr();
    static const char *reasons[] = {
      "power",
      "hardwareWatchdog",
      "exception",
      "softwareWatchdog",
      "softwareReboot",
      "deepSleepAwake",
      "externalReset"
    };
    if ((int)rip->reason < sizeof(reasons)/sizeof(reasons[0])) {
      reason = reasons[(int)rip->reason];
    }
#else
  RESET_REASON r = rtc_get_reset_reason(0);
  static const char *reasons[] = {
    "0",
    "power",
    "2",
    "softwareReboot",
    "legacyWatchdog",
    "deepSleepAwake",
    "sdio",
    "tg0Watchdog",
    "tg1Watchdog",
    "rtcWatchdog",
    "intrusion",
    "tgWatchdogCpu",
    "softwareRebootCpu",
    "rtcWatchdogCpu",
    "externalReset",
    "brownout",
    "rtcWatchdogRtc"
  };
  if ((int)r < sizeof(reasons)/sizeof(reasons[0])) {
    reason = reasons[(int)r];
  }
#endif
  }
  return reason;
}
static const char *modeName(config_mode mode) {
  if (mode == IOTSA_MODE_NORMAL)
    return "normal";
  if (mode == IOTSA_MODE_CONFIG)
    return "configuration";
  if (mode == IOTSA_MODE_OTA)
    return "OTA";
  if (mode == IOTSA_MODE_FACTORY_RESET)
    return "factory-reset";
  return "unknown";
}

void IotsaConfigMod::setup() {
  configLoad();
  if (app.status) app.status->showStatus();
  if (iotsaConfig.configurationMode) {
  	IFDEBUG IotsaSerial.println("tmpConfigMode, re-saving config.cfg without it");
  	configSave();
    iotsaConfig.configurationModeEndTime = millis() + 1000*iotsaConfig.configurationModeTimeout;
    IFDEBUG IotsaSerial.print("tempConfigMode=");
    IFDEBUG IotsaSerial.print((int)iotsaConfig.configurationMode);
    IFDEBUG IotsaSerial.print(", timeout at ");
    IFDEBUG IotsaSerial.println(iotsaConfig.configurationModeEndTime);
}
  // If a configuration mode was requested but the reset reason was not
  // external reset (the button) or powerup we do not honor the configuration mode
  // request: it could be triggered through a software bug or so, and we want to require
  // user interaction.
#ifndef ESP32
  rst_info *rip = ESP.getResetInfoPtr();
  int reason = (int)rip->reason;
  bool badReason = rip->reason != REASON_DEFAULT_RST && rip->reason != REASON_EXT_SYS_RST;
#else
  int reason = rtc_get_reset_reason(0);
  // xxxjack Not sure why I sometimes see the WDT reset on pressing the reset button...
  bool badReason = reason != POWERON_RESET && reason != RTCWDT_RTC_RESET;
#endif
  if (badReason) {
    iotsaConfig.configurationMode = IOTSA_MODE_NORMAL;
    IFDEBUG IotsaSerial.print("tmpConfigMode not honoured because of reset reason:");
    IFDEBUG IotsaSerial.println(reason);
  }
  // If factory reset is requested format the Flash and reboot
  if (iotsaConfig.configurationMode == IOTSA_MODE_FACTORY_RESET) {
  	IFDEBUG IotsaSerial.println("Factory-reset requested");
  	delay(1000);
#ifndef ESP32
  	IFDEBUG IotsaSerial.println("Formatting SPIFFS...");
  	SPIFFS.format();
  	IFDEBUG IotsaSerial.println("Format done, rebooting.");
#else
	SPIFFS.remove("/config/wifi.cfg");
	IFDEBUG IotsaSerial.println("Removed /config/wifi.cfg");
#endif
  	delay(2000);
  	ESP.restart();
  }
  if (app.status) app.status->showStatus();
}

void
IotsaConfigMod::handler() {
  bool anyChanged = false;
  bool hostnameChanged = false;
  if( server.hasArg("hostName")) {
    String argValue = server.arg("hostname");
    if (argValue != iotsaConfig.hostName) {
      iotsaConfig.hostName = argValue;
      anyChanged = true;
      hostnameChanged = true;
    }
  }
  if( server.hasArg("rebootTimeout")) {
    int newValue = server.arg("rebootTimeout").toInt();
    if (newValue != iotsaConfig.configurationModeTimeout) {
      iotsaConfig.configurationModeTimeout = newValue;
      anyChanged = true;
    }
  }
  if( server.hasArg("mode")) {
    String argValue = server.arg("mode");
    if (argValue != "0") {
      if (needsAuthentication("config")) return;
      iotsaConfig.nextConfigurationMode = config_mode(atoi(argValue.c_str()));
      iotsaConfig.nextConfigurationModeEndTime = millis() + iotsaConfig.configurationModeTimeout*1000;
      anyChanged = true;
    }
  }
  if( server.hasArg("factoryreset") && server.hasArg("iamsure")) {
    if (server.arg("factoryreset") == "1" && server.arg("iamsure") == "1") {
      iotsaConfig.nextConfigurationMode = IOTSA_MODE_FACTORY_RESET;
      iotsaConfig.nextConfigurationModeEndTime = millis() + iotsaConfig.configurationModeTimeout*1000;
      anyChanged = true;
    }
  }

  if (anyChanged) {
    	configSave();
	}

  String message = "<title>Iotsa configuration</title>
Iotsa configuration
";
  if (anyChanged) {
    message += "
Settings saved to EEPROM.
";
    if (hostnameChanged) {
      message += "
Rebooting device to change hostname.
";
    }
    if (iotsaConfig.nextConfigurationMode) {
      message += "
Special mode ";
      message += modeName(iotsaConfig.nextConfigurationMode);
      message += " requested. Power cycle within ";
      message += String((iotsaConfig.nextConfigurationModeEndTime - millis())/1000);
      message += " seconds to activate.
";
    }
  }
  if (!iotsaConfig.inConfigurationMode()) {
    message += "
Hostname: ";
    message += htmlEncode(iotsaConfig.hostName);
    message += " (goto configuration mode to change)
Configuration mode timeout: ";
    message += String(iotsaConfig.configurationModeTimeout);
    message += " (goto configuration mode to change)
";
  }
  message += "<form method='get'>";
  if (iotsaConfig.inConfigurationMode()) {
    message += "Hostname: <input name='hostName' value='";
    message += htmlEncode(iotsaConfig.hostName);
    message += "'>
Configuration mode timeout: <input name='rebootTimeout' value='";
    message += String(iotsaConfig.configurationModeTimeout);
    message += "'>
";
  }

  message += "<input name='mode' type='radio' value='0' checked> Enter normal mode after next reboot.
";
  message += "<input name='mode' type='radio' value='1'> Enter configuration mode after next reboot.
";
  if (app.otaEnabled()) {
    message += "<input name='mode' type='radio' value='2'> Enable over-the-air update after next reboot.";
    if (iotsaConfig.wifiPrivateNetworkMode) {
      message += "(Warning: Enabling OTA may not work because mDNS not available on this WiFi network.)";
    }
    message += "
";
  }
  message += "
<input name='factoryreset' type='checkbox' value='1'> Factory-reset and clear all files. <input name='iamsure' type='checkbox' value='1'> Yes, I am sure.
";
  message += "<input type='submit'></form>";
  message += "";
  server.send(200, "text/html", message);
  if (hostnameChanged) {
    IFDEBUG IotsaSerial.print("Restart in 2 seconds");
    delay(2000);
    ESP.restart();
  }
}

bool IotsaConfigMod::getHandler(const char *path, JsonObject& reply) {
  reply["hostName"] = iotsaConfig.hostName;
  reply["modeTimeout"] = iotsaConfig.configurationModeTimeout;
  if (iotsaConfig.configurationMode) {
    reply["currentMode"] = int(iotsaConfig.configurationMode);
    reply["currentModeTimeout"] = (iotsaConfig.configurationModeEndTime - millis())/1000;
  }
  if (iotsaConfig.wifiPrivateNetworkMode) {
    reply["privateWifi"] = true;
  }
  if (iotsaConfig.nextConfigurationMode) {
    reply["requestedMode"] = int(iotsaConfig.nextConfigurationMode);
    reply["requestedModeTimeout"] = (iotsaConfig.nextConfigurationModeEndTime - millis())/1000;
  }
  reply["iotsaVersion"] = IOTSA_VERSION;
  reply["iotsaFullVersion"] = IOTSA_FULL_VERSION;
  reply["program"] = app.title;
#ifdef IOTSA_CONFIG_PROGRAM_SOURCE
  reply["programSource"] = IOTSA_CONFIG_PROGRAM_SOURCE;
#endif
#ifdef IOTSA_CONFIG_PROGRAM_VERSION
  reply["programVersion"] = IOTSA_CONFIG_PROGRAM_VERSION;
#endif
  reply["bootCause"] = getBootReason();
  reply["uptime"] = millis() / 1000;
  JsonArray& modules = reply.createNestedArray("modules");
  for (IotsaBaseMod *m=app.firstEarlyModule; m; m=m->nextModule) {
    if (m->name != "")
      modules.add(m->name);
  }
  for (IotsaBaseMod *m=app.firstModule; m; m=m->nextModule) {
    if (m->name != "")
      modules.add(m->name);
  }
  return true;
}

bool IotsaConfigMod::putHandler(const char *path, const JsonVariant& request, JsonObject& reply) {
  bool anyChanged = false;
  bool hostnameChanged = true;
  JsonObject& reqObj = request.as<JsonObject>();
  if (reqObj.containsKey("hostName")) {
    iotsaConfig.hostName = reqObj.get<String>("hostName");
    anyChanged = true;
    hostnameChanged = true;
    reply["needsReboot"] = true;
  }
  if (reqObj.containsKey("modeTimeout")) {
    iotsaConfig.configurationModeTimeout = reqObj.get<int>("modeTimeout");
    anyChanged = true;
  }
  if (reqObj.containsKey("requestedMode")) {
    iotsaConfig.nextConfigurationMode = config_mode(reqObj.get<int>("requestedMode"));
    anyChanged = iotsaConfig.nextConfigurationMode != config_mode(0);
    if (anyChanged) {
      iotsaConfig.nextConfigurationModeEndTime = millis() + iotsaConfig.configurationModeTimeout*1000;
      reply["requestedMode"] = int(iotsaConfig.nextConfigurationMode);
      reply["requestedModeTimeout"] = (iotsaConfig.nextConfigurationModeEndTime - millis())/1000;
      reply["needsReboot"] = true;
    }
  }
  if (anyChanged) configSave();
  if (reqObj.get<bool>("reboot")) {
    delay(2000);
    ESP.restart();
  }
  return anyChanged;
}

void IotsaConfigMod::serverSetup() {
  server.on("/config", std::bind(&IotsaConfigMod::handler, this));
  api.setup("/api/config", true, true);
  name = "config";
}

String IotsaConfigMod::info() {
  String message;
  if (iotsaConfig.configurationMode) {
  	message += "
In configuration mode ";
    message += modeName(iotsaConfig.configurationMode);
    message += ", will timeout in " + String((iotsaConfig.configurationModeEndTime-millis())/1000) + " seconds.
";
  } else if (iotsaConfig.nextConfigurationMode) {
  	message += "
Configuration mode ";
    message += modeName(iotsaConfig.nextConfigurationMode);
    message += " requested, enable by rebooting within " + String((iotsaConfig.nextConfigurationModeEndTime-millis())/1000) + " seconds.
";
  } else if (iotsaConfig.configurationModeEndTime) {
  	message += "
Strange, no configuration mode but timeout is " + String(iotsaConfig.configurationModeEndTime-millis()) + "ms.
";
  }
  message += "
" + app.title + " is based on iotsa " + IOTSA_FULL_VERSION + ". See /config to change configuration.";
  message += "Last boot " + String((int)millis()/1000) + " seconds ago, reason ";
  message += getBootReason();
  message += ".
";
  return message;
}

void IotsaConfigMod::configLoad() {
  IotsaConfigFileLoad cf("/config/config.cfg");
  int tcm;
  cf.get("mode", tcm, -1);
  if (tcm == -1) {
    // Fallback: get from wifi.cfg
    IotsaConfigFileLoad cfcompat("/config/wifi.cfg");
    cfcompat.get("mode", tcm, IOTSA_MODE_NORMAL);
    iotsaConfig.configurationMode = (config_mode)tcm;
    cfcompat.get("hostName", iotsaConfig.hostName, "");
    cfcompat.get("rebootTimeout", iotsaConfig.configurationModeTimeout, CONFIGURATION_MODE_TIMEOUT);
    if (iotsaConfig.hostName == "") wifiDefaultHostName();
    return;
  }
  iotsaConfig.configurationMode = (config_mode)tcm;
  cf.get("hostName", iotsaConfig.hostName, "");
  cf.get("rebootTimeout", iotsaConfig.configurationModeTimeout, CONFIGURATION_MODE_TIMEOUT);
  if (iotsaConfig.hostName == "") wifiDefaultHostName();

}

void IotsaConfigMod::configSave() {
  IotsaConfigFileSave cf("/config/config.cfg");
  cf.put("mode", iotsaConfig.nextConfigurationMode); // Note: nextConfigurationMode, which will be read as configurationMode
  cf.put("hostName", iotsaConfig.hostName);
  cf.put("rebootTimeout", iotsaConfig.configurationModeTimeout);
  IFDEBUG IotsaSerial.println("Saved config.cfg");
}

void IotsaConfigMod::loop() {
  if (iotsaConfig.configurationModeEndTime && millis() > iotsaConfig.configurationModeEndTime) {
    IFDEBUG IotsaSerial.println("Configuration mode timeout. reboot.");
    iotsaConfig.configurationMode = IOTSA_MODE_NORMAL;
    iotsaConfig.configurationModeEndTime = 0;
    ESP.restart();
  }
  if (iotsaConfig.nextConfigurationModeEndTime && millis() > iotsaConfig.nextConfigurationModeEndTime) {
    IFDEBUG IotsaSerial.println("Next configuration mode timeout. Clearing.");
    iotsaConfig.nextConfigurationMode = IOTSA_MODE_NORMAL;
    iotsaConfig.nextConfigurationModeEndTime = 0;
    configSave();
  }
  // xxxjack
  if (!iotsaConfig.wifiPrivateNetworkMode) {
  	// Should be in normal mode, check that we have WiFi
  	static int disconnectedCount = 0;
  	if (WiFi.status() == WL_CONNECTED) {
  		if (disconnectedCount) {
  			IFDEBUG IotsaSerial.println("Config reconnected");
  		}
  		disconnectedCount = 0;
	} else {
		if (disconnectedCount == 0) {
			IFDEBUG IotsaSerial.println("Config connection lost");
		}
		disconnectedCount++;
		if (disconnectedCount > 60000) {
			IFDEBUG IotsaSerial.println("Config connection lost too long. Reboot.");
			ESP.restart();
		}
	}
  }
}
<commit_msg>Return board name in /api/config<commit_after>#include <Esp.h>
#ifdef ESP32
#include <ESPmDNS.h>
#include <SPIFFS.h>
#include <esp_log.h>
#include <rom/rtc.h>
#else
#include <ESP8266mDNS.h>
#include <user_interface.h>
#endif
#include <FS.h>

#include "iotsa.h"
#include "iotsaConfigFile.h"
#include "iotsaConfig.h"

//
// Global variables, because other modules need them too.
//
IotsaConfig iotsaConfig = {
  false,
  IOTSA_MODE_NORMAL,
  0,
  IOTSA_MODE_NORMAL,
  0,
  ""
};

String& hostName(iotsaConfig.hostName);

static void wifiDefaultHostName() {
  iotsaConfig.hostName = "iotsa";
#ifdef ESP32
  iotsaConfig.hostName += String(uint32_t(ESP.getEfuseMac()), HEX);
#else
  iotsaConfig.hostName += String(ESP.getChipId(), HEX);
#endif
}

static const char* getBootReason() {
  static const char *reason = NULL;
  if (reason == NULL) {
    reason = "unknown";
#ifndef ESP32
    rst_info *rip = ESP.getResetInfoPtr();
    static const char *reasons[] = {
      "power",
      "hardwareWatchdog",
      "exception",
      "softwareWatchdog",
      "softwareReboot",
      "deepSleepAwake",
      "externalReset"
    };
    if ((int)rip->reason < sizeof(reasons)/sizeof(reasons[0])) {
      reason = reasons[(int)rip->reason];
    }
#else
  RESET_REASON r = rtc_get_reset_reason(0);
  static const char *reasons[] = {
    "0",
    "power",
    "2",
    "softwareReboot",
    "legacyWatchdog",
    "deepSleepAwake",
    "sdio",
    "tg0Watchdog",
    "tg1Watchdog",
    "rtcWatchdog",
    "intrusion",
    "tgWatchdogCpu",
    "softwareRebootCpu",
    "rtcWatchdogCpu",
    "externalReset",
    "brownout",
    "rtcWatchdogRtc"
  };
  if ((int)r < sizeof(reasons)/sizeof(reasons[0])) {
    reason = reasons[(int)r];
  }
#endif
  }
  return reason;
}
static const char *modeName(config_mode mode) {
  if (mode == IOTSA_MODE_NORMAL)
    return "normal";
  if (mode == IOTSA_MODE_CONFIG)
    return "configuration";
  if (mode == IOTSA_MODE_OTA)
    return "OTA";
  if (mode == IOTSA_MODE_FACTORY_RESET)
    return "factory-reset";
  return "unknown";
}

void IotsaConfigMod::setup() {
  configLoad();
  if (app.status) app.status->showStatus();
  if (iotsaConfig.configurationMode) {
  	IFDEBUG IotsaSerial.println("tmpConfigMode, re-saving config.cfg without it");
  	configSave();
    iotsaConfig.configurationModeEndTime = millis() + 1000*iotsaConfig.configurationModeTimeout;
    IFDEBUG IotsaSerial.print("tempConfigMode=");
    IFDEBUG IotsaSerial.print((int)iotsaConfig.configurationMode);
    IFDEBUG IotsaSerial.print(", timeout at ");
    IFDEBUG IotsaSerial.println(iotsaConfig.configurationModeEndTime);
}
  // If a configuration mode was requested but the reset reason was not
  // external reset (the button) or powerup we do not honor the configuration mode
  // request: it could be triggered through a software bug or so, and we want to require
  // user interaction.
#ifndef ESP32
  rst_info *rip = ESP.getResetInfoPtr();
  int reason = (int)rip->reason;
  bool badReason = rip->reason != REASON_DEFAULT_RST && rip->reason != REASON_EXT_SYS_RST;
#else
  int reason = rtc_get_reset_reason(0);
  // xxxjack Not sure why I sometimes see the WDT reset on pressing the reset button...
  bool badReason = reason != POWERON_RESET && reason != RTCWDT_RTC_RESET;
#endif
  if (badReason) {
    iotsaConfig.configurationMode = IOTSA_MODE_NORMAL;
    IFDEBUG IotsaSerial.print("tmpConfigMode not honoured because of reset reason:");
    IFDEBUG IotsaSerial.println(reason);
  }
  // If factory reset is requested format the Flash and reboot
  if (iotsaConfig.configurationMode == IOTSA_MODE_FACTORY_RESET) {
  	IFDEBUG IotsaSerial.println("Factory-reset requested");
  	delay(1000);
#ifndef ESP32
  	IFDEBUG IotsaSerial.println("Formatting SPIFFS...");
  	SPIFFS.format();
  	IFDEBUG IotsaSerial.println("Format done, rebooting.");
#else
	SPIFFS.remove("/config/wifi.cfg");
	IFDEBUG IotsaSerial.println("Removed /config/wifi.cfg");
#endif
  	delay(2000);
  	ESP.restart();
  }
  if (app.status) app.status->showStatus();
}

void
IotsaConfigMod::handler() {
  bool anyChanged = false;
  bool hostnameChanged = false;
  if( server.hasArg("hostName")) {
    String argValue = server.arg("hostname");
    if (argValue != iotsaConfig.hostName) {
      iotsaConfig.hostName = argValue;
      anyChanged = true;
      hostnameChanged = true;
    }
  }
  if( server.hasArg("rebootTimeout")) {
    int newValue = server.arg("rebootTimeout").toInt();
    if (newValue != iotsaConfig.configurationModeTimeout) {
      iotsaConfig.configurationModeTimeout = newValue;
      anyChanged = true;
    }
  }
  if( server.hasArg("mode")) {
    String argValue = server.arg("mode");
    if (argValue != "0") {
      if (needsAuthentication("config")) return;
      iotsaConfig.nextConfigurationMode = config_mode(atoi(argValue.c_str()));
      iotsaConfig.nextConfigurationModeEndTime = millis() + iotsaConfig.configurationModeTimeout*1000;
      anyChanged = true;
    }
  }
  if( server.hasArg("factoryreset") && server.hasArg("iamsure")) {
    if (server.arg("factoryreset") == "1" && server.arg("iamsure") == "1") {
      iotsaConfig.nextConfigurationMode = IOTSA_MODE_FACTORY_RESET;
      iotsaConfig.nextConfigurationModeEndTime = millis() + iotsaConfig.configurationModeTimeout*1000;
      anyChanged = true;
    }
  }

  if (anyChanged) {
    	configSave();
	}

  String message = "<title>Iotsa configuration</title>
Iotsa configuration
";
  if (anyChanged) {
    message += "
Settings saved to EEPROM.
";
    if (hostnameChanged) {
      message += "
Rebooting device to change hostname.
";
    }
    if (iotsaConfig.nextConfigurationMode) {
      message += "
Special mode ";
      message += modeName(iotsaConfig.nextConfigurationMode);
      message += " requested. Power cycle within ";
      message += String((iotsaConfig.nextConfigurationModeEndTime - millis())/1000);
      message += " seconds to activate.
";
    }
  }
  if (!iotsaConfig.inConfigurationMode()) {
    message += "
Hostname: ";
    message += htmlEncode(iotsaConfig.hostName);
    message += " (goto configuration mode to change)
Configuration mode timeout: ";
    message += String(iotsaConfig.configurationModeTimeout);
    message += " (goto configuration mode to change)
";
  }
  message += "<form method='get'>";
  if (iotsaConfig.inConfigurationMode()) {
    message += "Hostname: <input name='hostName' value='";
    message += htmlEncode(iotsaConfig.hostName);
    message += "'>
Configuration mode timeout: <input name='rebootTimeout' value='";
    message += String(iotsaConfig.configurationModeTimeout);
    message += "'>
";
  }

  message += "<input name='mode' type='radio' value='0' checked> Enter normal mode after next reboot.
";
  message += "<input name='mode' type='radio' value='1'> Enter configuration mode after next reboot.
";
  if (app.otaEnabled()) {
    message += "<input name='mode' type='radio' value='2'> Enable over-the-air update after next reboot.";
    if (iotsaConfig.wifiPrivateNetworkMode) {
      message += "(Warning: Enabling OTA may not work because mDNS not available on this WiFi network.)";
    }
    message += "
";
  }
  message += "
<input name='factoryreset' type='checkbox' value='1'> Factory-reset and clear all files. <input name='iamsure' type='checkbox' value='1'> Yes, I am sure.
";
  message += "<input type='submit'></form>";
  message += "";
  server.send(200, "text/html", message);
  if (hostnameChanged) {
    IFDEBUG IotsaSerial.print("Restart in 2 seconds");
    delay(2000);
    ESP.restart();
  }
}

bool IotsaConfigMod::getHandler(const char *path, JsonObject& reply) {
  reply["hostName"] = iotsaConfig.hostName;
  reply["modeTimeout"] = iotsaConfig.configurationModeTimeout;
  if (iotsaConfig.configurationMode) {
    reply["currentMode"] = int(iotsaConfig.configurationMode);
    reply["currentModeTimeout"] = (iotsaConfig.configurationModeEndTime - millis())/1000;
  }
  if (iotsaConfig.wifiPrivateNetworkMode) {
    reply["privateWifi"] = true;
  }
  if (iotsaConfig.nextConfigurationMode) {
    reply["requestedMode"] = int(iotsaConfig.nextConfigurationMode);
    reply["requestedModeTimeout"] = (iotsaConfig.nextConfigurationModeEndTime - millis())/1000;
  }
  reply["iotsaVersion"] = IOTSA_VERSION;
  reply["iotsaFullVersion"] = IOTSA_FULL_VERSION;
  reply["program"] = app.title;
#ifdef IOTSA_CONFIG_PROGRAM_SOURCE
  reply["programSource"] = IOTSA_CONFIG_PROGRAM_SOURCE;
#endif
#ifdef IOTSA_CONFIG_PROGRAM_VERSION
  reply["programVersion"] = IOTSA_CONFIG_PROGRAM_VERSION;
#endif
#ifdef ARDUINO_VARIANT
  reply["board"] = ARDUINO_VARIANT;
#endif
  reply["bootCause"] = getBootReason();
  reply["uptime"] = millis() / 1000;
  JsonArray& modules = reply.createNestedArray("modules");
  for (IotsaBaseMod *m=app.firstEarlyModule; m; m=m->nextModule) {
    if (m->name != "")
      modules.add(m->name);
  }
  for (IotsaBaseMod *m=app.firstModule; m; m=m->nextModule) {
    if (m->name != "")
      modules.add(m->name);
  }
  return true;
}

bool IotsaConfigMod::putHandler(const char *path, const JsonVariant& request, JsonObject& reply) {
  bool anyChanged = false;
  bool hostnameChanged = true;
  JsonObject& reqObj = request.as<JsonObject>();
  if (reqObj.containsKey("hostName")) {
    iotsaConfig.hostName = reqObj.get<String>("hostName");
    anyChanged = true;
    hostnameChanged = true;
    reply["needsReboot"] = true;
  }
  if (reqObj.containsKey("modeTimeout")) {
    iotsaConfig.configurationModeTimeout = reqObj.get<int>("modeTimeout");
    anyChanged = true;
  }
  if (reqObj.containsKey("requestedMode")) {
    iotsaConfig.nextConfigurationMode = config_mode(reqObj.get<int>("requestedMode"));
    anyChanged = iotsaConfig.nextConfigurationMode != config_mode(0);
    if (anyChanged) {
      iotsaConfig.nextConfigurationModeEndTime = millis() + iotsaConfig.configurationModeTimeout*1000;
      reply["requestedMode"] = int(iotsaConfig.nextConfigurationMode);
      reply["requestedModeTimeout"] = (iotsaConfig.nextConfigurationModeEndTime - millis())/1000;
      reply["needsReboot"] = true;
    }
  }
  if (anyChanged) configSave();
  if (reqObj.get<bool>("reboot")) {
    delay(2000);
    ESP.restart();
  }
  return anyChanged;
}

void IotsaConfigMod::serverSetup() {
  server.on("/config", std::bind(&IotsaConfigMod::handler, this));
  api.setup("/api/config", true, true);
  name = "config";
}

String IotsaConfigMod::info() {
  String message;
  if (iotsaConfig.configurationMode) {
  	message += "
In configuration mode ";
    message += modeName(iotsaConfig.configurationMode);
    message += ", will timeout in " + String((iotsaConfig.configurationModeEndTime-millis())/1000) + " seconds.
";
  } else if (iotsaConfig.nextConfigurationMode) {
  	message += "
Configuration mode ";
    message += modeName(iotsaConfig.nextConfigurationMode);
    message += " requested, enable by rebooting within " + String((iotsaConfig.nextConfigurationModeEndTime-millis())/1000) + " seconds.
";
  } else if (iotsaConfig.configurationModeEndTime) {
  	message += "
Strange, no configuration mode but timeout is " + String(iotsaConfig.configurationModeEndTime-millis()) + "ms.
";
  }
  message += "
" + app.title + " is based on iotsa " + IOTSA_FULL_VERSION + ". See /config to change configuration.";
  message += "Last boot " + String((int)millis()/1000) + " seconds ago, reason ";
  message += getBootReason();
  message += ".
";
  return message;
}

void IotsaConfigMod::configLoad() {
  IotsaConfigFileLoad cf("/config/config.cfg");
  int tcm;
  cf.get("mode", tcm, -1);
  if (tcm == -1) {
    // Fallback: get from wifi.cfg
    IotsaConfigFileLoad cfcompat("/config/wifi.cfg");
    cfcompat.get("mode", tcm, IOTSA_MODE_NORMAL);
    iotsaConfig.configurationMode = (config_mode)tcm;
    cfcompat.get("hostName", iotsaConfig.hostName, "");
    cfcompat.get("rebootTimeout", iotsaConfig.configurationModeTimeout, CONFIGURATION_MODE_TIMEOUT);
    if (iotsaConfig.hostName == "") wifiDefaultHostName();
    return;
  }
  iotsaConfig.configurationMode = (config_mode)tcm;
  cf.get("hostName", iotsaConfig.hostName, "");
  cf.get("rebootTimeout", iotsaConfig.configurationModeTimeout, CONFIGURATION_MODE_TIMEOUT);
  if (iotsaConfig.hostName == "") wifiDefaultHostName();

}

void IotsaConfigMod::configSave() {
  IotsaConfigFileSave cf("/config/config.cfg");
  cf.put("mode", iotsaConfig.nextConfigurationMode); // Note: nextConfigurationMode, which will be read as configurationMode
  cf.put("hostName", iotsaConfig.hostName);
  cf.put("rebootTimeout", iotsaConfig.configurationModeTimeout);
  IFDEBUG IotsaSerial.println("Saved config.cfg");
}

void IotsaConfigMod::loop() {
  if (iotsaConfig.configurationModeEndTime && millis() > iotsaConfig.configurationModeEndTime) {
    IFDEBUG IotsaSerial.println("Configuration mode timeout. reboot.");
    iotsaConfig.configurationMode = IOTSA_MODE_NORMAL;
    iotsaConfig.configurationModeEndTime = 0;
    ESP.restart();
  }
  if (iotsaConfig.nextConfigurationModeEndTime && millis() > iotsaConfig.nextConfigurationModeEndTime) {
    IFDEBUG IotsaSerial.println("Next configuration mode timeout. Clearing.");
    iotsaConfig.nextConfigurationMode = IOTSA_MODE_NORMAL;
    iotsaConfig.nextConfigurationModeEndTime = 0;
    configSave();
  }
  // xxxjack
  if (!iotsaConfig.wifiPrivateNetworkMode) {
  	// Should be in normal mode, check that we have WiFi
  	static int disconnectedCount = 0;
  	if (WiFi.status() == WL_CONNECTED) {
  		if (disconnectedCount) {
  			IFDEBUG IotsaSerial.println("Config reconnected");
  		}
  		disconnectedCount = 0;
	} else {
		if (disconnectedCount == 0) {
			IFDEBUG IotsaSerial.println("Config connection lost");
		}
		disconnectedCount++;
		if (disconnectedCount > 60000) {
			IFDEBUG IotsaSerial.println("Config connection lost too long. Reboot.");
			ESP.restart();
		}
	}
  }
}
<|endoftext|>


<commit_before>#include "Session.hpp"

#include <iostream>
#include <memory>

using dl::tcp::Session;

std::atomic<unsigned long> Session::mCount(0ul);

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
std::shared_ptr<Session> Session::Create(
  asio::io_service& IoService,
  asio::io_service& CallbackService)
{
  return std::shared_ptr<Session>(new Session(IoService, CallbackService));
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Session::Session(asio::io_service& IoService, asio::io_service& CallbackService)
 : mSessionId(++mCount),
   mIoService(IoService),
   mCallbackService(CallbackService),
   mSocket(mIoService),
   mStrand(mIoService)
{
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Session::Start()
{
  mSocket.async_read_some(
    asio::buffer(mData, mMaxLength),
    [this] (const asio::error_code& Error, const size_t BytesTransfered)
    {
      OnRead(Error, BytesTransfered);
    });
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Session::Write(const std::string& Bytes)
{
  std::weak_ptr<Session> pWeak = shared_from_this();
  mIoService.post(mStrand.wrap(
    [this, Bytes, pWeak]
    {
      if (auto pThis = pWeak.lock())
      {
        mWriteQueue.push_back(Bytes);

        AsyncWrite(pThis);
      }
    }));
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Session::AsyncWrite(std::weak_ptr<Session> pWeak)
{
  auto pThis = pWeak.lock();

  if (pThis && !mWriteQueue.empty())
  {
    auto Message = mWriteQueue.front();

    mWriteQueue.pop_front();

    asio::async_write(
      mSocket,
      asio::buffer(Message),
      mStrand.wrap(
        [this, pThis, &Message] (const asio::error_code& Error, const size_t BytesTransfered)
        {
          if (!Error)
          {
            AsyncWrite(pThis);
          }
          else
          {
            CallSignalOnThreadPool(mSignalWriteError, Error, std::move(Message));
          }
        }));
  }
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Session::OnRead(const asio::error_code& Error, const size_t BytesTransfered)
{
  if (!Error)
  {
    std::string Bytes(mData, BytesTransfered);
    CallSignalOnThreadPool(mSignalOnRx, std::move(Bytes));

    mSocket.async_read_some(
      asio::buffer(mData, mMaxLength),
      [this] (const asio::error_code& Error, const size_t BytesTransfered)
      {
        OnRead(Error, BytesTransfered);
      });
  }
  else if (
    (Error == asio::error::eof) || (Error == asio::error::connection_reset))
  {
    CallSignalOnThreadPool(mSignalOnDisconnect);
  }
  else
  {
    CallSignalOnThreadPool(mSignalReadError, Error);
  }
}
<commit_msg>changed from shared from this to weak from this<commit_after>#include "Session.hpp"

#include <iostream>
#include <memory>

using dl::tcp::Session;

std::atomic<unsigned long> Session::mCount(0ul);

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
std::shared_ptr<Session> Session::Create(
  asio::io_service& IoService,
  asio::io_service& CallbackService)
{
  return std::shared_ptr<Session>(new Session(IoService, CallbackService));
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Session::Session(asio::io_service& IoService, asio::io_service& CallbackService)
 : mSessionId(++mCount),
   mIoService(IoService),
   mCallbackService(CallbackService),
   mSocket(mIoService),
   mStrand(mIoService)
{
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Session::Start()
{
  mSocket.async_read_some(
    asio::buffer(mData, mMaxLength),
    [this] (const asio::error_code& Error, const size_t BytesTransfered)
    {
      OnRead(Error, BytesTransfered);
    });
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Session::Write(const std::string& Bytes)
{
  std::weak_ptr<Session> pWeak = weak_from_this();
  mIoService.post(mStrand.wrap(
    [this, Bytes, pWeak]
    {
      if (auto pThis = pWeak.lock())
      {
        mWriteQueue.push_back(Bytes);

        AsyncWrite(pThis);
      }
    }));
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Session::AsyncWrite(std::weak_ptr<Session> pWeak)
{
  auto pThis = pWeak.lock();

  if (pThis && !mWriteQueue.empty())
  {
    auto Message = mWriteQueue.front();

    mWriteQueue.pop_front();

    asio::async_write(
      mSocket,
      asio::buffer(Message),
      mStrand.wrap(
        [this, pThis, &Message] (const asio::error_code& Error, const size_t BytesTransfered)
        {
          if (!Error)
          {
            AsyncWrite(pThis);
          }
          else
          {
            CallSignalOnThreadPool(mSignalWriteError, Error, std::move(Message));
          }
        }));
  }
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Session::OnRead(const asio::error_code& Error, const size_t BytesTransfered)
{
  if (!Error)
  {
    std::string Bytes(mData, BytesTransfered);
    CallSignalOnThreadPool(mSignalOnRx, std::move(Bytes));

    mSocket.async_read_some(
      asio::buffer(mData, mMaxLength),
      [this] (const asio::error_code& Error, const size_t BytesTransfered)
      {
        OnRead(Error, BytesTransfered);
      });
  }
  else if (
    (Error == asio::error::eof) || (Error == asio::error::connection_reset))
  {
    CallSignalOnThreadPool(mSignalOnDisconnect);
  }
  else
  {
    CallSignalOnThreadPool(mSignalReadError, Error);
  }
}
<|endoftext|>